diff options
author | Johan Hovold <jhovold@gmail.com> | 2012-10-25 12:29:19 +0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-10-25 20:37:13 +0400 |
commit | f525c05babc7938cc1d4236550fd8a659fb05960 (patch) | |
tree | bf5923b4ddf04ddd25ee199e4728c9d9c4de1c52 /drivers/usb | |
parent | 084817d79399ab5ccab2f90a148b0369912a8369 (diff) | |
download | linux-f525c05babc7938cc1d4236550fd8a659fb05960.tar.xz |
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 0998d0631001288 (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: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/serial/sierra.c | 125 |
1 files changed, 58 insertions, 67 deletions
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index bb2ecaf3296b..270860f6bb2a 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -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,77 +895,71 @@ 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; - } - 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; - - /* Determine actual memory requirements */ - 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; - } - } - else { - himemoryp = - (struct sierra_iface_info *)&typeA_interface_list; - if (is_himemory(i, himemoryp)) { - portdata->num_out_urbs = N_OUT_URB_HM; - portdata->num_in_urbs = N_IN_URB_HM; - } - } - dev_dbg(&serial->dev->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; +} + +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); + + /* Assume low memory requirements */ + portdata->num_out_urbs = N_OUT_URB; + portdata->num_in_urbs = N_IN_URB; + + /* Determine actual memory requirements */ + if (serial->num_ports == 1) { + /* Get interface number for composite device */ + ifnum = sierra_calc_interface(serial); + 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; } - return 0; -err: - for (--i; i >= 0; --i) { - portdata = usb_get_serial_port_data(serial->port[i]); - kfree(portdata); + if (is_himemory(ifnum, himemoryp)) { + portdata->num_out_urbs = N_OUT_URB_HM; + portdata->num_in_urbs = N_IN_URB_HM; } - kfree(intfdata); - return -ENOMEM; + dev_dbg(&port->dev, + "Memory usage (urbs) interface #%d, in=%d, out=%d\n", + ifnum, portdata->num_in_urbs, portdata->num_out_urbs); + + usb_set_serial_port_data(port, portdata); + + return 0; } -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); + portdata = usb_get_serial_port_data(port); + kfree(portdata); + + 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, |