2009-11-09 21:15:20 +08:00
|
|
|
/*
|
2009-11-09 21:15:23 +08:00
|
|
|
* mass_storage.c -- Mass Storage USB Gadget
|
2009-11-09 21:15:20 +08:00
|
|
|
*
|
|
|
|
* Copyright (C) 2003-2008 Alan Stern
|
|
|
|
* Copyright (C) 2009 Samsung Electronics
|
2012-01-13 22:05:16 +08:00
|
|
|
* Author: Michal Nazarewicz <mina86@mina86.com>
|
2009-11-09 21:15:23 +08:00
|
|
|
* All rights reserved.
|
2009-11-09 21:15:20 +08:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The Mass Storage Gadget acts as a USB Mass Storage device,
|
|
|
|
* appearing to the host as a disk drive or as a CD-ROM drive. In
|
|
|
|
* addition to providing an example of a genuinely useful gadget
|
|
|
|
* driver for a USB device, it also illustrates a technique of
|
|
|
|
* double-buffering for increased throughput. Last but not least, it
|
|
|
|
* gives an easy way to probe the behavior of the Mass Storage drivers
|
|
|
|
* in a USB host.
|
|
|
|
*
|
|
|
|
* Since this file serves only administrative purposes and all the
|
|
|
|
* business logic is implemented in f_mass_storage.* file. Read
|
|
|
|
* comments in this file for more detailed description.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/usb/ch9.h>
|
2012-09-07 02:11:27 +08:00
|
|
|
#include <linux/module.h>
|
2009-11-09 21:15:20 +08:00
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
#define DRIVER_DESC "Mass Storage Gadget"
|
2009-11-09 21:15:23 +08:00
|
|
|
#define DRIVER_VERSION "2009/09/11"
|
2009-11-09 21:15:20 +08:00
|
|
|
|
2013-09-26 20:38:16 +08:00
|
|
|
/*
|
|
|
|
* Thanks to NetChip Technologies for donating this product ID.
|
|
|
|
*
|
|
|
|
* DO NOT REUSE THESE IDs with any other driver!! Ever!!
|
|
|
|
* Instead: allocate your own, using normal USB-IF procedures.
|
|
|
|
*/
|
|
|
|
#define FSG_VENDOR_ID 0x0525 /* NetChip */
|
|
|
|
#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
|
|
|
|
|
2013-10-09 16:06:02 +08:00
|
|
|
#include "f_mass_storage.h"
|
2009-11-09 21:15:20 +08:00
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
2012-09-10 21:01:53 +08:00
|
|
|
USB_GADGET_COMPOSITE_OPTIONS();
|
2009-11-09 21:15:20 +08:00
|
|
|
|
|
|
|
static struct usb_device_descriptor msg_device_desc = {
|
|
|
|
.bLength = sizeof msg_device_desc,
|
|
|
|
.bDescriptorType = USB_DT_DEVICE,
|
|
|
|
|
2015-10-21 00:33:13 +08:00
|
|
|
/* .bcdUSB = DYNAMIC */
|
2009-11-09 21:15:20 +08:00
|
|
|
.bDeviceClass = USB_CLASS_PER_INTERFACE,
|
|
|
|
|
|
|
|
/* Vendor and product id can be overridden by module parameters. */
|
|
|
|
.idVendor = cpu_to_le16(FSG_VENDOR_ID),
|
|
|
|
.idProduct = cpu_to_le16(FSG_PRODUCT_ID),
|
|
|
|
.bNumConfigurations = 1,
|
|
|
|
};
|
|
|
|
|
2015-07-09 15:18:55 +08:00
|
|
|
static const struct usb_descriptor_header *otg_desc[2];
|
2009-11-09 21:15:20 +08:00
|
|
|
|
2012-09-10 21:01:54 +08:00
|
|
|
static struct usb_string strings_dev[] = {
|
|
|
|
[USB_GADGET_MANUFACTURER_IDX].s = "",
|
2012-09-10 21:01:57 +08:00
|
|
|
[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
|
2012-09-10 21:01:54 +08:00
|
|
|
[USB_GADGET_SERIAL_IDX].s = "",
|
|
|
|
{ } /* end of list */
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct usb_gadget_strings stringtab_dev = {
|
|
|
|
.language = 0x0409, /* en-us */
|
|
|
|
.strings = strings_dev,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct usb_gadget_strings *dev_strings[] = {
|
|
|
|
&stringtab_dev,
|
|
|
|
NULL,
|
|
|
|
};
|
2009-11-09 21:15:20 +08:00
|
|
|
|
2013-10-09 16:06:02 +08:00
|
|
|
static struct usb_function_instance *fi_msg;
|
|
|
|
static struct usb_function *f_msg;
|
|
|
|
|
2009-11-09 21:15:20 +08:00
|
|
|
/****************************** Configurations ******************************/
|
|
|
|
|
2009-11-09 21:15:21 +08:00
|
|
|
static struct fsg_module_parameters mod_data = {
|
|
|
|
.stall = 1
|
|
|
|
};
|
2013-09-26 20:38:16 +08:00
|
|
|
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
|
|
|
|
|
|
|
static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Number of buffers we will use.
|
|
|
|
* 2 is usually enough for good buffering pipeline
|
|
|
|
*/
|
|
|
|
#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
|
|
|
|
|
|
|
|
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
|
|
|
|
|
2009-11-09 21:15:21 +08:00
|
|
|
FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
|
|
|
|
|
2015-04-11 06:14:21 +08:00
|
|
|
static int msg_do_config(struct usb_configuration *c)
|
2009-11-09 21:15:20 +08:00
|
|
|
{
|
2013-10-09 16:06:02 +08:00
|
|
|
struct fsg_opts *opts;
|
2009-11-09 21:15:20 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (gadget_is_otg(c->cdev->gadget)) {
|
|
|
|
c->descriptors = otg_desc;
|
|
|
|
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
|
|
|
}
|
|
|
|
|
2013-10-09 16:06:02 +08:00
|
|
|
opts = fsg_opts_from_func_inst(fi_msg);
|
|
|
|
|
|
|
|
f_msg = usb_get_function(fi_msg);
|
|
|
|
if (IS_ERR(f_msg))
|
|
|
|
return PTR_ERR(f_msg);
|
2009-11-09 21:15:20 +08:00
|
|
|
|
2013-10-09 16:06:02 +08:00
|
|
|
ret = usb_add_function(c, f_msg);
|
|
|
|
if (ret)
|
|
|
|
goto put_func;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
put_func:
|
|
|
|
usb_put_function(f_msg);
|
2009-11-09 21:15:20 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct usb_configuration msg_config_driver = {
|
|
|
|
.label = "Linux File-Backed Storage",
|
|
|
|
.bConfigurationValue = 1,
|
|
|
|
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/****************************** Gadget Bind ******************************/
|
|
|
|
|
2015-04-11 06:14:21 +08:00
|
|
|
static int msg_bind(struct usb_composite_dev *cdev)
|
2009-11-09 21:15:20 +08:00
|
|
|
{
|
2013-10-09 16:06:02 +08:00
|
|
|
struct fsg_opts *opts;
|
|
|
|
struct fsg_config config;
|
2009-11-09 21:15:20 +08:00
|
|
|
int status;
|
|
|
|
|
2013-10-09 16:06:02 +08:00
|
|
|
fi_msg = usb_get_function_instance("mass_storage");
|
|
|
|
if (IS_ERR(fi_msg))
|
|
|
|
return PTR_ERR(fi_msg);
|
|
|
|
|
|
|
|
fsg_config_from_params(&config, &mod_data, fsg_num_buffers);
|
|
|
|
opts = fsg_opts_from_func_inst(fi_msg);
|
|
|
|
|
|
|
|
opts->no_configfs = true;
|
|
|
|
status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
|
|
|
|
if (status)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
|
|
|
|
if (status)
|
|
|
|
goto fail_set_cdev;
|
|
|
|
|
|
|
|
fsg_common_set_sysfs(opts->common, true);
|
|
|
|
status = fsg_common_create_luns(opts->common, &config);
|
|
|
|
if (status)
|
|
|
|
goto fail_set_cdev;
|
|
|
|
|
|
|
|
fsg_common_set_inquiry_string(opts->common, config.vendor_name,
|
|
|
|
config.product_name);
|
|
|
|
|
2012-09-10 21:01:54 +08:00
|
|
|
status = usb_string_ids_tab(cdev, strings_dev);
|
|
|
|
if (status < 0)
|
2013-10-09 16:06:02 +08:00
|
|
|
goto fail_string_ids;
|
2012-09-10 21:01:57 +08:00
|
|
|
msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
|
2012-09-10 21:01:54 +08:00
|
|
|
|
2015-07-09 15:18:55 +08:00
|
|
|
if (gadget_is_otg(cdev->gadget) && !otg_desc[0]) {
|
|
|
|
struct usb_descriptor_header *usb_desc;
|
|
|
|
|
|
|
|
usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
|
|
|
|
if (!usb_desc)
|
|
|
|
goto fail_string_ids;
|
|
|
|
usb_otg_descriptor_init(cdev->gadget, usb_desc);
|
|
|
|
otg_desc[0] = usb_desc;
|
|
|
|
otg_desc[1] = NULL;
|
|
|
|
}
|
|
|
|
|
2010-08-12 23:43:55 +08:00
|
|
|
status = usb_add_config(cdev, &msg_config_driver, msg_do_config);
|
2009-11-09 21:15:20 +08:00
|
|
|
if (status < 0)
|
2015-07-09 15:18:55 +08:00
|
|
|
goto fail_otg_desc;
|
2013-10-09 16:06:02 +08:00
|
|
|
|
2012-09-10 21:01:53 +08:00
|
|
|
usb_composite_overwrite_options(cdev, &coverwrite);
|
2010-08-12 23:43:47 +08:00
|
|
|
dev_info(&cdev->gadget->dev,
|
|
|
|
DRIVER_DESC ", version: " DRIVER_VERSION "\n");
|
2009-11-09 21:15:20 +08:00
|
|
|
return 0;
|
2013-10-09 16:06:02 +08:00
|
|
|
|
2015-07-09 15:18:55 +08:00
|
|
|
fail_otg_desc:
|
|
|
|
kfree(otg_desc[0]);
|
|
|
|
otg_desc[0] = NULL;
|
2013-10-09 16:06:02 +08:00
|
|
|
fail_string_ids:
|
|
|
|
fsg_common_remove_luns(opts->common);
|
|
|
|
fail_set_cdev:
|
|
|
|
fsg_common_free_buffers(opts->common);
|
|
|
|
fail:
|
|
|
|
usb_put_function_instance(fi_msg);
|
|
|
|
return status;
|
2009-11-09 21:15:20 +08:00
|
|
|
}
|
|
|
|
|
2013-10-09 16:06:02 +08:00
|
|
|
static int msg_unbind(struct usb_composite_dev *cdev)
|
|
|
|
{
|
|
|
|
if (!IS_ERR(f_msg))
|
|
|
|
usb_put_function(f_msg);
|
|
|
|
|
|
|
|
if (!IS_ERR(fi_msg))
|
|
|
|
usb_put_function_instance(fi_msg);
|
|
|
|
|
2015-07-09 15:18:55 +08:00
|
|
|
kfree(otg_desc[0]);
|
|
|
|
otg_desc[0] = NULL;
|
|
|
|
|
2013-10-09 16:06:02 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2009-11-09 21:15:20 +08:00
|
|
|
|
|
|
|
/****************************** Some noise ******************************/
|
|
|
|
|
2015-04-11 06:14:21 +08:00
|
|
|
static struct usb_composite_driver msg_driver = {
|
2009-11-09 21:15:20 +08:00
|
|
|
.name = "g_mass_storage",
|
|
|
|
.dev = &msg_device_desc,
|
2011-08-03 19:33:27 +08:00
|
|
|
.max_speed = USB_SPEED_SUPER,
|
2010-08-12 23:43:47 +08:00
|
|
|
.needs_serial = 1,
|
2012-09-10 21:01:54 +08:00
|
|
|
.strings = dev_strings,
|
2012-09-07 02:11:04 +08:00
|
|
|
.bind = msg_bind,
|
2013-10-09 16:06:02 +08:00
|
|
|
.unbind = msg_unbind,
|
2009-11-09 21:15:20 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION(DRIVER_DESC);
|
|
|
|
MODULE_AUTHOR("Michal Nazarewicz");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
|
|
static int __init msg_init(void)
|
|
|
|
{
|
USB: g_mass_storage: Fix deadlock when driver is unbound
As a holdover from the old g_file_storage gadget, the g_mass_storage
legacy gadget driver attempts to unregister itself when its main
operating thread terminates (if it hasn't been unregistered already).
This is not strictly necessary; it was never more than an attempt to
have the gadget fail cleanly if something went wrong and the main
thread was killed.
However, now that the UDC core manages gadget drivers independently of
UDC drivers, this scheme doesn't work any more. A simple test:
modprobe dummy-hcd
modprobe g-mass-storage file=...
rmmod dummy-hcd
ends up in a deadlock with the following backtrace:
sysrq: SysRq : Show Blocked State
task PC stack pid father
file-storage D 0 1130 2 0x00000000
Call Trace:
__schedule+0x53e/0x58c
schedule+0x6e/0x77
schedule_preempt_disabled+0xd/0xf
__mutex_lock.isra.1+0x129/0x224
? _raw_spin_unlock_irqrestore+0x12/0x14
__mutex_lock_slowpath+0x12/0x14
mutex_lock+0x28/0x2b
usb_gadget_unregister_driver+0x29/0x9b [udc_core]
usb_composite_unregister+0x10/0x12 [libcomposite]
msg_cleanup+0x1d/0x20 [g_mass_storage]
msg_thread_exits+0xd/0xdd7 [g_mass_storage]
fsg_main_thread+0x1395/0x13d6 [usb_f_mass_storage]
? __schedule+0x573/0x58c
kthread+0xd9/0xdb
? do_set_interface+0x25c/0x25c [usb_f_mass_storage]
? init_completion+0x1e/0x1e
ret_from_fork+0x19/0x24
rmmod D 0 1155 683 0x00000000
Call Trace:
__schedule+0x53e/0x58c
schedule+0x6e/0x77
schedule_timeout+0x26/0xbc
? __schedule+0x573/0x58c
do_wait_for_common+0xb3/0x128
? usleep_range+0x81/0x81
? wake_up_q+0x3f/0x3f
wait_for_common+0x2e/0x45
wait_for_completion+0x17/0x19
fsg_common_put+0x34/0x81 [usb_f_mass_storage]
fsg_free_inst+0x13/0x1e [usb_f_mass_storage]
usb_put_function_instance+0x1a/0x25 [libcomposite]
msg_unbind+0x2a/0x42 [g_mass_storage]
__composite_unbind+0x4a/0x6f [libcomposite]
composite_unbind+0x12/0x14 [libcomposite]
usb_gadget_remove_driver+0x4f/0x77 [udc_core]
usb_del_gadget_udc+0x52/0xcc [udc_core]
dummy_udc_remove+0x27/0x2c [dummy_hcd]
platform_drv_remove+0x1d/0x31
device_release_driver_internal+0xe9/0x16d
device_release_driver+0x11/0x13
bus_remove_device+0xd2/0xe2
device_del+0x19f/0x221
? selinux_capable+0x22/0x27
platform_device_del+0x21/0x63
platform_device_unregister+0x10/0x1a
cleanup+0x20/0x817 [dummy_hcd]
SyS_delete_module+0x10c/0x197
? ____fput+0xd/0xf
? task_work_run+0x55/0x62
? prepare_exit_to_usermode+0x65/0x75
do_fast_syscall_32+0x86/0xc3
entry_SYSENTER_32+0x4e/0x7c
What happens is that removing the dummy-hcd driver causes the UDC core
to unbind the gadget driver, which it does while holding the udc_lock
mutex. The unbind routine in g_mass_storage tells the main thread to
exit and waits for it to terminate.
But as mentioned above, when the main thread exits it tries to
unregister the mass-storage function driver. Via the composite
framework this ends up calling usb_gadget_unregister_driver(), which
tries to acquire the udc_lock mutex. The result is deadlock.
The simplest way to fix the problem is not to be so clever: The main
thread doesn't have to unregister the function driver. The side
effects won't be so terrible; if the gadget is still attached to a USB
host when the main thread is killed, it will appear to the host as
though the gadget's firmware has crashed -- a reasonably accurate
interpretation, and an all-too-common occurrence for USB mass-storage
devices.
In fact, the code to unregister the driver when the main thread exits
is specific to g-mass-storage; it is not used when f-mass-storage is
included as a function in a larger composite device. Therefore the
entire mechanism responsible for this (the fsg_operations structure
with its ->thread_exits method, the fsg_common_set_ops() routine, and
the msg_thread_exits() callback routine) can all be eliminated. Even
the msg_registered bitflag can be removed, because now the driver is
unregistered in only one place rather than in two places.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: <stable@vger.kernel.org>
Acked-by: Felipe Balbi <felipe.balbi@linux.intel.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-09-22 01:22:00 +08:00
|
|
|
return usb_composite_probe(&msg_driver);
|
2009-11-09 21:15:20 +08:00
|
|
|
}
|
|
|
|
module_init(msg_init);
|
|
|
|
|
USB: g_mass_storage: Fix deadlock when driver is unbound
As a holdover from the old g_file_storage gadget, the g_mass_storage
legacy gadget driver attempts to unregister itself when its main
operating thread terminates (if it hasn't been unregistered already).
This is not strictly necessary; it was never more than an attempt to
have the gadget fail cleanly if something went wrong and the main
thread was killed.
However, now that the UDC core manages gadget drivers independently of
UDC drivers, this scheme doesn't work any more. A simple test:
modprobe dummy-hcd
modprobe g-mass-storage file=...
rmmod dummy-hcd
ends up in a deadlock with the following backtrace:
sysrq: SysRq : Show Blocked State
task PC stack pid father
file-storage D 0 1130 2 0x00000000
Call Trace:
__schedule+0x53e/0x58c
schedule+0x6e/0x77
schedule_preempt_disabled+0xd/0xf
__mutex_lock.isra.1+0x129/0x224
? _raw_spin_unlock_irqrestore+0x12/0x14
__mutex_lock_slowpath+0x12/0x14
mutex_lock+0x28/0x2b
usb_gadget_unregister_driver+0x29/0x9b [udc_core]
usb_composite_unregister+0x10/0x12 [libcomposite]
msg_cleanup+0x1d/0x20 [g_mass_storage]
msg_thread_exits+0xd/0xdd7 [g_mass_storage]
fsg_main_thread+0x1395/0x13d6 [usb_f_mass_storage]
? __schedule+0x573/0x58c
kthread+0xd9/0xdb
? do_set_interface+0x25c/0x25c [usb_f_mass_storage]
? init_completion+0x1e/0x1e
ret_from_fork+0x19/0x24
rmmod D 0 1155 683 0x00000000
Call Trace:
__schedule+0x53e/0x58c
schedule+0x6e/0x77
schedule_timeout+0x26/0xbc
? __schedule+0x573/0x58c
do_wait_for_common+0xb3/0x128
? usleep_range+0x81/0x81
? wake_up_q+0x3f/0x3f
wait_for_common+0x2e/0x45
wait_for_completion+0x17/0x19
fsg_common_put+0x34/0x81 [usb_f_mass_storage]
fsg_free_inst+0x13/0x1e [usb_f_mass_storage]
usb_put_function_instance+0x1a/0x25 [libcomposite]
msg_unbind+0x2a/0x42 [g_mass_storage]
__composite_unbind+0x4a/0x6f [libcomposite]
composite_unbind+0x12/0x14 [libcomposite]
usb_gadget_remove_driver+0x4f/0x77 [udc_core]
usb_del_gadget_udc+0x52/0xcc [udc_core]
dummy_udc_remove+0x27/0x2c [dummy_hcd]
platform_drv_remove+0x1d/0x31
device_release_driver_internal+0xe9/0x16d
device_release_driver+0x11/0x13
bus_remove_device+0xd2/0xe2
device_del+0x19f/0x221
? selinux_capable+0x22/0x27
platform_device_del+0x21/0x63
platform_device_unregister+0x10/0x1a
cleanup+0x20/0x817 [dummy_hcd]
SyS_delete_module+0x10c/0x197
? ____fput+0xd/0xf
? task_work_run+0x55/0x62
? prepare_exit_to_usermode+0x65/0x75
do_fast_syscall_32+0x86/0xc3
entry_SYSENTER_32+0x4e/0x7c
What happens is that removing the dummy-hcd driver causes the UDC core
to unbind the gadget driver, which it does while holding the udc_lock
mutex. The unbind routine in g_mass_storage tells the main thread to
exit and waits for it to terminate.
But as mentioned above, when the main thread exits it tries to
unregister the mass-storage function driver. Via the composite
framework this ends up calling usb_gadget_unregister_driver(), which
tries to acquire the udc_lock mutex. The result is deadlock.
The simplest way to fix the problem is not to be so clever: The main
thread doesn't have to unregister the function driver. The side
effects won't be so terrible; if the gadget is still attached to a USB
host when the main thread is killed, it will appear to the host as
though the gadget's firmware has crashed -- a reasonably accurate
interpretation, and an all-too-common occurrence for USB mass-storage
devices.
In fact, the code to unregister the driver when the main thread exits
is specific to g-mass-storage; it is not used when f-mass-storage is
included as a function in a larger composite device. Therefore the
entire mechanism responsible for this (the fsg_operations structure
with its ->thread_exits method, the fsg_common_set_ops() routine, and
the msg_thread_exits() callback routine) can all be eliminated. Even
the msg_registered bitflag can be removed, because now the driver is
unregistered in only one place rather than in two places.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: <stable@vger.kernel.org>
Acked-by: Felipe Balbi <felipe.balbi@linux.intel.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-09-22 01:22:00 +08:00
|
|
|
static void __exit msg_cleanup(void)
|
2009-11-09 21:15:20 +08:00
|
|
|
{
|
USB: g_mass_storage: Fix deadlock when driver is unbound
As a holdover from the old g_file_storage gadget, the g_mass_storage
legacy gadget driver attempts to unregister itself when its main
operating thread terminates (if it hasn't been unregistered already).
This is not strictly necessary; it was never more than an attempt to
have the gadget fail cleanly if something went wrong and the main
thread was killed.
However, now that the UDC core manages gadget drivers independently of
UDC drivers, this scheme doesn't work any more. A simple test:
modprobe dummy-hcd
modprobe g-mass-storage file=...
rmmod dummy-hcd
ends up in a deadlock with the following backtrace:
sysrq: SysRq : Show Blocked State
task PC stack pid father
file-storage D 0 1130 2 0x00000000
Call Trace:
__schedule+0x53e/0x58c
schedule+0x6e/0x77
schedule_preempt_disabled+0xd/0xf
__mutex_lock.isra.1+0x129/0x224
? _raw_spin_unlock_irqrestore+0x12/0x14
__mutex_lock_slowpath+0x12/0x14
mutex_lock+0x28/0x2b
usb_gadget_unregister_driver+0x29/0x9b [udc_core]
usb_composite_unregister+0x10/0x12 [libcomposite]
msg_cleanup+0x1d/0x20 [g_mass_storage]
msg_thread_exits+0xd/0xdd7 [g_mass_storage]
fsg_main_thread+0x1395/0x13d6 [usb_f_mass_storage]
? __schedule+0x573/0x58c
kthread+0xd9/0xdb
? do_set_interface+0x25c/0x25c [usb_f_mass_storage]
? init_completion+0x1e/0x1e
ret_from_fork+0x19/0x24
rmmod D 0 1155 683 0x00000000
Call Trace:
__schedule+0x53e/0x58c
schedule+0x6e/0x77
schedule_timeout+0x26/0xbc
? __schedule+0x573/0x58c
do_wait_for_common+0xb3/0x128
? usleep_range+0x81/0x81
? wake_up_q+0x3f/0x3f
wait_for_common+0x2e/0x45
wait_for_completion+0x17/0x19
fsg_common_put+0x34/0x81 [usb_f_mass_storage]
fsg_free_inst+0x13/0x1e [usb_f_mass_storage]
usb_put_function_instance+0x1a/0x25 [libcomposite]
msg_unbind+0x2a/0x42 [g_mass_storage]
__composite_unbind+0x4a/0x6f [libcomposite]
composite_unbind+0x12/0x14 [libcomposite]
usb_gadget_remove_driver+0x4f/0x77 [udc_core]
usb_del_gadget_udc+0x52/0xcc [udc_core]
dummy_udc_remove+0x27/0x2c [dummy_hcd]
platform_drv_remove+0x1d/0x31
device_release_driver_internal+0xe9/0x16d
device_release_driver+0x11/0x13
bus_remove_device+0xd2/0xe2
device_del+0x19f/0x221
? selinux_capable+0x22/0x27
platform_device_del+0x21/0x63
platform_device_unregister+0x10/0x1a
cleanup+0x20/0x817 [dummy_hcd]
SyS_delete_module+0x10c/0x197
? ____fput+0xd/0xf
? task_work_run+0x55/0x62
? prepare_exit_to_usermode+0x65/0x75
do_fast_syscall_32+0x86/0xc3
entry_SYSENTER_32+0x4e/0x7c
What happens is that removing the dummy-hcd driver causes the UDC core
to unbind the gadget driver, which it does while holding the udc_lock
mutex. The unbind routine in g_mass_storage tells the main thread to
exit and waits for it to terminate.
But as mentioned above, when the main thread exits it tries to
unregister the mass-storage function driver. Via the composite
framework this ends up calling usb_gadget_unregister_driver(), which
tries to acquire the udc_lock mutex. The result is deadlock.
The simplest way to fix the problem is not to be so clever: The main
thread doesn't have to unregister the function driver. The side
effects won't be so terrible; if the gadget is still attached to a USB
host when the main thread is killed, it will appear to the host as
though the gadget's firmware has crashed -- a reasonably accurate
interpretation, and an all-too-common occurrence for USB mass-storage
devices.
In fact, the code to unregister the driver when the main thread exits
is specific to g-mass-storage; it is not used when f-mass-storage is
included as a function in a larger composite device. Therefore the
entire mechanism responsible for this (the fsg_operations structure
with its ->thread_exits method, the fsg_common_set_ops() routine, and
the msg_thread_exits() callback routine) can all be eliminated. Even
the msg_registered bitflag can be removed, because now the driver is
unregistered in only one place rather than in two places.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: <stable@vger.kernel.org>
Acked-by: Felipe Balbi <felipe.balbi@linux.intel.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-09-22 01:22:00 +08:00
|
|
|
usb_composite_unregister(&msg_driver);
|
2009-11-09 21:15:20 +08:00
|
|
|
}
|
|
|
|
module_exit(msg_cleanup);
|