Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4d150bc9 authored by Azhar Shaikh's avatar Azhar Shaikh
Browse files

usb: gadget: f_gsi: Fix memory leak in f_gsi driver



Unconfiguring of gsi eps is done in gsi_disable(). gsi_disable() is
called in interrupt context. The freeing of trbs in case of cable
disconnect or composition switch is done from
ipa_disconnect_work_handler() which is a workqueue. So the calling
of this workqueue might get delayed most of times compared to
gsi_disable() and it will be executed earlier.
Since the endpoints will be unconfigured earlier, hence when
usb_gsi_ep_op() will be called with GSI_EP_OP_FREE_TRBS to free
the trbs, it will not be executed and will lead to a memory leak.
So move the unconfiguring of the eps after freeing of trbs.

Change-Id: I713e100424d30f234324cb823c2310d0fe4a4651
Signed-off-by: default avatarAzhar Shaikh <azhars@codeaurora.org>
parent 19240e8d
Loading
Loading
Loading
Loading
+19 −8
Original line number Original line Diff line number Diff line
@@ -618,6 +618,25 @@ static void ipa_disconnect_work_handler(struct gsi_data_port *d_port)


	if (gsi->d_port.out_ep)
	if (gsi->d_port.out_ep)
		usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_FREE_TRBS);
		usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_FREE_TRBS);

	/*
	 * Unconfig the gsi eps after freeing the trbs. If done in
	 * gsi_disable() then since gsi_disable() is called in interrupt context
	 * and the usb_gsi_ep_op() for GSI_EP_OP_FREE_TRBS which is called from
	 * ipa_disconnect_work_handler() a worker thread, can get delayed. So
	 * when gsi_disable() unconfigures the eps, usb_gsi_ep_op() will not be
	 * executed which leads to a memory leak.
	 * Also if this is done in gsi_unbind() then again this is executed in
	 * interrupt context and ipa_disconnect_work_handler() is a worker
	 * thread which can get delayed.
	 */
	if (gadget_is_dwc3(d_port->gadget)) {
		if (gsi->d_port.in_ep)
			msm_ep_unconfig(gsi->d_port.in_ep);
		if (gsi->d_port.out_ep)
			msm_ep_unconfig(gsi->d_port.out_ep);
	}

}
}


static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
@@ -2014,7 +2033,6 @@ fail:
static void gsi_disable(struct usb_function *f)
static void gsi_disable(struct usb_function *f)
{
{
	struct f_gsi *gsi = func_to_gsi(f);
	struct f_gsi *gsi = func_to_gsi(f);
	struct usb_composite_dev *cdev = f->config->cdev;


	atomic_set(&gsi->connected, 0);
	atomic_set(&gsi->connected, 0);


@@ -2041,13 +2059,6 @@ static void gsi_disable(struct usb_function *f)
		return;
		return;
	}
	}


	if (gadget_is_dwc3(cdev->gadget)) {
		if (gsi->d_port.in_ep)
			msm_ep_unconfig(gsi->d_port.in_ep);
		if (gsi->d_port.out_ep)
			msm_ep_unconfig(gsi->d_port.out_ep);
	}

	gsi->data_interface_up = false;
	gsi->data_interface_up = false;


	pr_debug("%s deactivated\n", gsi->function.name);
	pr_debug("%s deactivated\n", gsi->function.name);