Skip to content

Commit 1fbbb78

Browse files
AlanSterngregkh
authored andcommitted
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 <[email protected]> CC: <[email protected]> Acked-by: Felipe Balbi <[email protected]> Acked-by: Michal Nazarewicz <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 520b72f commit 1fbbb78

File tree

3 files changed

+10
-57
lines changed

3 files changed

+10
-57
lines changed

drivers/usb/gadget/function/f_mass_storage.c

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,6 @@ struct fsg_common {
307307
struct completion thread_notifier;
308308
struct task_struct *thread_task;
309309

310-
/* Callback functions. */
311-
const struct fsg_operations *ops;
312310
/* Gadget's private data. */
313311
void *private_data;
314312

@@ -2438,6 +2436,7 @@ static void handle_exception(struct fsg_common *common)
24382436
static int fsg_main_thread(void *common_)
24392437
{
24402438
struct fsg_common *common = common_;
2439+
int i;
24412440

24422441
/*
24432442
* Allow the thread to be killed by a signal, but set the signal mask
@@ -2476,21 +2475,16 @@ static int fsg_main_thread(void *common_)
24762475
common->thread_task = NULL;
24772476
spin_unlock_irq(&common->lock);
24782477

2479-
if (!common->ops || !common->ops->thread_exits
2480-
|| common->ops->thread_exits(common) < 0) {
2481-
int i;
2478+
/* Eject media from all LUNs */
24822479

2483-
down_write(&common->filesem);
2484-
for (i = 0; i < ARRAY_SIZE(common->luns); i++) {
2485-
struct fsg_lun *curlun = common->luns[i];
2486-
if (!curlun || !fsg_lun_is_open(curlun))
2487-
continue;
2480+
down_write(&common->filesem);
2481+
for (i = 0; i < ARRAY_SIZE(common->luns); i++) {
2482+
struct fsg_lun *curlun = common->luns[i];
24882483

2484+
if (curlun && fsg_lun_is_open(curlun))
24892485
fsg_lun_close(curlun);
2490-
curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
2491-
}
2492-
up_write(&common->filesem);
24932486
}
2487+
up_write(&common->filesem);
24942488

24952489
/* Let fsg_unbind() know the thread has exited */
24962490
complete_and_exit(&common->thread_notifier, 0);
@@ -2681,13 +2675,6 @@ void fsg_common_remove_luns(struct fsg_common *common)
26812675
}
26822676
EXPORT_SYMBOL_GPL(fsg_common_remove_luns);
26832677

2684-
void fsg_common_set_ops(struct fsg_common *common,
2685-
const struct fsg_operations *ops)
2686-
{
2687-
common->ops = ops;
2688-
}
2689-
EXPORT_SYMBOL_GPL(fsg_common_set_ops);
2690-
26912678
void fsg_common_free_buffers(struct fsg_common *common)
26922679
{
26932680
_fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);

drivers/usb/gadget/function/f_mass_storage.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,6 @@ struct fsg_module_parameters {
6060
struct fsg_common;
6161

6262
/* FSF callback functions */
63-
struct fsg_operations {
64-
/*
65-
* Callback function to call when thread exits. If no
66-
* callback is set or it returns value lower then zero MSF
67-
* will force eject all LUNs it operates on (including those
68-
* marked as non-removable or with prevent_medium_removal flag
69-
* set).
70-
*/
71-
int (*thread_exits)(struct fsg_common *common);
72-
};
73-
7463
struct fsg_lun_opts {
7564
struct config_group group;
7665
struct fsg_lun *lun;
@@ -142,9 +131,6 @@ void fsg_common_remove_lun(struct fsg_lun *lun);
142131

143132
void fsg_common_remove_luns(struct fsg_common *common);
144133

145-
void fsg_common_set_ops(struct fsg_common *common,
146-
const struct fsg_operations *ops);
147-
148134
int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
149135
unsigned int id, const char *name,
150136
const char **name_pfx);

drivers/usb/gadget/legacy/mass_storage.c

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,6 @@ static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
107107

108108
FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
109109

110-
static unsigned long msg_registered;
111-
static void msg_cleanup(void);
112-
113-
static int msg_thread_exits(struct fsg_common *common)
114-
{
115-
msg_cleanup();
116-
return 0;
117-
}
118-
119110
static int msg_do_config(struct usb_configuration *c)
120111
{
121112
struct fsg_opts *opts;
@@ -154,9 +145,6 @@ static struct usb_configuration msg_config_driver = {
154145

155146
static int msg_bind(struct usb_composite_dev *cdev)
156147
{
157-
static const struct fsg_operations ops = {
158-
.thread_exits = msg_thread_exits,
159-
};
160148
struct fsg_opts *opts;
161149
struct fsg_config config;
162150
int status;
@@ -173,8 +161,6 @@ static int msg_bind(struct usb_composite_dev *cdev)
173161
if (status)
174162
goto fail;
175163

176-
fsg_common_set_ops(opts->common, &ops);
177-
178164
status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
179165
if (status)
180166
goto fail_set_cdev;
@@ -256,18 +242,12 @@ MODULE_LICENSE("GPL");
256242

257243
static int __init msg_init(void)
258244
{
259-
int ret;
260-
261-
ret = usb_composite_probe(&msg_driver);
262-
set_bit(0, &msg_registered);
263-
264-
return ret;
245+
return usb_composite_probe(&msg_driver);
265246
}
266247
module_init(msg_init);
267248

268-
static void msg_cleanup(void)
249+
static void __exit msg_cleanup(void)
269250
{
270-
if (test_and_clear_bit(0, &msg_registered))
271-
usb_composite_unregister(&msg_driver);
251+
usb_composite_unregister(&msg_driver);
272252
}
273253
module_exit(msg_cleanup);

0 commit comments

Comments
 (0)