Commit 2caabb3d authored by David S. Miller's avatar David S. Miller

Merge branch 'hso-next'

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