Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: Properly unregister reboot notifier in case of failure in ehci hcd

If some problem occurs during ehci startup, for instance, request_irq fails,
echi hcd driver tries it best to cleanup, but fails to unregister reboot
notifier, which in turn leads to crash on reboot/poweroff.

The following patch resolves this problem by not using reboot notifiers
anymore, but instead making ehci/ohci driver get its own shutdown method. For
PCI, it is done through pci glue, for everything else through platform driver
glue.

One downside: sa1111 does not use platform driver stuff, and does not have its
own shutdown hook, so no 'shutdown' is called for it now. I'm not sure if it
is really necessary on that platform, though.

Signed-off-by: Aleks Gorelov <dared1st@yahoo.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Aleksey Gorelov and committed by
Greg Kroah-Hartman
64a21d02 a94da897

+69 -23
+16
drivers/usb/core/hcd-pci.c
··· 413 413 414 414 #endif /* CONFIG_PM */ 415 415 416 + /** 417 + * usb_hcd_pci_shutdown - shutdown host controller 418 + * @dev: USB Host Controller being shutdown 419 + */ 420 + void usb_hcd_pci_shutdown (struct pci_dev *dev) 421 + { 422 + struct usb_hcd *hcd; 423 + 424 + hcd = pci_get_drvdata(dev); 425 + if (!hcd) 426 + return; 427 + 428 + if (hcd->driver->shutdown) 429 + hcd->driver->shutdown(hcd); 430 + } 431 + EXPORT_SYMBOL (usb_hcd_pci_shutdown); 416 432
+11
drivers/usb/core/hcd.c
··· 36 36 #include <linux/mutex.h> 37 37 #include <asm/irq.h> 38 38 #include <asm/byteorder.h> 39 + #include <linux/platform_device.h> 39 40 40 41 #include <linux/usb.h> 41 42 ··· 1915 1914 hcd_buffer_destroy(hcd); 1916 1915 } 1917 1916 EXPORT_SYMBOL (usb_remove_hcd); 1917 + 1918 + void 1919 + usb_hcd_platform_shutdown(struct platform_device* dev) 1920 + { 1921 + struct usb_hcd *hcd = platform_get_drvdata(dev); 1922 + 1923 + if (hcd->driver->shutdown) 1924 + hcd->driver->shutdown(hcd); 1925 + } 1926 + EXPORT_SYMBOL (usb_hcd_platform_shutdown); 1918 1927 1919 1928 /*-------------------------------------------------------------------------*/ 1920 1929
+8
drivers/usb/core/hcd.h
··· 192 192 /* cleanly make HCD stop writing memory and doing I/O */ 193 193 void (*stop) (struct usb_hcd *hcd); 194 194 195 + /* shutdown HCD */ 196 + void (*shutdown) (struct usb_hcd *hcd); 197 + 195 198 /* return current frame number */ 196 199 int (*get_frame_number) (struct usb_hcd *hcd); 197 200 ··· 230 227 unsigned int irqnum, unsigned long irqflags); 231 228 extern void usb_remove_hcd(struct usb_hcd *hcd); 232 229 230 + struct platform_device; 231 + extern void usb_hcd_platform_shutdown(struct platform_device* dev); 232 + 233 233 #ifdef CONFIG_PCI 234 234 struct pci_dev; 235 235 struct pci_device_id; ··· 244 238 extern int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t state); 245 239 extern int usb_hcd_pci_resume (struct pci_dev *dev); 246 240 #endif /* CONFIG_PM */ 241 + 242 + extern void usb_hcd_pci_shutdown (struct pci_dev *dev); 247 243 248 244 #endif /* CONFIG_PCI */ 249 245
+2
drivers/usb/host/ehci-au1xxx.c
··· 200 200 .reset = ehci_init, 201 201 .start = ehci_run, 202 202 .stop = ehci_stop, 203 + .shutdown = ehci_shutdown, 203 204 204 205 /* 205 206 * managing i/o requests and associated device resources ··· 269 268 static struct platform_driver ehci_hcd_au1xxx_driver = { 270 269 .probe = ehci_hcd_au1xxx_drv_probe, 271 270 .remove = ehci_hcd_au1xxx_drv_remove, 271 + .shutdown = usb_hcd_platform_shutdown, 272 272 /*.suspend = ehci_hcd_au1xxx_drv_suspend, */ 273 273 /*.resume = ehci_hcd_au1xxx_drv_resume, */ 274 274 .driver = {
+2
drivers/usb/host/ehci-fsl.c
··· 285 285 .resume = ehci_bus_resume, 286 286 #endif 287 287 .stop = ehci_stop, 288 + .shutdown = ehci_shutdown, 288 289 289 290 /* 290 291 * managing i/o requests and associated device resources ··· 330 329 static struct platform_driver ehci_fsl_driver = { 331 330 .probe = ehci_fsl_drv_probe, 332 331 .remove = ehci_fsl_drv_remove, 332 + .shutdown = usb_hcd_platform_shutdown, 333 333 .driver = { 334 334 .name = "fsl-ehci", 335 335 },
+5 -11
drivers/usb/host/ehci-hcd.c
··· 292 292 spin_unlock_irqrestore (&ehci->lock, flags); 293 293 } 294 294 295 - /* Reboot notifiers kick in for silicon on any bus (not just pci, etc). 295 + /* ehci_shutdown kick in for silicon on any bus (not just pci, etc). 296 296 * This forcibly disables dma and IRQs, helping kexec and other cases 297 297 * where the next system software may expect clean state. 298 298 */ 299 - static int 300 - ehci_reboot (struct notifier_block *self, unsigned long code, void *null) 299 + static void 300 + ehci_shutdown (struct usb_hcd *hcd) 301 301 { 302 - struct ehci_hcd *ehci; 302 + struct ehci_hcd *ehci; 303 303 304 - ehci = container_of (self, struct ehci_hcd, reboot_notifier); 304 + ehci = hcd_to_ehci (hcd); 305 305 (void) ehci_halt (ehci); 306 306 307 307 /* make BIOS/etc use companion controller during reboot */ 308 308 writel (0, &ehci->regs->configured_flag); 309 - return 0; 310 309 } 311 310 312 311 static void ehci_port_power (struct ehci_hcd *ehci, int is_on) ··· 380 381 381 382 /* let companion controllers work when we aren't */ 382 383 writel (0, &ehci->regs->configured_flag); 383 - unregister_reboot_notifier (&ehci->reboot_notifier); 384 384 385 385 remove_debug_files (ehci); 386 386 ··· 481 483 } 482 484 ehci->command = temp; 483 485 484 - ehci->reboot_notifier.notifier_call = ehci_reboot; 485 - register_reboot_notifier(&ehci->reboot_notifier); 486 - 487 486 return 0; 488 487 } 489 488 ··· 494 499 495 500 /* EHCI spec section 4.1 */ 496 501 if ((retval = ehci_reset(ehci)) != 0) { 497 - unregister_reboot_notifier(&ehci->reboot_notifier); 498 502 ehci_mem_cleanup(ehci); 499 503 return retval; 500 504 }
+2
drivers/usb/host/ehci-pci.c
··· 338 338 .resume = ehci_pci_resume, 339 339 #endif 340 340 .stop = ehci_stop, 341 + .shutdown = ehci_shutdown, 341 342 342 343 /* 343 344 * managing i/o requests and associated device resources ··· 385 384 .suspend = usb_hcd_pci_suspend, 386 385 .resume = usb_hcd_pci_resume, 387 386 #endif 387 + .shutdown = usb_hcd_pci_shutdown, 388 388 };
-1
drivers/usb/host/ehci.h
··· 82 82 struct dma_pool *sitd_pool; /* sitd per split iso urb */ 83 83 84 84 struct timer_list watchdog; 85 - struct notifier_block reboot_notifier; 86 85 unsigned long actions; 87 86 unsigned stamp; 88 87 unsigned long next_statechange;
+2
drivers/usb/host/ohci-at91.c
··· 221 221 */ 222 222 .start = ohci_at91_start, 223 223 .stop = ohci_stop, 224 + .shutdown = ohci_shutdown, 224 225 225 226 /* 226 227 * managing i/o requests and associated device resources ··· 311 310 static struct platform_driver ohci_hcd_at91_driver = { 312 311 .probe = ohci_hcd_at91_drv_probe, 313 312 .remove = ohci_hcd_at91_drv_remove, 313 + .shutdown = usb_hcd_platform_shutdown, 314 314 .suspend = ohci_hcd_at91_drv_suspend, 315 315 .resume = ohci_hcd_at91_drv_resume, 316 316 .driver = {
+2
drivers/usb/host/ohci-au1xxx.c
··· 269 269 */ 270 270 .start = ohci_au1xxx_start, 271 271 .stop = ohci_stop, 272 + .shutdown = ohci_shutdown, 272 273 273 274 /* 274 275 * managing i/o requests and associated device resources ··· 336 335 static struct platform_driver ohci_hcd_au1xxx_driver = { 337 336 .probe = ohci_hcd_au1xxx_drv_probe, 338 337 .remove = ohci_hcd_au1xxx_drv_remove, 338 + .shutdown = usb_hcd_platform_shutdown, 339 339 /*.suspend = ohci_hcd_au1xxx_drv_suspend, */ 340 340 /*.resume = ohci_hcd_au1xxx_drv_resume, */ 341 341 .driver = {
+2
drivers/usb/host/ohci-ep93xx.c
··· 128 128 .flags = HCD_USB11 | HCD_MEMORY, 129 129 .start = ohci_ep93xx_start, 130 130 .stop = ohci_stop, 131 + .shutdown = ohci_shutdown, 131 132 .urb_enqueue = ohci_urb_enqueue, 132 133 .urb_dequeue = ohci_urb_dequeue, 133 134 .endpoint_disable = ohci_endpoint_disable, ··· 204 203 static struct platform_driver ohci_hcd_ep93xx_driver = { 205 204 .probe = ohci_hcd_ep93xx_drv_probe, 206 205 .remove = ohci_hcd_ep93xx_drv_remove, 206 + .shutdown = usb_hcd_platform_shutdown, 207 207 #ifdef CONFIG_PM 208 208 .suspend = ohci_hcd_ep93xx_drv_suspend, 209 209 .resume = ohci_hcd_ep93xx_drv_resume,
+4 -8
drivers/usb/host/ohci-hcd.c
··· 136 136 static void ohci_dump (struct ohci_hcd *ohci, int verbose); 137 137 static int ohci_init (struct ohci_hcd *ohci); 138 138 static void ohci_stop (struct usb_hcd *hcd); 139 - static int ohci_reboot (struct notifier_block *, unsigned long , void *); 140 139 141 140 #include "ohci-hub.c" 142 141 #include "ohci-dbg.c" ··· 418 419 ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); 419 420 } 420 421 421 - /* reboot notifier forcibly disables IRQs and DMA, helping kexec and 422 + /* ohci_shutdown forcibly disables IRQs and DMA, helping kexec and 422 423 * other cases where the next software may expect clean state from the 423 424 * "firmware". this is bus-neutral, unlike shutdown() methods. 424 425 */ 425 - static int 426 - ohci_reboot (struct notifier_block *block, unsigned long code, void *null) 426 + static void 427 + ohci_shutdown (struct usb_hcd *hcd) 427 428 { 428 429 struct ohci_hcd *ohci; 429 430 430 - ohci = container_of (block, struct ohci_hcd, reboot_notifier); 431 + ohci = hcd_to_ohci (hcd); 431 432 ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); 432 433 ohci_usb_reset (ohci); 433 434 /* flush the writes */ 434 435 (void) ohci_readl (ohci, &ohci->regs->control); 435 - return 0; 436 436 } 437 437 438 438 /*-------------------------------------------------------------------------* ··· 502 504 if ((ret = ohci_mem_init (ohci)) < 0) 503 505 ohci_stop (hcd); 504 506 else { 505 - register_reboot_notifier (&ohci->reboot_notifier); 506 507 create_debug_files (ohci); 507 508 } 508 509 ··· 797 800 ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); 798 801 799 802 remove_debug_files (ohci); 800 - unregister_reboot_notifier (&ohci->reboot_notifier); 801 803 ohci_mem_cleanup (ohci); 802 804 if (ohci->hcca) { 803 805 dma_free_coherent (hcd->self.controller,
+2
drivers/usb/host/ohci-lh7a404.c
··· 174 174 */ 175 175 .start = ohci_lh7a404_start, 176 176 .stop = ohci_stop, 177 + .shutdown = ohci_shutdown, 177 178 178 179 /* 179 180 * managing i/o requests and associated device resources ··· 242 241 static struct platform_driver ohci_hcd_lh7a404_driver = { 243 242 .probe = ohci_hcd_lh7a404_drv_probe, 244 243 .remove = ohci_hcd_lh7a404_drv_remove, 244 + .shutdown = usb_hcd_platform_shutdown, 245 245 /*.suspend = ohci_hcd_lh7a404_drv_suspend, */ 246 246 /*.resume = ohci_hcd_lh7a404_drv_resume, */ 247 247 .driver = {
-1
drivers/usb/host/ohci-mem.c
··· 28 28 ohci->next_statechange = jiffies; 29 29 spin_lock_init (&ohci->lock); 30 30 INIT_LIST_HEAD (&ohci->pending); 31 - ohci->reboot_notifier.notifier_call = ohci_reboot; 32 31 } 33 32 34 33 /*-------------------------------------------------------------------------*/
+2
drivers/usb/host/ohci-omap.c
··· 447 447 .reset = ohci_omap_init, 448 448 .start = ohci_omap_start, 449 449 .stop = ohci_omap_stop, 450 + .shutdown = ohci_shutdown, 450 451 451 452 /* 452 453 * managing i/o requests and associated device resources ··· 533 532 static struct platform_driver ohci_hcd_omap_driver = { 534 533 .probe = ohci_hcd_omap_drv_probe, 535 534 .remove = ohci_hcd_omap_drv_remove, 535 + .shutdown = usb_hcd_platform_shutdown, 536 536 #ifdef CONFIG_PM 537 537 .suspend = ohci_omap_suspend, 538 538 .resume = ohci_omap_resume,
+3
drivers/usb/host/ohci-pci.c
··· 177 177 .reset = ohci_pci_reset, 178 178 .start = ohci_pci_start, 179 179 .stop = ohci_stop, 180 + .shutdown = ohci_shutdown, 180 181 181 182 #ifdef CONFIG_PM 182 183 /* these suspend/resume entries are for upstream PCI glue ONLY */ ··· 233 232 .suspend = usb_hcd_pci_suspend, 234 233 .resume = usb_hcd_pci_resume, 235 234 #endif 235 + 236 + .shutdown = usb_hcd_pci_shutdown, 236 237 }; 237 238 238 239
+2
drivers/usb/host/ohci-ppc-soc.c
··· 148 148 */ 149 149 .start = ohci_ppc_soc_start, 150 150 .stop = ohci_stop, 151 + .shutdown = ohci_shutdown, 151 152 152 153 /* 153 154 * managing i/o requests and associated device resources ··· 197 196 static struct platform_driver ohci_hcd_ppc_soc_driver = { 198 197 .probe = ohci_hcd_ppc_soc_drv_probe, 199 198 .remove = ohci_hcd_ppc_soc_drv_remove, 199 + .shutdown = usb_hcd_platform_shutdown, 200 200 #ifdef CONFIG_PM 201 201 /*.suspend = ohci_hcd_ppc_soc_drv_suspend,*/ 202 202 /*.resume = ohci_hcd_ppc_soc_drv_resume,*/
+2
drivers/usb/host/ohci-pxa27x.c
··· 270 270 */ 271 271 .start = ohci_pxa27x_start, 272 272 .stop = ohci_stop, 273 + .shutdown = ohci_shutdown, 273 274 274 275 /* 275 276 * managing i/o requests and associated device resources ··· 359 358 static struct platform_driver ohci_hcd_pxa27x_driver = { 360 359 .probe = ohci_hcd_pxa27x_drv_probe, 361 360 .remove = ohci_hcd_pxa27x_drv_remove, 361 + .shutdown = usb_hcd_platform_shutdown, 362 362 #ifdef CONFIG_PM 363 363 .suspend = ohci_hcd_pxa27x_drv_suspend, 364 364 .resume = ohci_hcd_pxa27x_drv_resume,
+2
drivers/usb/host/ohci-s3c2410.c
··· 447 447 */ 448 448 .start = ohci_s3c2410_start, 449 449 .stop = ohci_stop, 450 + .shutdown = ohci_shutdown, 450 451 451 452 /* 452 453 * managing i/o requests and associated device resources ··· 492 491 static struct platform_driver ohci_hcd_s3c2410_driver = { 493 492 .probe = ohci_hcd_s3c2410_drv_probe, 494 493 .remove = ohci_hcd_s3c2410_drv_remove, 494 + .shutdown = usb_hcd_platform_shutdown, 495 495 /*.suspend = ohci_hcd_s3c2410_drv_suspend, */ 496 496 /*.resume = ohci_hcd_s3c2410_drv_resume, */ 497 497 .driver = {
-2
drivers/usb/host/ohci.h
··· 389 389 unsigned long next_statechange; /* suspend/resume */ 390 390 u32 fminterval; /* saved register */ 391 391 392 - struct notifier_block reboot_notifier; 393 - 394 392 unsigned long flags; /* for HC bugs */ 395 393 #define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ 396 394 #define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */