diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 2dae474dc..9914c8b58 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -744,7 +744,11 @@ static void vub300_inactivity_timer_expired(struct timer_list *t) struct vub300_mmc_host *vub300 = timer_container_of(vub300, t, inactivity_timer); if (!vub300->interface) { - kref_put(&vub300->kref, vub300_delete); + /* + * The disconnect/error paths have already shut this timer + * down, so it can no longer be rearmed or run. + */ + return; } else if (vub300->cmd) { mod_timer(&vub300->inactivity_timer, jiffies + HZ); } else { @@ -2327,7 +2331,6 @@ static int vub300_probe(struct usb_interface *interface, INIT_WORK(&vub300->deadwork, vub300_deadwork_thread); kref_init(&vub300->kref); timer_setup(&vub300->sg_transfer_timer, vub300_sg_timed_out, 0); - kref_get(&vub300->kref); timer_setup(&vub300->inactivity_timer, vub300_inactivity_timer_expired, 0); vub300->inactivity_timer.expires = jiffies + HZ; @@ -2349,6 +2352,13 @@ static int vub300_probe(struct usb_interface *interface, return 0; err_stop_io: + /* + * The inactivity timer was armed before mmc_add_host() failed. + * Shut it down before dropping the host reference so that its + * callback can neither run nor be rearmed after the memory + * holding the timer has been freed. + */ + timer_shutdown_sync(&vub300->inactivity_timer); vub300->interface = NULL; kref_put(&vub300->kref, vub300_delete); @@ -2382,6 +2392,13 @@ static void vub300_disconnect(struct usb_interface *interface) } else { int ifnum = interface_to_InterfaceNumber(interface); usb_set_intfdata(interface, NULL); + /* + * Shut the inactivity timer down before clearing interface. + * Otherwise it can be rearmed (by its own callback or by a + * concurrent path that already checked interface) and later + * fire after the host, which embeds the timer, is freed. + */ + timer_shutdown_sync(&vub300->inactivity_timer); /* prevent more I/O from starting */ vub300->interface = NULL; mmc_remove_host(mmc);