[PATCH 00/11] ALSA: Kill tasklet usage
Hi,
here is a patch set to convert the tasklet usage in sound tree with either the threaded irq or the dedicated work. It's applied after the tasket API conversion series, found in topic/tasklet-convert branch of sound git tree (which will be included in the next pull request for 5.9-rc4).
This contains only non-ASoC changes, the changes for ASoC will follow at next.
Takashi
===
Takashi Iwai (11): ALSA: pcsp: Replace tasklet with work ALSA: timer: Replace tasklet with work ALSA: usb-audio: Replace tasklet with work ALSA: ua101: Replace tasklet with work ALSA: aloop: Replace tasklet with work ALSA: hdsp: Replace tasklet with work ALSA: hdspm: Replace tasklet with work ALSA: riptide: Replace tasklet with threaded irq ALSA: asihpi: Replace tasklet with threaded irq ALSA: firewire: Replace tasklet with work ALSA: mixart: Correct comment wrt obsoleted tasklet usage
include/sound/timer.h | 8 +++--- sound/core/hrtimer.c | 2 +- sound/core/timer.c | 20 +++++++------- sound/drivers/aloop.c | 23 ++++++++-------- sound/drivers/pcsp/pcsp_lib.c | 12 ++++---- sound/firewire/amdtp-stream-trace.h | 2 +- sound/firewire/amdtp-stream.c | 25 +++++++++-------- sound/firewire/amdtp-stream.h | 2 +- sound/pci/asihpi/asihpi.c | 28 ++----------------- sound/pci/asihpi/hpioctl.c | 16 +++++++++-- sound/pci/mixart/mixart.h | 2 +- sound/pci/riptide/riptide.c | 20 ++++++++------ sound/pci/rme9652/hdsp.c | 55 ++++++++++++++++++------------------- sound/pci/rme9652/hdspm.c | 13 ++++----- sound/usb/midi.c | 13 +++++---- sound/usb/misc/ua101.c | 16 +++++------ 16 files changed, 122 insertions(+), 135 deletions(-)
The tasklet is an old API that should be deprecated, usually can be converted to another decent API. This patch replaces the usage of tasklet in pcsp driver with a simple work. In pcsp driver, a global tasklet is used for offloading the period-elapse handling in the hrtimer callback (introduced in commit 96c7d478efad "ALSA: pcsp - Fix locking messes in snd-pcsp"). It can be achieved gracefully with a work queued in the high-prio system workqueue.
This also changes tasklet_kill() with cancel_work_sync() in the sync_stop callback, which is anyway better to assure canceling the pending tasks.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/drivers/pcsp/pcsp_lib.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c index 4e79293d7f11..ed40d0f7432c 100644 --- a/sound/drivers/pcsp/pcsp_lib.c +++ b/sound/drivers/pcsp/pcsp_lib.c @@ -23,10 +23,10 @@ MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround " #define DMIX_WANTS_S16 1
/* - * Call snd_pcm_period_elapsed in a tasklet + * Call snd_pcm_period_elapsed in a work * This avoids spinlock messes and long-running irq contexts */ -static void pcsp_call_pcm_elapsed(unsigned long priv) +static void pcsp_call_pcm_elapsed(struct work_struct *work) { if (atomic_read(&pcsp_chip.timer_active)) { struct snd_pcm_substream *substream; @@ -36,7 +36,7 @@ static void pcsp_call_pcm_elapsed(unsigned long priv) } }
-static DECLARE_TASKLET_OLD(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed); +static DECLARE_WORK(pcsp_pcm_work, pcsp_call_pcm_elapsed);
/* write the port and returns the next expire time in ns; * called at the trigger-start and in hrtimer callback @@ -119,11 +119,9 @@ static void pcsp_pointer_update(struct snd_pcsp *chip) if (periods_elapsed) { chip->period_ptr += periods_elapsed * period_bytes; chip->period_ptr %= buffer_bytes; + queue_work(system_highpri_wq, &pcsp_pcm_work); } spin_unlock_irqrestore(&chip->substream_lock, flags); - - if (periods_elapsed) - tasklet_schedule(&pcsp_pcm_tasklet); }
enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) @@ -196,7 +194,7 @@ void pcsp_sync_stop(struct snd_pcsp *chip) pcsp_stop_playing(chip); local_irq_enable(); hrtimer_cancel(&chip->timer); - tasklet_kill(&pcsp_pcm_tasklet); + cancel_work_sync(&pcsp_pcm_work); }
static int snd_pcsp_playback_close(struct snd_pcm_substream *substream)
The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In ALSA core timer API, the callbacks can be offlined to a tasklet when a flag is set in the timer backend. It can be achieved gracefully with a work queued in the high-prio system workqueue.
This patch replaces the usage of tasklet in ALSA timer API with a simple work. Currently the tasklet feature is used only in the system timer and hrtimer backends, so both are patched to use the new flag name SNDRV_TIMER_HW_WORK, too.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/timer.h | 8 ++++---- sound/core/hrtimer.c | 2 +- sound/core/timer.c | 20 ++++++++++---------- 3 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/include/sound/timer.h b/include/sound/timer.h index 23e885d31525..760e132cc0cd 100644 --- a/include/sound/timer.h +++ b/include/sound/timer.h @@ -21,13 +21,13 @@ #define SNDRV_TIMER_HW_STOP 0x00000002 /* call stop before start */ #define SNDRV_TIMER_HW_SLAVE 0x00000004 /* only slave timer (variable resolution) */ #define SNDRV_TIMER_HW_FIRST 0x00000008 /* first tick can be incomplete */ -#define SNDRV_TIMER_HW_TASKLET 0x00000010 /* timer is called from tasklet */ +#define SNDRV_TIMER_HW_WORK 0x00000010 /* timer is called from work */
#define SNDRV_TIMER_IFLG_SLAVE 0x00000001 #define SNDRV_TIMER_IFLG_RUNNING 0x00000002 #define SNDRV_TIMER_IFLG_START 0x00000004 #define SNDRV_TIMER_IFLG_AUTO 0x00000008 /* auto restart */ -#define SNDRV_TIMER_IFLG_FAST 0x00000010 /* fast callback (do not use tasklet) */ +#define SNDRV_TIMER_IFLG_FAST 0x00000010 /* fast callback (do not use work) */ #define SNDRV_TIMER_IFLG_CALLBACK 0x00000020 /* timer callback is active */ #define SNDRV_TIMER_IFLG_EXCLUSIVE 0x00000040 /* exclusive owner - no more instances */ #define SNDRV_TIMER_IFLG_EARLY_EVENT 0x00000080 /* write early event to the poll queue */ @@ -74,7 +74,7 @@ struct snd_timer { struct list_head active_list_head; struct list_head ack_list_head; struct list_head sack_list_head; /* slow ack list head */ - struct tasklet_struct task_queue; + struct work_struct task_work; int max_instances; /* upper limit of timer instances */ int num_instances; /* current number of timer instances */ }; @@ -96,7 +96,7 @@ struct snd_timer_instance { unsigned long ticks; /* auto-load ticks when expired */ unsigned long cticks; /* current ticks */ unsigned long pticks; /* accumulated ticks for callback */ - unsigned long resolution; /* current resolution for tasklet */ + unsigned long resolution; /* current resolution for work */ unsigned long lost; /* lost ticks */ int slave_class; unsigned int slave_id; diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index c61ba52a530a..e97ff8cccb64 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -114,7 +114,7 @@ static int snd_hrtimer_stop(struct snd_timer *t) }
static const struct snd_timer_hardware hrtimer_hw __initconst = { - .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_TASKLET, + .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_WORK, .open = snd_hrtimer_open, .close = snd_hrtimer_close, .start = snd_hrtimer_start, diff --git a/sound/core/timer.c b/sound/core/timer.c index 6e27d87b18ed..4e74aa3d9e1f 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -813,12 +813,12 @@ static void snd_timer_clear_callbacks(struct snd_timer *timer, }
/* - * timer tasklet + * timer work * */ -static void snd_timer_tasklet(struct tasklet_struct *t) +static void snd_timer_work(struct work_struct *work) { - struct snd_timer *timer = from_tasklet(timer, t, task_queue); + struct snd_timer *timer = container_of(work, struct snd_timer, task_work); unsigned long flags;
if (timer->card && timer->card->shutdown) { @@ -843,7 +843,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) unsigned long resolution; struct list_head *ack_list_head; unsigned long flags; - int use_tasklet = 0; + bool use_work = false;
if (timer == NULL) return; @@ -884,7 +884,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) --timer->running; list_del_init(&ti->active_list); } - if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || + if ((timer->hw.flags & SNDRV_TIMER_HW_WORK) || (ti->flags & SNDRV_TIMER_IFLG_FAST)) ack_list_head = &timer->ack_list_head; else @@ -919,11 +919,11 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) snd_timer_process_callbacks(timer, &timer->ack_list_head);
/* do we have any slow callbacks? */ - use_tasklet = !list_empty(&timer->sack_list_head); + use_work = !list_empty(&timer->sack_list_head); spin_unlock_irqrestore(&timer->lock, flags);
- if (use_tasklet) - tasklet_schedule(&timer->task_queue); + if (use_work) + queue_work(system_highpri_wq, &timer->task_work); } EXPORT_SYMBOL(snd_timer_interrupt);
@@ -967,7 +967,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, INIT_LIST_HEAD(&timer->ack_list_head); INIT_LIST_HEAD(&timer->sack_list_head); spin_lock_init(&timer->lock); - tasklet_setup(&timer->task_queue, snd_timer_tasklet); + INIT_WORK(&timer->task_work, snd_timer_work); timer->max_instances = 1000; /* default limit per timer */ if (card != NULL) { timer->module = card->module; @@ -1200,7 +1200,7 @@ static int snd_timer_s_close(struct snd_timer *timer)
static const struct snd_timer_hardware snd_timer_system = { - .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET, + .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_WORK, .resolution = 1000000000L / HZ, .ticks = 10000000L, .close = snd_timer_s_close,
The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In USB-audio driver, a tasklet is still used in MIDI interface code for handling the output byte stream. It can be achieved gracefully with a work queued in the high-prio system workqueue.
This patch replaces the tasklet usage in USB-audio driver with a simple work.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/midi.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/sound/usb/midi.c b/sound/usb/midi.c index e8287a05e36b..c8213652470c 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -142,7 +142,7 @@ struct snd_usb_midi_out_endpoint { unsigned int active_urbs; unsigned int drain_urbs; int max_transfer; /* size of urb buffer */ - struct tasklet_struct tasklet; + struct work_struct work; unsigned int next_urb; spinlock_t buffer_lock;
@@ -344,9 +344,10 @@ static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint *ep) spin_unlock_irqrestore(&ep->buffer_lock, flags); }
-static void snd_usbmidi_out_tasklet(struct tasklet_struct *t) +static void snd_usbmidi_out_work(struct work_struct *work) { - struct snd_usb_midi_out_endpoint *ep = from_tasklet(ep, t, tasklet); + struct snd_usb_midi_out_endpoint *ep = + container_of(work, struct snd_usb_midi_out_endpoint, work);
snd_usbmidi_do_output(ep); } @@ -1177,7 +1178,7 @@ static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream, snd_rawmidi_proceed(substream); return; } - tasklet_schedule(&port->ep->tasklet); + queue_work(system_highpri_wq, &port->ep->work); } }
@@ -1440,7 +1441,7 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi *umidi, }
spin_lock_init(&ep->buffer_lock); - tasklet_setup(&ep->tasklet, snd_usbmidi_out_tasklet); + INIT_WORK(&ep->work, snd_usbmidi_out_work); init_waitqueue_head(&ep->drain_wait);
for (i = 0; i < 0x10; ++i) @@ -1503,7 +1504,7 @@ void snd_usbmidi_disconnect(struct list_head *p) for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { struct snd_usb_midi_endpoint *ep = &umidi->endpoints[i]; if (ep->out) - tasklet_kill(&ep->out->tasklet); + cancel_work_sync(&ep->out->work); if (ep->out) { for (j = 0; j < OUTPUT_URBS; ++j) usb_kill_urb(ep->out->urbs[j].urb);
The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In UA101 driver, a tasklet is still used for handling the output URBs. It can be achieved gracefully with a work queued in the high-prio system workqueue, too.
This patch replaces the tasklet usage in UA101 driver with a simple work.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/misc/ua101.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 3b2dce1043f5..6b30155964ec 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -96,7 +96,7 @@ struct ua101 { u8 rate_feedback[MAX_QUEUE_LENGTH];
struct list_head ready_playback_urbs; - struct tasklet_struct playback_tasklet; + struct work_struct playback_work; wait_queue_head_t alsa_capture_wait; wait_queue_head_t rate_feedback_wait; wait_queue_head_t alsa_playback_wait; @@ -188,7 +188,7 @@ static void playback_urb_complete(struct urb *usb_urb) spin_lock_irqsave(&ua->lock, flags); list_add_tail(&urb->ready_list, &ua->ready_playback_urbs); if (ua->rate_feedback_count > 0) - tasklet_schedule(&ua->playback_tasklet); + queue_work(system_highpri_wq, &ua->playback_work); ua->playback.substream->runtime->delay -= urb->urb.iso_frame_desc[0].length / ua->playback.frame_bytes; @@ -247,9 +247,9 @@ static inline void add_with_wraparound(struct ua101 *ua, *value -= ua->playback.queue_length; }
-static void playback_tasklet(struct tasklet_struct *t) +static void playback_work(struct work_struct *work) { - struct ua101 *ua = from_tasklet(ua, t, playback_tasklet); + struct ua101 *ua = container_of(work, struct ua101, playback_work); unsigned long flags; unsigned int frames; struct ua101_urb *urb; @@ -401,7 +401,7 @@ static void capture_urb_complete(struct urb *urb) } if (test_bit(USB_PLAYBACK_RUNNING, &ua->states) && !list_empty(&ua->ready_playback_urbs)) - tasklet_schedule(&ua->playback_tasklet); + queue_work(system_highpri_wq, &ua->playback_work); }
spin_unlock_irqrestore(&ua->lock, flags); @@ -532,7 +532,7 @@ static void stop_usb_playback(struct ua101 *ua)
kill_stream_urbs(&ua->playback);
- tasklet_kill(&ua->playback_tasklet); + cancel_work_sync(&ua->playback_work);
disable_iso_interface(ua, INTF_PLAYBACK); } @@ -550,7 +550,7 @@ static int start_usb_playback(struct ua101 *ua) return 0;
kill_stream_urbs(&ua->playback); - tasklet_kill(&ua->playback_tasklet); + cancel_work_sync(&ua->playback_work);
err = enable_iso_interface(ua, INTF_PLAYBACK); if (err < 0) @@ -1218,7 +1218,7 @@ static int ua101_probe(struct usb_interface *interface, spin_lock_init(&ua->lock); mutex_init(&ua->mutex); INIT_LIST_HEAD(&ua->ready_playback_urbs); - tasklet_setup(&ua->playback_tasklet, playback_tasklet); + INIT_WORK(&ua->playback_work, playback_work); init_waitqueue_head(&ua->alsa_capture_wait); init_waitqueue_head(&ua->rate_feedback_wait); init_waitqueue_head(&ua->alsa_playback_wait);
The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In aloop driver, a tasklet is still used for offloading the timer event task. It can be achieved gracefully with a work queued, too.
This patch replaces the tasklet usage in aloop driver with a simple work.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/drivers/aloop.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 251eaf1152e2..c91356326699 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -110,7 +110,7 @@ struct loopback_cable { struct { int stream; struct snd_timer_id id; - struct tasklet_struct event_tasklet; + struct work_struct event_work; struct snd_timer_instance *instance; } snd_timer; }; @@ -309,8 +309,8 @@ static int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm) */ snd_timer_close(cable->snd_timer.instance);
- /* wait till drain tasklet has finished if requested */ - tasklet_kill(&cable->snd_timer.event_tasklet); + /* wait till drain work has finished if requested */ + cancel_work_sync(&cable->snd_timer.event_work);
snd_timer_instance_free(cable->snd_timer.instance); memset(&cable->snd_timer, 0, sizeof(cable->snd_timer)); @@ -794,11 +794,11 @@ static void loopback_snd_timer_function(struct snd_timer_instance *timeri, resolution); }
-static void loopback_snd_timer_tasklet(unsigned long arg) +static void loopback_snd_timer_work(struct work_struct *work) { - struct snd_timer_instance *timeri = (struct snd_timer_instance *)arg; - struct loopback_cable *cable = timeri->callback_data; + struct loopback_cable *cable;
+ cable = container_of(work, struct loopback_cable, snd_timer.event_work); loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_MSTOP, 0); }
@@ -828,9 +828,9 @@ static void loopback_snd_timer_event(struct snd_timer_instance *timeri, * state the streaming will be aborted by the usual timeout. It * should not be aborted here because may be the timer sound * card does only a recovery and the timer is back soon. - * This tasklet triggers loopback_snd_timer_tasklet() + * This work triggers loopback_snd_timer_work() */ - tasklet_schedule(&cable->snd_timer.event_tasklet); + schedule_work(&cable->snd_timer.event_work); } }
@@ -1124,7 +1124,7 @@ static int loopback_snd_timer_open(struct loopback_pcm *dpcm) err = -ENOMEM; goto exit; } - /* The callback has to be called from another tasklet. If + /* The callback has to be called from another work. If * SNDRV_TIMER_IFLG_FAST is specified it will be called from the * snd_pcm_period_elapsed() call of the selected sound card. * snd_pcm_period_elapsed() helds snd_pcm_stream_lock_irqsave(). @@ -1137,9 +1137,8 @@ static int loopback_snd_timer_open(struct loopback_pcm *dpcm) timeri->callback_data = (void *)cable; timeri->ccallback = loopback_snd_timer_event;
- /* initialise a tasklet used for draining */ - tasklet_init(&cable->snd_timer.event_tasklet, - loopback_snd_timer_tasklet, (unsigned long)timeri); + /* initialise a work used for draining */ + INIT_WORK(&cable->snd_timer.event_work, loopback_snd_timer_work);
/* The mutex loopback->cable_lock is kept locked. * Therefore snd_timer_open() cannot be called a second time
The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In HDSP driver, a tasklet is still used for offloading the MIDI I/O handling (optional via mixer switch). It can be achieved gracefully with a work queued, too.
This patch replaces the tasklet usage in HDSP driver with a simple work. The conversion is fairly straightforward. The only significant difference is that a superfluous tasklet_kill() call is removed from snd_hdap_midi_input_trigger().
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/rme9652/hdsp.c | 55 ++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 28 deletions(-)
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index dda56ecfd33b..cea53a878c36 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -447,8 +447,8 @@ struct hdsp { struct snd_pcm_substream *capture_substream; struct snd_pcm_substream *playback_substream; struct hdsp_midi midi[2]; - struct tasklet_struct midi_tasklet; - int use_midi_tasklet; + struct work_struct midi_work; + int use_midi_work; int precise_ptr; u32 control_register; /* cached value */ u32 control2_register; /* cached value */ @@ -1385,7 +1385,6 @@ static void snd_hdsp_midi_input_trigger(struct snd_rawmidi_substream *substream, } } else { hdsp->control_register &= ~ie; - tasklet_kill(&hdsp->midi_tasklet); }
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); @@ -2542,37 +2541,37 @@ static int snd_hdsp_put_precise_pointer(struct snd_kcontrol *kcontrol, struct sn return change; }
-#define HDSP_USE_MIDI_TASKLET(xname, xindex) \ +#define HDSP_USE_MIDI_WORK(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_CARD, \ .name = xname, \ .index = xindex, \ - .info = snd_hdsp_info_use_midi_tasklet, \ - .get = snd_hdsp_get_use_midi_tasklet, \ - .put = snd_hdsp_put_use_midi_tasklet \ + .info = snd_hdsp_info_use_midi_work, \ + .get = snd_hdsp_get_use_midi_work, \ + .put = snd_hdsp_put_use_midi_work \ }
-static int hdsp_set_use_midi_tasklet(struct hdsp *hdsp, int use_tasklet) +static int hdsp_set_use_midi_work(struct hdsp *hdsp, int use_work) { - if (use_tasklet) - hdsp->use_midi_tasklet = 1; + if (use_work) + hdsp->use_midi_work = 1; else - hdsp->use_midi_tasklet = 0; + hdsp->use_midi_work = 0; return 0; }
-#define snd_hdsp_info_use_midi_tasklet snd_ctl_boolean_mono_info +#define snd_hdsp_info_use_midi_work snd_ctl_boolean_mono_info
-static int snd_hdsp_get_use_midi_tasklet(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int snd_hdsp_get_use_midi_work(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
spin_lock_irq(&hdsp->lock); - ucontrol->value.integer.value[0] = hdsp->use_midi_tasklet; + ucontrol->value.integer.value[0] = hdsp->use_midi_work; spin_unlock_irq(&hdsp->lock); return 0; }
-static int snd_hdsp_put_use_midi_tasklet(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int snd_hdsp_put_use_midi_work(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); int change; @@ -2582,8 +2581,8 @@ static int snd_hdsp_put_use_midi_tasklet(struct snd_kcontrol *kcontrol, struct s return -EBUSY; val = ucontrol->value.integer.value[0] & 1; spin_lock_irq(&hdsp->lock); - change = (int)val != hdsp->use_midi_tasklet; - hdsp_set_use_midi_tasklet(hdsp, val); + change = (int)val != hdsp->use_midi_work; + hdsp_set_use_midi_work(hdsp, val); spin_unlock_irq(&hdsp->lock); return change; } @@ -2950,7 +2949,7 @@ HDSP_SPDIF_SYNC_CHECK("SPDIF Lock Status", 0), HDSP_ADATSYNC_SYNC_CHECK("ADAT Sync Lock Status", 0), HDSP_TOGGLE_SETTING("Line Out", HDSP_LineOut), HDSP_PRECISE_POINTER("Precise Pointer", 0), -HDSP_USE_MIDI_TASKLET("Use Midi Tasklet", 0), +HDSP_USE_MIDI_WORK("Use Midi Tasklet", 0), };
@@ -3370,7 +3369,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0)); snd_iprintf(buffer, "MIDI2 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut1)); snd_iprintf(buffer, "MIDI2 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn1)); - snd_iprintf(buffer, "Use Midi Tasklet: %s\n", hdsp->use_midi_tasklet ? "on" : "off"); + snd_iprintf(buffer, "Use Midi Tasklet: %s\n", hdsp->use_midi_work ? "on" : "off");
snd_iprintf(buffer, "\n");
@@ -3791,9 +3790,9 @@ static int snd_hdsp_set_defaults(struct hdsp *hdsp) return 0; }
-static void hdsp_midi_tasklet(struct tasklet_struct *t) +static void hdsp_midi_work(struct work_struct *work) { - struct hdsp *hdsp = from_tasklet(hdsp, t, midi_tasklet); + struct hdsp *hdsp = container_of(work, struct hdsp, midi_work);
if (hdsp->midi[0].pending) snd_hdsp_midi_input_read (&hdsp->midi[0]); @@ -3838,7 +3837,7 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id) }
if (midi0 && midi0status) { - if (hdsp->use_midi_tasklet) { + if (hdsp->use_midi_work) { /* we disable interrupts for this input until processing is done */ hdsp->control_register &= ~HDSP_Midi0InterruptEnable; hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); @@ -3849,7 +3848,7 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id) } } if (hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632 && midi1 && midi1status) { - if (hdsp->use_midi_tasklet) { + if (hdsp->use_midi_work) { /* we disable interrupts for this input until processing is done */ hdsp->control_register &= ~HDSP_Midi1InterruptEnable; hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); @@ -3859,8 +3858,8 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id) snd_hdsp_midi_input_read (&hdsp->midi[1]); } } - if (hdsp->use_midi_tasklet && schedule) - tasklet_schedule(&hdsp->midi_tasklet); + if (hdsp->use_midi_work && schedule) + queue_work(system_highpri_wq, &hdsp->midi_work); return IRQ_HANDLED; }
@@ -5182,7 +5181,7 @@ static int snd_hdsp_create(struct snd_card *card,
spin_lock_init(&hdsp->lock);
- tasklet_setup(&hdsp->midi_tasklet, hdsp_midi_tasklet); + INIT_WORK(&hdsp->midi_work, hdsp_midi_work);
pci_read_config_word(hdsp->pci, PCI_CLASS_REVISION, &hdsp->firmware_rev); hdsp->firmware_rev &= 0xff; @@ -5235,7 +5234,7 @@ static int snd_hdsp_create(struct snd_card *card, hdsp->irq = pci->irq; card->sync_irq = hdsp->irq; hdsp->precise_ptr = 0; - hdsp->use_midi_tasklet = 1; + hdsp->use_midi_work = 1; hdsp->dds_value = 0;
if ((err = snd_hdsp_initialize_memory(hdsp)) < 0) @@ -5305,7 +5304,7 @@ static int snd_hdsp_free(struct hdsp *hdsp) { if (hdsp->port) { /* stop the audio, and cancel all interrupts */ - tasklet_kill(&hdsp->midi_tasklet); + cancel_work_sync(&hdsp->midi_work); hdsp->control_register &= ~(HDSP_Start|HDSP_AudioInterruptEnable|HDSP_Midi0InterruptEnable|HDSP_Midi1InterruptEnable); hdsp_write (hdsp, HDSP_controlRegister, hdsp->control_register); }
The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In HDSP-MADI driver, a tasklet is still used for offloading the MIDI I/O handling (optional via mixer switch). It can be achieved gracefully with a work queued, too.
This patch replaces the tasklet usage in HDSP-MADI driver with a simple work. The conversion is fairly straightforward. The only significant difference is that the work initialization is moved to the right place in snd_hdspm_create() and cancel_work_sync() is always called in snd_hdspm_free() to assure killing the pending works.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/rme9652/hdspm.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 572350aaf18d..e532312a5e1c 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -997,7 +997,7 @@ struct hdspm { u32 settings_register; /* cached value for AIO / RayDat (sync reference, master/slave) */
struct hdspm_midi midi[4]; - struct tasklet_struct midi_tasklet; + struct work_struct midi_work;
size_t period_bytes; unsigned char ss_in_channels; @@ -2169,9 +2169,9 @@ static int snd_hdspm_create_midi(struct snd_card *card, }
-static void hdspm_midi_tasklet(struct tasklet_struct *t) +static void hdspm_midi_work(struct work_struct *work) { - struct hdspm *hdspm = from_tasklet(hdspm, t, midi_tasklet); + struct hdspm *hdspm = container_of(work, struct hdspm, midi_work); int i = 0;
while (i < hdspm->midiPorts) { @@ -5449,7 +5449,7 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) }
if (schedule) - tasklet_hi_schedule(&hdspm->midi_tasklet); + queue_work(system_highpri_wq, &hdspm->midi_work); }
return IRQ_HANDLED; @@ -6538,6 +6538,7 @@ static int snd_hdspm_create(struct snd_card *card, hdspm->card = card;
spin_lock_init(&hdspm->lock); + INIT_WORK(&hdspm->midi_work, hdspm_midi_work);
pci_read_config_word(hdspm->pci, PCI_CLASS_REVISION, &hdspm->firmware_rev); @@ -6836,9 +6837,6 @@ static int snd_hdspm_create(struct snd_card *card,
}
- tasklet_setup(&hdspm->midi_tasklet, hdspm_midi_tasklet); - - if (hdspm->io_type != MADIface) { hdspm->serial = (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF; @@ -6873,6 +6871,7 @@ static int snd_hdspm_free(struct hdspm * hdspm) {
if (hdspm->port) { + cancel_work_sync(&hdspm->midi_work);
/* stop th audio, and cancel all interrupts */ hdspm->control_register &=
The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In Riptide driver, a tasklet is still used for offloading the PCM IRQ handling. It can be achieved gracefully with a threaded IRQ, too.
This patch replaces the tasklet usage in riptide driver with a threaded IRQ.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/riptide/riptide.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 098c69b3b7aa..fcc2073c5025 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -445,7 +445,6 @@ struct snd_riptide { union firmware_version firmware;
spinlock_t lock; - struct tasklet_struct riptide_tq; struct snd_info_entry *proc_entry;
unsigned long received_irqs; @@ -1070,9 +1069,9 @@ getmixer(struct cmdif *cif, short num, unsigned short *rval, return 0; }
-static void riptide_handleirq(struct tasklet_struct *t) +static irqreturn_t riptide_handleirq(int irq, void *dev_id) { - struct snd_riptide *chip = from_tasklet(chip, t, riptide_tq); + struct snd_riptide *chip = dev_id; struct cmdif *cif = chip->cif; struct snd_pcm_substream *substream[PLAYBACK_SUBSTREAMS + 1]; struct snd_pcm_runtime *runtime; @@ -1083,7 +1082,7 @@ static void riptide_handleirq(struct tasklet_struct *t) unsigned int flag;
if (!cif) - return; + return IRQ_HANDLED;
for (i = 0; i < PLAYBACK_SUBSTREAMS; i++) substream[i] = chip->playback_substream[i]; @@ -1134,6 +1133,8 @@ static void riptide_handleirq(struct tasklet_struct *t) } } } + + return IRQ_HANDLED; }
#ifdef CONFIG_PM_SLEEP @@ -1699,13 +1700,14 @@ snd_riptide_interrupt(int irq, void *dev_id) { struct snd_riptide *chip = dev_id; struct cmdif *cif = chip->cif; + irqreturn_t ret = IRQ_HANDLED;
if (cif) { chip->received_irqs++; if (IS_EOBIRQ(cif->hwport) || IS_EOSIRQ(cif->hwport) || IS_EOCIRQ(cif->hwport)) { chip->handled_irqs++; - tasklet_schedule(&chip->riptide_tq); + ret = IRQ_WAKE_THREAD; } if (chip->rmidi && IS_MPUIRQ(cif->hwport)) { chip->handled_irqs++; @@ -1714,7 +1716,7 @@ snd_riptide_interrupt(int irq, void *dev_id) } SET_AIACK(cif->hwport); } - return IRQ_HANDLED; + return ret; }
static void @@ -1843,7 +1845,6 @@ snd_riptide_create(struct snd_card *card, struct pci_dev *pci, chip->received_irqs = 0; chip->handled_irqs = 0; chip->cif = NULL; - tasklet_setup(&chip->riptide_tq, riptide_handleirq);
if ((chip->res_port = request_region(chip->port, 64, "RIPTIDE")) == NULL) { @@ -1856,8 +1857,9 @@ snd_riptide_create(struct snd_card *card, struct pci_dev *pci, hwport = (struct riptideport *)chip->port; UNSET_AIE(hwport);
- if (request_irq(pci->irq, snd_riptide_interrupt, IRQF_SHARED, - KBUILD_MODNAME, chip)) { + if (request_threaded_irq(pci->irq, snd_riptide_interrupt, + riptide_handleirq, IRQF_SHARED, + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "Riptide: unable to grab IRQ %d\n", pci->irq); snd_riptide_free(chip);
The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In ASIHPI driver, a tasklet is still used for offloading the PCM IRQ handling. It can be achieved gracefully with a threaded IRQ, too.
This patch replaces the tasklet usage in asihpi driver with a threaded IRQ. It also simplified some call patterns.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/asihpi/asihpi.c | 28 +++------------------------- sound/pci/asihpi/hpioctl.c | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 28 deletions(-)
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 35e76480306e..46d8166ceaeb 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -117,7 +117,6 @@ struct snd_card_asihpi { * snd_card_asihpi_timer_function(). */ struct snd_card_asihpi_pcm *llmode_streampriv; - struct tasklet_struct t; void (*pcm_start)(struct snd_pcm_substream *substream); void (*pcm_stop)(struct snd_pcm_substream *substream);
@@ -547,9 +546,7 @@ static void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream) card = snd_pcm_substream_chip(substream);
WARN_ON(in_interrupt()); - tasklet_disable(&card->t); card->llmode_streampriv = dpcm; - tasklet_enable(&card->t);
hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index, HPI_ADAPTER_PROPERTY_IRQ_RATE, @@ -565,13 +562,7 @@ static void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream) hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index, HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0));
- if (in_interrupt()) - card->llmode_streampriv = NULL; - else { - tasklet_disable(&card->t); - card->llmode_streampriv = NULL; - tasklet_enable(&card->t); - } + card->llmode_streampriv = NULL; }
static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, @@ -921,10 +912,9 @@ static void snd_card_asihpi_timer_function(struct timer_list *t) add_timer(&dpcm->timer); }
-static void snd_card_asihpi_int_task(struct tasklet_struct *t) +static void snd_card_asihpi_isr(struct hpi_adapter *a) { - struct snd_card_asihpi *asihpi = from_tasklet(asihpi, t, t); - struct hpi_adapter *a = asihpi->hpi; + struct snd_card_asihpi *asihpi;
WARN_ON(!a || !a->snd_card || !a->snd_card->private_data); asihpi = (struct snd_card_asihpi *)a->snd_card->private_data; @@ -933,15 +923,6 @@ static void snd_card_asihpi_int_task(struct tasklet_struct *t) &asihpi->llmode_streampriv->timer); }
-static void snd_card_asihpi_isr(struct hpi_adapter *a) -{ - struct snd_card_asihpi *asihpi; - - WARN_ON(!a || !a->snd_card || !a->snd_card->private_data); - asihpi = (struct snd_card_asihpi *)a->snd_card->private_data; - tasklet_schedule(&asihpi->t); -} - /***************************** PLAYBACK OPS ****************/ static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream * substream) @@ -2871,7 +2852,6 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, if (hpi->interrupt_mode) { asihpi->pcm_start = snd_card_asihpi_pcm_int_start; asihpi->pcm_stop = snd_card_asihpi_pcm_int_stop; - tasklet_setup(&asihpi->t, snd_card_asihpi_int_task); hpi->interrupt_callback = snd_card_asihpi_isr; } else { asihpi->pcm_start = snd_card_asihpi_pcm_timer_start; @@ -2960,14 +2940,12 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, static void snd_asihpi_remove(struct pci_dev *pci_dev) { struct hpi_adapter *hpi = pci_get_drvdata(pci_dev); - struct snd_card_asihpi *asihpi = hpi->snd_card->private_data;
/* Stop interrupts */ if (hpi->interrupt_mode) { hpi->interrupt_callback = NULL; hpi_handle_error(hpi_adapter_set_property(hpi->adapter->index, HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0)); - tasklet_kill(&asihpi->t); }
snd_card_free(hpi->snd_card); diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 496dcde9715d..6cc2b6964bb5 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -329,11 +329,20 @@ static irqreturn_t asihpi_isr(int irq, void *dev_id) asihpi_irq_count, a->adapter->type, a->adapter->index); */
if (a->interrupt_callback) - a->interrupt_callback(a); + return IRQ_WAKE_THREAD;
return IRQ_HANDLED; }
+static irqreturn_t asihpi_isr_thread(int irq, void *dev_id) +{ + struct hpi_adapter *a = dev_id; + + if (a->interrupt_callback) + a->interrupt_callback(a); + return IRQ_HANDLED; +} + int asihpi_adapter_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { @@ -478,8 +487,9 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, }
/* Note: request_irq calls asihpi_isr here */ - if (request_irq(pci_dev->irq, asihpi_isr, IRQF_SHARED, - "asihpi", &adapters[adapter_index])) { + if (request_threaded_irq(pci_dev->irq, asihpi_isr, + asihpi_isr_thread, IRQF_SHARED, + "asihpi", &adapters[adapter_index])) { dev_err(&pci_dev->dev, "request_irq(%d) failed\n", pci_dev->irq); goto err;
The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In FireWire driver, a tasklet is still used for offloading the AMDTP PCM stream handling. It can be achieved gracefully with a work queued, too.
This patch replaces the tasklet usage in firewire-lib driver with a simple work. The conversion is fairly straightforward but for the in_interrupt() checks that are replaced with the check using the current_work().
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/firewire/amdtp-stream-trace.h | 2 +- sound/firewire/amdtp-stream.c | 25 +++++++++++++------------ sound/firewire/amdtp-stream.h | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h index 26e7cb555d3c..5386d548cada 100644 --- a/sound/firewire/amdtp-stream-trace.h +++ b/sound/firewire/amdtp-stream-trace.h @@ -49,7 +49,7 @@ TRACE_EVENT(amdtp_packet, __entry->data_blocks = data_blocks; __entry->data_block_counter = data_block_counter, __entry->packet_index = s->packet_index; - __entry->irq = !!in_interrupt(); + __entry->irq = (current_work() == &s->period_work); __entry->index = index; ), TP_printk( diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index ee1c428b1fd3..4e2f2bb7879f 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -64,7 +64,7 @@ #define IT_PKT_HEADER_SIZE_CIP 8 // For 2 CIP header. #define IT_PKT_HEADER_SIZE_NO_CIP 0 // Nothing.
-static void pcm_period_tasklet(struct tasklet_struct *t); +static void pcm_period_work(struct work_struct *work);
/** * amdtp_stream_init - initialize an AMDTP stream structure @@ -94,7 +94,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->flags = flags; s->context = ERR_PTR(-1); mutex_init(&s->mutex); - tasklet_setup(&s->period_tasklet, pcm_period_tasklet); + INIT_WORK(&s->period_work, pcm_period_work); s->packet_index = 0;
init_waitqueue_head(&s->callback_wait); @@ -203,7 +203,7 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
// Linux driver for 1394 OHCI controller voluntarily flushes isoc // context when total size of accumulated context header reaches - // PAGE_SIZE. This kicks tasklet for the isoc context and brings + // PAGE_SIZE. This kicks work for the isoc context and brings // callback in the middle of scheduled interrupts. // Although AMDTP streams in the same domain use the same events per // IRQ, use the largest size of context header between IT/IR contexts. @@ -333,7 +333,7 @@ EXPORT_SYMBOL(amdtp_stream_get_max_payload); */ void amdtp_stream_pcm_prepare(struct amdtp_stream *s) { - tasklet_kill(&s->period_tasklet); + cancel_work_sync(&s->period_work); s->pcm_buffer_pointer = 0; s->pcm_period_pointer = 0; } @@ -437,13 +437,14 @@ static void update_pcm_pointers(struct amdtp_stream *s, s->pcm_period_pointer += frames; if (s->pcm_period_pointer >= pcm->runtime->period_size) { s->pcm_period_pointer -= pcm->runtime->period_size; - tasklet_hi_schedule(&s->period_tasklet); + queue_work(system_highpri_wq, &s->period_work); } }
-static void pcm_period_tasklet(struct tasklet_struct *t) +static void pcm_period_work(struct work_struct *work) { - struct amdtp_stream *s = from_tasklet(s, t, period_tasklet); + struct amdtp_stream *s = container_of(work, struct amdtp_stream, + period_work); struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
if (pcm) @@ -794,7 +795,7 @@ static void generate_pkt_descs(struct amdtp_stream *s, struct pkt_desc *descs, static inline void cancel_stream(struct amdtp_stream *s) { s->packet_index = -1; - if (in_interrupt()) + if (current_work() == &s->period_work) amdtp_stream_pcm_abort(s); WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN); } @@ -1184,7 +1185,7 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
if (irq_target && amdtp_stream_running(irq_target)) { // This function is called in software IRQ context of - // period_tasklet or process context. + // period_work or process context. // // When the software IRQ context was scheduled by software IRQ // context of IT contexts, queued packets were already handled. @@ -1195,9 +1196,9 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, // immediately to keep better granularity of PCM pointer. // // Later, the process context will sometimes schedules software - // IRQ context of the period_tasklet. Then, no need to flush the + // IRQ context of the period_work. Then, no need to flush the // queue by the same reason as described in the above - if (!in_interrupt()) { + if (current_work() != &s->period_work) { // Queued packet should be processed without any kernel // preemption to keep latency against bus cycle. preempt_disable(); @@ -1263,7 +1264,7 @@ static void amdtp_stream_stop(struct amdtp_stream *s) return; }
- tasklet_kill(&s->period_tasklet); + cancel_work_sync(&s->period_work); fw_iso_context_stop(s->context); fw_iso_context_destroy(s->context); s->context = ERR_PTR(-1); diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 703b710aaf7f..2ceb57d1d58e 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -163,7 +163,7 @@ struct amdtp_stream {
/* For a PCM substream processing. */ struct snd_pcm_substream *pcm; - struct tasklet_struct period_tasklet; + struct work_struct period_work; snd_pcm_uframes_t pcm_buffer_pointer; unsigned int pcm_period_pointer;
Hi,
On Thu, Sep 03, 2020 at 12:41:30PM +0200, Takashi Iwai wrote:
The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In FireWire driver, a tasklet is still used for offloading the AMDTP PCM stream handling. It can be achieved gracefully with a work queued, too.
This patch replaces the tasklet usage in firewire-lib driver with a simple work. The conversion is fairly straightforward but for the in_interrupt() checks that are replaced with the check using the current_work().
Signed-off-by: Takashi Iwai tiwai@suse.de
sound/firewire/amdtp-stream-trace.h | 2 +- sound/firewire/amdtp-stream.c | 25 +++++++++++++------------ sound/firewire/amdtp-stream.h | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-)
After testing this patch, I agree with the usage of '(current_work() == &s->period_work)' as an alternative of 'in_interrupt()'.
However, the usage is not appropriate for tracepoints event in the case.
diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h index 26e7cb555d3c..5386d548cada 100644 --- a/sound/firewire/amdtp-stream-trace.h +++ b/sound/firewire/amdtp-stream-trace.h @@ -49,7 +49,7 @@ TRACE_EVENT(amdtp_packet, __entry->data_blocks = data_blocks; __entry->data_block_counter = data_block_counter, __entry->packet_index = s->packet_index;
__entry->irq = !!in_interrupt();
__entry->index = index; ), TP_printk(__entry->irq = (current_work() == &s->period_work);
The tracepoints event is probed in two contexts: * softirq for isochronous context to process hardware events of 1394 OHCI. * user task of ALSA PCM applications.
However, it's not probed in the workqueue task since the case is already avoided carefully in below patch:
@@ -1184,7 +1185,7 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
if (irq_target && amdtp_stream_running(irq_target)) { // This function is called in software IRQ context of
// period_tasklet or process context.
// // When the software IRQ context was scheduled by software IRQ // context of IT contexts, queued packets were already handled.// period_work or process context.
@@ -1195,9 +1196,9 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, // immediately to keep better granularity of PCM pointer. // // Later, the process context will sometimes schedules software
// IRQ context of the period_tasklet. Then, no need to flush the
// queue by the same reason as described in the above// IRQ context of the period_work. Then, no need to flush the
if (!in_interrupt()) {
if (current_work() != &s->period_work) { // Queued packet should be processed without any kernel // preemption to keep latency against bus cycle. preempt_disable();
as long as testing, I can see no logs for the trancepoints event with the 'irq' field is 1. I would like you to leave 'amdtp-stream-trace.h' as is by dropping the above change since the irq field should record whether the context is softirq or user task.
Thanks
Takashi Sakamoto
On Sun, 06 Sep 2020 10:26:28 +0200, Takashi Sakamoto wrote:
Hi,
On Thu, Sep 03, 2020 at 12:41:30PM +0200, Takashi Iwai wrote:
The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In FireWire driver, a tasklet is still used for offloading the AMDTP PCM stream handling. It can be achieved gracefully with a work queued, too.
This patch replaces the tasklet usage in firewire-lib driver with a simple work. The conversion is fairly straightforward but for the in_interrupt() checks that are replaced with the check using the current_work().
Signed-off-by: Takashi Iwai tiwai@suse.de
sound/firewire/amdtp-stream-trace.h | 2 +- sound/firewire/amdtp-stream.c | 25 +++++++++++++------------ sound/firewire/amdtp-stream.h | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-)
After testing this patch, I agree with the usage of '(current_work() == &s->period_work)' as an alternative of 'in_interrupt()'.
However, the usage is not appropriate for tracepoints event in the case.
diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h index 26e7cb555d3c..5386d548cada 100644 --- a/sound/firewire/amdtp-stream-trace.h +++ b/sound/firewire/amdtp-stream-trace.h @@ -49,7 +49,7 @@ TRACE_EVENT(amdtp_packet, __entry->data_blocks = data_blocks; __entry->data_block_counter = data_block_counter, __entry->packet_index = s->packet_index;
__entry->irq = !!in_interrupt();
__entry->index = index; ), TP_printk(__entry->irq = (current_work() == &s->period_work);
The tracepoints event is probed in two contexts:
- softirq for isochronous context to process hardware events of 1394 OHCI.
- user task of ALSA PCM applications.
However, it's not probed in the workqueue task since the case is already avoided carefully in below patch:
@@ -1184,7 +1185,7 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
if (irq_target && amdtp_stream_running(irq_target)) { // This function is called in software IRQ context of
// period_tasklet or process context.
// // When the software IRQ context was scheduled by software IRQ // context of IT contexts, queued packets were already handled.// period_work or process context.
@@ -1195,9 +1196,9 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, // immediately to keep better granularity of PCM pointer. // // Later, the process context will sometimes schedules software
// IRQ context of the period_tasklet. Then, no need to flush the
// queue by the same reason as described in the above// IRQ context of the period_work. Then, no need to flush the
if (!in_interrupt()) {
if (current_work() != &s->period_work) { // Queued packet should be processed without any kernel // preemption to keep latency against bus cycle. preempt_disable();
as long as testing, I can see no logs for the trancepoints event with the 'irq' field is 1. I would like you to leave 'amdtp-stream-trace.h' as is by dropping the above change since the irq field should record whether the context is softirq or user task.
OK, makes sense. Will fix in v2. Thanks for reviewing!
Takashi
The miXart driver has been already converted to use the threaded IRQ instead of tasklet while there is a remaining comment still mentioning a tasklet. Update the comment appropriately.
Fixes: 8d3a8b5cb57d ("ALSA: mixart: Use nonatomic PCM ops") Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/mixart/mixart.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/pci/mixart/mixart.h b/sound/pci/mixart/mixart.h index 42111562e9bc..cbed6d9a9f2e 100644 --- a/sound/pci/mixart/mixart.h +++ b/sound/pci/mixart/mixart.h @@ -69,7 +69,7 @@ struct mixart_mgr { u32 msg_fifo[MSG_FIFO_SIZE]; int msg_fifo_readptr; int msg_fifo_writeptr; - atomic_t msg_processed; /* number of messages to be processed in tasklet */ + atomic_t msg_processed; /* number of messages to be processed in irq thread */
struct mutex lock; /* interrupt lock */ struct mutex msg_lock; /* mailbox lock */
participants (2)
-
Takashi Iwai
-
Takashi Sakamoto