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

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
...@@ -878,12 +878,7 @@ static void sierra_dtr_rts(struct usb_serial_port *port, int on) ...@@ -878,12 +878,7 @@ static void sierra_dtr_rts(struct usb_serial_port *port, int on)
static int sierra_startup(struct usb_serial *serial) static int sierra_startup(struct usb_serial *serial)
{ {
struct usb_serial_port *port;
struct sierra_intf_private *intfdata; 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); intfdata = kzalloc(sizeof(*intfdata), GFP_KERNEL);
if (!intfdata) if (!intfdata)
...@@ -900,20 +895,32 @@ static int sierra_startup(struct usb_serial *serial) ...@@ -900,20 +895,32 @@ static int sierra_startup(struct usb_serial *serial)
if (nmea) if (nmea)
sierra_vsc_set_nmea(serial->dev, 1); sierra_vsc_set_nmea(serial->dev, 1);
/* Now setup per port private data */ return 0;
for (i = 0; i < serial->num_ports; i++) { }
port = serial->port[i];
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); portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
if (!portdata) { if (!portdata)
dev_dbg(&port->dev, "%s: kmalloc for " return -ENOMEM;
"sierra_port_private (%d) failed!\n",
__func__, i);
goto err;
}
spin_lock_init(&portdata->lock); spin_lock_init(&portdata->lock);
init_usb_anchor(&portdata->active); init_usb_anchor(&portdata->active);
init_usb_anchor(&portdata->delayed); init_usb_anchor(&portdata->delayed);
ifnum = i;
/* Assume low memory requirements */ /* Assume low memory requirements */
portdata->num_out_urbs = N_OUT_URB; portdata->num_out_urbs = N_OUT_URB;
portdata->num_in_urbs = N_IN_URB; portdata->num_in_urbs = N_IN_URB;
...@@ -922,55 +929,37 @@ static int sierra_startup(struct usb_serial *serial) ...@@ -922,55 +929,37 @@ static int sierra_startup(struct usb_serial *serial)
if (serial->num_ports == 1) { if (serial->num_ports == 1) {
/* Get interface number for composite device */ /* Get interface number for composite device */
ifnum = sierra_calc_interface(serial); ifnum = sierra_calc_interface(serial);
himemoryp = himemoryp = &typeB_interface_list;
(struct sierra_iface_info *)&typeB_interface_list; } else {
if (is_himemory(ifnum, himemoryp)) { /* This is really the usb-serial port number of the interface
portdata->num_out_urbs = N_OUT_URB_HM; * rather than the interface number.
portdata->num_in_urbs = N_IN_URB_HM; */
} ifnum = port->number - serial->minor;
himemoryp = &typeA_interface_list;
} }
else {
himemoryp = if (is_himemory(ifnum, himemoryp)) {
(struct sierra_iface_info *)&typeA_interface_list;
if (is_himemory(i, himemoryp)) {
portdata->num_out_urbs = N_OUT_URB_HM; portdata->num_out_urbs = N_OUT_URB_HM;
portdata->num_in_urbs = N_IN_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", "Memory usage (urbs) interface #%d, in=%d, out=%d\n",
ifnum,portdata->num_in_urbs, portdata->num_out_urbs ); ifnum, portdata->num_in_urbs, portdata->num_out_urbs);
/* Set the port private data pointer */
usb_set_serial_port_data(port, portdata); usb_set_serial_port_data(port, portdata);
}
return 0; 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; 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); portdata = usb_get_serial_port_data(port);
if (!portdata)
continue;
kfree(portdata); kfree(portdata);
}
kfree(serial->private); return 0;
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
...@@ -1074,6 +1063,8 @@ static struct usb_serial_driver sierra_device = { ...@@ -1074,6 +1063,8 @@ static struct usb_serial_driver sierra_device = {
.tiocmset = sierra_tiocmset, .tiocmset = sierra_tiocmset,
.attach = sierra_startup, .attach = sierra_startup,
.release = sierra_release, .release = sierra_release,
.port_probe = sierra_port_probe,
.port_remove = sierra_port_remove,
.suspend = sierra_suspend, .suspend = sierra_suspend,
.resume = sierra_resume, .resume = sierra_resume,
.read_int_callback = sierra_instat_callback, .read_int_callback = sierra_instat_callback,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment