From 2bdbada7bd67bed564b828c8fe56530d0bd9a7ef Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Sun, 25 Jan 2009 14:08:07 -0200 Subject: em28xx: Fix for fail to submit URB with IRQs and Pre-emption Disabled From: Robert Krakora Trace: (Provided by Douglas) BUG: sleeping function called from invalid context at drivers/usb/core/urb.c:558 in_atomic():0, irqs_disabled():1 Pid: 4918, comm: sox Not tainted 2.6.27.5 #1 [] __might_sleep+0xc6/0xcb [] usb_kill_urb+0x1a/0xd8 [] ? __kmalloc+0x9b/0xfc [] ? __kmalloc+0xb8/0xfc [] ? usb_alloc_urb+0xf/0x31 [] em28xx_isoc_audio_deinit+0x2f/0x6c [em28xx_alsa] [] em28xx_cmd+0x1aa/0x1c5 [em28xx_alsa] [] snd_em28xx_capture_trigger+0x53/0x68 [em28xx_alsa] [] snd_pcm_do_start+0x1c/0x23 [snd_pcm] [] snd_pcm_action_single+0x25/0x4b [snd_pcm] [] snd_pcm_action+0x6a/0x76 [snd_pcm] [] snd_pcm_start+0x14/0x16 [snd_pcm] [] snd_pcm_lib_read1+0x66/0x273 [snd_pcm] [] ? snd_pcm_kernel_ioctl+0x46/0x5f [snd_pcm] [] snd_pcm_lib_read+0xbf/0xcd [snd_pcm] [] ? snd_pcm_lib_read_transfer+0x0/0xaf [snd_pcm] [] snd_pcm_oss_read3+0x99/0xdc [snd_pcm_oss] [] snd_pcm_oss_read2+0xa3/0xbf [snd_pcm_oss] [] ? _cond_resched+0x8/0x32 [] snd_pcm_oss_read+0x106/0x150 [snd_pcm_oss] [] ? snd_pcm_oss_read+0x0/0x150 [snd_pcm_oss] [] vfs_read+0x81/0xdc [] sys_read+0x3b/0x60 [] sysenter_do_call+0x12/0x34 ======================= The culprit in the trace is snd_pcm_action() which invokes a spin lock which disables pre-emption which disables an IRQ which causes the __might_sleep() function to fail the irqs_disabled() test. Since pre-emption is enabled then it is safe to de-allocate the memory if you first unlink each URB. In this instance you are safe since pre-emption is disabled. If pre-emption and irqs are not disabled then call usb_kill_urb(), else call usb_unlink_urb(). Thanks to Douglas for tracking down this bug originally!!! Priority: high Signed-off-by: Robert Krakora [dougsland@redhat.com: Fixed codyingstyle] Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/video/em28xx/em28xx-audio.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'linux/drivers/media/video/em28xx/em28xx-audio.c') diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index 62dab8696..c64cd42a8 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -63,12 +63,15 @@ static int em28xx_isoc_audio_deinit(struct em28xx *dev) dprintk("Stopping isoc\n"); for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { - usb_kill_urb(dev->adev.urb[i]); + if (!irqs_disabled()) + usb_kill_urb(dev->adev.urb[i]); + else + usb_unlink_urb(dev->adev.urb[i]); usb_free_urb(dev->adev.urb[i]); dev->adev.urb[i] = NULL; - kfree(dev->adev.transfer_buffer[i]); - dev->adev.transfer_buffer[i] = NULL; + kfree(dev->adev.transfer_buffer[i]); + dev->adev.transfer_buffer[i] = NULL; } return 0; -- cgit v1.2.3 From 1c4acbfba514ff97e939c530ec86a0c95ec8e656 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Sun, 25 Jan 2009 16:12:29 -0200 Subject: em28xx: fix spaces From: Douglas Schilling Landgraf Fixed spaces Priority: normal Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/video/em28xx/em28xx-audio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux/drivers/media/video/em28xx/em28xx-audio.c') diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index c64cd42a8..ebbc64b3a 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -470,9 +470,9 @@ static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream snd_pcm_uframes_t hwptr_done; dev = snd_pcm_substream_chip(substream); - spin_lock_irqsave(&dev->adev.slock, flags); + spin_lock_irqsave(&dev->adev.slock, flags); hwptr_done = dev->adev.hwptr_done_capture; - spin_unlock_irqrestore(&dev->adev.slock, flags); + spin_unlock_irqrestore(&dev->adev.slock, flags); return hwptr_done; } -- cgit v1.2.3 From 6af1615ac94a5752ea7bf0b3e02ebe2030a32f0e Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Sun, 25 Jan 2009 20:19:23 -0200 Subject: em28xx: Add check before call em28xx_isoc_audio_deinit() From: Douglas Schilling Landgraf Just call em28xx_isoc_audio_deinit() if em28xx sent a usb_submit(). Priority: normal Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/video/em28xx/em28xx-audio.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'linux/drivers/media/video/em28xx/em28xx-audio.c') diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index ebbc64b3a..fcf21a614 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -62,7 +62,7 @@ static int em28xx_isoc_audio_deinit(struct em28xx *dev) int i; dprintk("Stopping isoc\n"); - for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { if (!irqs_disabled()) usb_kill_urb(dev->adev.urb[i]); else @@ -74,6 +74,7 @@ static int em28xx_isoc_audio_deinit(struct em28xx *dev) dev->adev.transfer_buffer[i] = NULL; } + dev->isoc_ctl.num_bufs = 0; return 0; } @@ -178,6 +179,8 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) dprintk("Starting isoc transfers\n"); + dev->isoc_ctl.num_bufs = 0; + for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { struct urb *urb; int j, k; @@ -219,10 +222,19 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); if (errCode) { - em28xx_isoc_audio_deinit(dev); + if (dev->isoc_ctl.num_bufs == 0) { + usb_free_urb(dev->adev.urb[i]); + dev->adev.urb[i] = NULL; + kfree(dev->adev.transfer_buffer[i]); + dev->adev.transfer_buffer[i] = NULL; + } else + em28xx_isoc_audio_deinit(dev); return errCode; } + mutex_lock(&dev->lock); + dev->isoc_ctl.num_bufs++; + mutex_unlock(&dev->lock); } return 0; -- cgit v1.2.3