Merge branch 'hso-next'
authorDavid S. Miller <davem@davemloft.net>
Sun, 1 Feb 2015 20:33:31 +0000 (12:33 -0800)
committerDavid S. Miller <davem@davemloft.net>
Sun, 1 Feb 2015 20:33:31 +0000 (12:33 -0800)
Olivier Sobrie says:

====================
hso: fix some problems in the disconnect path

These patches attempt to fix some problems I observed when the hso
device is disconnected.
Several patches of this serie are fixing crashes or memleaks when a
hso device is disconnected.
This serie of patches is based on v3.18.

changes in v2:
 - Last patch of the serie dropped since another patch fix the issue.
   See http://marc.info/?l=linux-usb&m=142186699418489 for more info.

 - Added an extra patch avoiding name conflicts for the rfkill interface.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/usb/hso.c

index 9c5aa922a9f4bdb404588b74962769510be13887..7833bd1d9791408ccc4960874001739014f11109 100644 (file)
@@ -58,7 +58,6 @@
 #include <linux/module.h>
 #include <linux/ethtool.h>
 #include <linux/usb.h>
-#include <linux/timer.h>
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
@@ -154,6 +153,7 @@ struct hso_net {
        struct hso_device *parent;
        struct net_device *net;
        struct rfkill *rfkill;
+       char name[24];
 
        struct usb_endpoint_descriptor *in_endp;
        struct usb_endpoint_descriptor *out_endp;
@@ -274,7 +274,6 @@ struct hso_device {
        u8 usb_gone;
        struct work_struct async_get_intf;
        struct work_struct async_put_intf;
-       struct work_struct reset_device;
 
        struct usb_device *usb;
        struct usb_interface *interface;
@@ -340,7 +339,6 @@ static void async_put_intf(struct work_struct *data);
 static int hso_put_activity(struct hso_device *hso_dev);
 static int hso_get_activity(struct hso_device *hso_dev);
 static void tiocmget_intr_callback(struct urb *urb);
-static void reset_device(struct work_struct *data);
 /*****************************************************************************/
 /* Helping functions                                                         */
 /*****************************************************************************/
@@ -696,7 +694,7 @@ static void handle_usb_error(int status, const char *function,
        case -ETIMEDOUT:
                explanation = "protocol error";
                if (hso_dev)
-                       schedule_work(&hso_dev->reset_device);
+                       usb_queue_reset_device(hso_dev->interface);
                break;
        default:
                explanation = "unknown status";
@@ -1271,7 +1269,6 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp)
                goto err_out;
 
        D1("Opening %d", serial->minor);
-       kref_get(&serial->parent->ref);
 
        /* setup */
        tty->driver_data = serial;
@@ -1290,7 +1287,8 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp)
                if (result) {
                        hso_stop_serial_device(serial->parent);
                        serial->port.count--;
-                       kref_put(&serial->parent->ref, hso_serial_ref_free);
+               } else {
+                       kref_get(&serial->parent->ref);
                }
        } else {
                D1("Port was already open");
@@ -1340,8 +1338,6 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp)
                usb_autopm_put_interface(serial->parent->interface);
 
        mutex_unlock(&serial->parent->mutex);
-
-       kref_put(&serial->parent->ref, hso_serial_ref_free);
 }
 
 /* close the requested serial port */
@@ -1392,6 +1388,16 @@ static int hso_serial_write_room(struct tty_struct *tty)
        return room;
 }
 
+static void hso_serial_cleanup(struct tty_struct *tty)
+{
+       struct hso_serial *serial = tty->driver_data;
+
+       if (!serial)
+               return;
+
+       kref_put(&serial->parent->ref, hso_serial_ref_free);
+}
+
 /* setup the term */
 static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old)
 {
@@ -2198,8 +2204,8 @@ static int hso_stop_serial_device(struct hso_device *hso_dev)
 
        for (i = 0; i < serial->num_rx_urbs; i++) {
                if (serial->rx_urb[i]) {
-                               usb_kill_urb(serial->rx_urb[i]);
-                               serial->rx_urb_filled[i] = 0;
+                       usb_kill_urb(serial->rx_urb[i]);
+                       serial->rx_urb_filled[i] = 0;
                }
        }
        serial->curr_rx_urb_idx = 0;
@@ -2228,14 +2234,17 @@ static int hso_stop_serial_device(struct hso_device *hso_dev)
        return 0;
 }
 
-static void hso_serial_common_free(struct hso_serial *serial)
+static void hso_serial_tty_unregister(struct hso_serial *serial)
 {
-       int i;
-
        if (serial->parent->dev)
                device_remove_file(serial->parent->dev, &dev_attr_hsotype);
 
        tty_unregister_device(tty_drv, serial->minor);
+}
+
+static void hso_serial_common_free(struct hso_serial *serial)
+{
+       int i;
 
        for (i = 0; i < serial->num_rx_urbs; i++) {
                /* unlink and free RX URB */
@@ -2246,6 +2255,7 @@ static void hso_serial_common_free(struct hso_serial *serial)
 
        /* unlink and free TX URB */
        usb_free_urb(serial->tx_urb);
+       kfree(serial->tx_buffer);
        kfree(serial->tx_data);
        tty_port_destroy(&serial->port);
 }
@@ -2316,6 +2326,7 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs,
 
        return 0;
 exit:
+       hso_serial_tty_unregister(serial);
        hso_serial_common_free(serial);
        return -1;
 }
@@ -2338,7 +2349,6 @@ static struct hso_device *hso_create_device(struct usb_interface *intf,
 
        INIT_WORK(&hso_dev->async_get_intf, async_get_intf);
        INIT_WORK(&hso_dev->async_put_intf, async_put_intf);
-       INIT_WORK(&hso_dev->reset_device, reset_device);
 
        return hso_dev;
 }
@@ -2459,27 +2469,21 @@ static void hso_create_rfkill(struct hso_device *hso_dev,
 {
        struct hso_net *hso_net = dev2net(hso_dev);
        struct device *dev = &hso_net->net->dev;
-       char *rfkn;
+       static u32 rfkill_counter;
 
-       rfkn = kzalloc(20, GFP_KERNEL);
-       if (!rfkn)
-               dev_err(dev, "%s - Out of memory\n", __func__);
-
-       snprintf(rfkn, 20, "hso-%d",
-                interface->altsetting->desc.bInterfaceNumber);
+       snprintf(hso_net->name, sizeof(hso_net->name), "hso-%d",
+                rfkill_counter++);
 
-       hso_net->rfkill = rfkill_alloc(rfkn,
+       hso_net->rfkill = rfkill_alloc(hso_net->name,
                                       &interface_to_usbdev(interface)->dev,
                                       RFKILL_TYPE_WWAN,
                                       &hso_rfkill_ops, hso_dev);
        if (!hso_net->rfkill) {
                dev_err(dev, "%s - Out of memory\n", __func__);
-               kfree(rfkn);
                return;
        }
        if (rfkill_register(hso_net->rfkill) < 0) {
                rfkill_destroy(hso_net->rfkill);
-               kfree(rfkn);
                hso_net->rfkill = NULL;
                dev_err(dev, "%s - Failed to register rfkill\n", __func__);
                return;
@@ -2594,7 +2598,6 @@ static void hso_free_serial_device(struct hso_device *hso_dev)
 
        if (!serial)
                return;
-       set_serial_by_index(serial->minor, NULL);
 
        hso_serial_common_free(serial);
 
@@ -2684,6 +2687,7 @@ static struct hso_device *hso_create_bulk_serial_device(
        return hso_dev;
 
 exit2:
+       hso_serial_tty_unregister(serial);
        hso_serial_common_free(serial);
 exit:
        hso_free_tiomget(serial);
@@ -3083,26 +3087,6 @@ out:
        return result;
 }
 
-static void reset_device(struct work_struct *data)
-{
-       struct hso_device *hso_dev =
-           container_of(data, struct hso_device, reset_device);
-       struct usb_device *usb = hso_dev->usb;
-       int result;
-
-       if (hso_dev->usb_gone) {
-               D1("No reset during disconnect\n");
-       } else {
-               result = usb_lock_device_for_reset(usb, hso_dev->interface);
-               if (result < 0)
-                       D1("unable to lock device for reset: %d\n", result);
-               else {
-                       usb_reset_device(usb);
-                       usb_unlock_device(usb);
-               }
-       }
-}
-
 static void hso_serial_ref_free(struct kref *ref)
 {
        struct hso_device *hso_dev = container_of(ref, struct hso_device, ref);
@@ -3112,18 +3096,22 @@ static void hso_serial_ref_free(struct kref *ref)
 
 static void hso_free_interface(struct usb_interface *interface)
 {
-       struct hso_serial *hso_dev;
+       struct hso_serial *serial;
        int i;
 
        for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) {
                if (serial_table[i] &&
                    (serial_table[i]->interface == interface)) {
-                       hso_dev = dev2ser(serial_table[i]);
-                       tty_port_tty_hangup(&hso_dev->port, false);
-                       mutex_lock(&hso_dev->parent->mutex);
-                       hso_dev->parent->usb_gone = 1;
-                       mutex_unlock(&hso_dev->parent->mutex);
+                       serial = dev2ser(serial_table[i]);
+                       tty_port_tty_hangup(&serial->port, false);
+                       mutex_lock(&serial->parent->mutex);
+                       serial->parent->usb_gone = 1;
+                       mutex_unlock(&serial->parent->mutex);
+                       cancel_work_sync(&serial_table[i]->async_put_intf);
+                       cancel_work_sync(&serial_table[i]->async_get_intf);
+                       hso_serial_tty_unregister(serial);
                        kref_put(&serial_table[i]->ref, hso_serial_ref_free);
+                       set_serial_by_index(i, NULL);
                }
        }
 
@@ -3215,6 +3203,7 @@ static const struct tty_operations hso_serial_ops = {
        .close = hso_serial_close,
        .write = hso_serial_write,
        .write_room = hso_serial_write_room,
+       .cleanup = hso_serial_cleanup,
        .ioctl = hso_serial_ioctl,
        .set_termios = hso_serial_set_termios,
        .chars_in_buffer = hso_serial_chars_in_buffer,