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

Commit f525c05b authored by Johan Hovold's avatar Johan Hovold Committed by Greg Kroah-Hartman
Browse files

USB: sierra: fix port-data memory leak



Fix port-data memory leak by moving port data allocation and
deallocation to port_probe and port_remove.

Since commit 0998d063 (device-core: Ensure drvdata = NULL when no
driver is bound) the port private data is no longer freed at release as
it is no longer accessible.

Note also that urb-count for multi-port interfaces has not been changed
even though the usb-serial port number is now determined from the port
and interface minor numbers.

Compile-only tested.

Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarJohan Hovold <jhovold@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 084817d7
Loading
Loading
Loading
Loading
+58 −67
Original line number Diff line number Diff line
@@ -878,12 +878,7 @@ static void sierra_dtr_rts(struct usb_serial_port *port, int on)

static int sierra_startup(struct usb_serial *serial)
{
	struct usb_serial_port *port;
	struct sierra_intf_private *intfdata;
	struct sierra_port_private *portdata;
	struct sierra_iface_info *himemoryp = NULL;
	int i;
	u8 ifnum;

	intfdata = kzalloc(sizeof(*intfdata), GFP_KERNEL);
	if (!intfdata)
@@ -900,20 +895,32 @@ static int sierra_startup(struct usb_serial *serial)
	if (nmea)
		sierra_vsc_set_nmea(serial->dev, 1);

	/* Now setup per port private data */
	for (i = 0; i < serial->num_ports; i++) {
		port = serial->port[i];
		portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
		if (!portdata) {
			dev_dbg(&port->dev, "%s: kmalloc for "
				"sierra_port_private (%d) failed!\n",
				__func__, i);
			goto err;
	return 0;
}

static void sierra_release(struct usb_serial *serial)
{
	struct sierra_intf_private *intfdata;

	intfdata = usb_get_serial_data(serial);
	kfree(intfdata);
}

static int sierra_port_probe(struct usb_serial_port *port)
{
	struct usb_serial *serial = port->serial;
	struct sierra_port_private *portdata;
	const struct sierra_iface_info *himemoryp;
	u8 ifnum;

	portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
	if (!portdata)
		return -ENOMEM;

	spin_lock_init(&portdata->lock);
	init_usb_anchor(&portdata->active);
	init_usb_anchor(&portdata->delayed);
		ifnum = i;

	/* Assume low memory requirements */
	portdata->num_out_urbs = N_OUT_URB;
	portdata->num_in_urbs  = N_IN_URB;
@@ -922,55 +929,37 @@ static int sierra_startup(struct usb_serial *serial)
	if (serial->num_ports == 1) {
		/* Get interface number for composite device */
		ifnum = sierra_calc_interface(serial);
			himemoryp =
			    (struct sierra_iface_info *)&typeB_interface_list;
			if (is_himemory(ifnum, himemoryp)) {
				portdata->num_out_urbs = N_OUT_URB_HM;
				portdata->num_in_urbs  = N_IN_URB_HM;
			}
		himemoryp = &typeB_interface_list;
	} else {
		/* This is really the usb-serial port number of the interface
		 * rather than the interface number.
		 */
		ifnum = port->number - serial->minor;
		himemoryp = &typeA_interface_list;
	}
		else {
			himemoryp =
			    (struct sierra_iface_info *)&typeA_interface_list;
			if (is_himemory(i, himemoryp)) {

	if (is_himemory(ifnum, himemoryp)) {
		portdata->num_out_urbs = N_OUT_URB_HM;
		portdata->num_in_urbs  = N_IN_URB_HM;
	}
		}
		dev_dbg(&serial->dev->dev,

	dev_dbg(&port->dev,
			"Memory usage (urbs) interface #%d, in=%d, out=%d\n",
			ifnum, portdata->num_in_urbs, portdata->num_out_urbs);
		/* Set the port private data pointer */

	usb_set_serial_port_data(port, portdata);
	}

	return 0;
err:
	for (--i; i >= 0; --i) {
		portdata = usb_get_serial_port_data(serial->port[i]);
		kfree(portdata);
	}
	kfree(intfdata);

	return -ENOMEM;
}

static void sierra_release(struct usb_serial *serial)
static int sierra_port_remove(struct usb_serial_port *port)
{
	int i;
	struct usb_serial_port *port;
	struct sierra_port_private *portdata;

	for (i = 0; i < serial->num_ports; ++i) {
		port = serial->port[i];
		if (!port)
			continue;
	portdata = usb_get_serial_port_data(port);
		if (!portdata)
			continue;
	kfree(portdata);
	}
	kfree(serial->private);

	return 0;
}

#ifdef CONFIG_PM
@@ -1074,6 +1063,8 @@ static struct usb_serial_driver sierra_device = {
	.tiocmset          = sierra_tiocmset,
	.attach            = sierra_startup,
	.release           = sierra_release,
	.port_probe        = sierra_port_probe,
	.port_remove       = sierra_port_remove,
	.suspend	   = sierra_suspend,
	.resume		   = sierra_resume,
	.read_int_callback = sierra_instat_callback,