[alsa-devel] [PATCH 2/2] ALSA: ctl: refactoring for read operation
Takashi Iwai
tiwai at suse.de
Fri Apr 10 12:43:43 CEST 2015
At Fri, 10 Apr 2015 19:32:58 +0900,
Takashi Sakamoto wrote:
>
> On Apr 10 2015 16:48, Takashi Iwai wrote:
> > At Fri, 10 Apr 2015 08:43:01 +0900,
> > Takashi Sakamoto wrote:
> >>
> >> snd_ctl_read() has nested loop and complicated condition for return
> >> value. This is not better for reading.
> >>
> >> This commit applies refactoring with two loops.
> >>
> >> Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
> >> ---
> >> sound/core/control.c | 77 ++++++++++++++++++++++++++++++++--------------------
> >> 1 file changed, 47 insertions(+), 30 deletions(-)
> >>
> >> diff --git a/sound/core/control.c b/sound/core/control.c
> >> index de19d56..da6497d 100644
> >> --- a/sound/core/control.c
> >> +++ b/sound/core/control.c
> >> @@ -1520,58 +1520,75 @@ static ssize_t snd_ctl_read(struct file *file, char __user *buffer,
> >> size_t count, loff_t * offset)
> >> {
> >> struct snd_ctl_file *ctl;
> >> - int err = 0;
> >> - ssize_t result = 0;
> >> + struct snd_ctl_event ev;
> >> + struct snd_kctl_event *kev;
> >> + wait_queue_t wait;
> >> + ssize_t result;
> >>
> >> ctl = file->private_data;
> >> if (snd_BUG_ON(!ctl || !ctl->card))
> >> return -ENXIO;
> >> if (!ctl->subscribed)
> >> return -EBADFD;
> >> +
> >> + /* The size of given buffer should be larger than at least one event. */
> >> if (count < sizeof(struct snd_ctl_event))
> >> return -EINVAL;
> >> +
> >> + /* Block till any events were queued. */
> >> spin_lock_irq(&ctl->read_lock);
> >> - while (count >= sizeof(struct snd_ctl_event)) {
> >> - struct snd_ctl_event ev;
> >> - struct snd_kctl_event *kev;
> >> - while (list_empty(&ctl->events)) {
> >> - wait_queue_t wait;
> >> - if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
> >> - err = -EAGAIN;
> >> - goto __end_lock;
> >> - }
> >> - init_waitqueue_entry(&wait, current);
> >> - add_wait_queue(&ctl->change_sleep, &wait);
> >> - set_current_state(TASK_INTERRUPTIBLE);
> >> - spin_unlock_irq(&ctl->read_lock);
> >> - schedule();
> >> - remove_wait_queue(&ctl->change_sleep, &wait);
> >> - if (ctl->card->shutdown)
> >> - return -ENODEV;
> >> - if (signal_pending(current))
> >> - return -ERESTARTSYS;
> >> - spin_lock_irq(&ctl->read_lock);
> >> + while (list_empty(&ctl->events)) {
> >> + if (file->f_flags & O_NONBLOCK) {
> >> + result = -EAGAIN;
> >> + goto unlock;
> >> }
> >> +
> >> + /* The card was disconnected. The queued events are dropped. */
> >> + if (ctl->card->shutdown) {
> >> + result = -ENODEV;
> >> + goto unlock;
> >> + }
> >> +
> >> +
> >> + init_waitqueue_entry(&wait, current);
> >> + add_wait_queue(&ctl->change_sleep, &wait);
> >> + set_current_state(TASK_INTERRUPTIBLE);
> >> + spin_unlock_irq(&ctl->read_lock);
> >> +
> >> + schedule();
> >> +
> >> + remove_wait_queue(&ctl->change_sleep, &wait);
> >> +
> >> + if (signal_pending(current))
> >> + return -ERESTARTSYS;
> >> +
> >> + spin_lock_irq(&ctl->read_lock);
> >> + }
> >> +
> >> + /* Copy events till the buffer filled, or no events are remained. */
> >> + result = 0;
> >> + while (count >= sizeof(struct snd_ctl_event)) {
> >> + if (list_empty(&ctl->events))
> >> + break;
> >> kev = snd_kctl_event(ctl->events.next);
> >> + list_del(&kev->list);
> >> +
> >> ev.type = SNDRV_CTL_EVENT_ELEM;
> >> ev.data.elem.mask = kev->mask;
> >> ev.data.elem.id = kev->id;
> >> - list_del(&kev->list);
> >> - spin_unlock_irq(&ctl->read_lock);
> >> kfree(kev);
> >> if (copy_to_user(buffer, &ev, sizeof(struct snd_ctl_event))) {
> >> - err = -EFAULT;
> >> - goto __end;
> >> + result = -EFAULT;
> >> + break;
> >
> > This still changes the behavior. The read returns the size that has
> > been read at first even when an error happens if some data was already
> > read. The error code is returned at the next read. This is a design
> > problem of read syscall.
>
> I'm unaware of it... These code should be:
>
> if (copy_to_user(buffer, &ev, sizeof(struct snd_ctl_event))) {
> if (result == 0)
> result = -EFAULT;
> break;
Well, it's not much better than the original code, IMO.
> And I realize sound/firewire/fireworks/fireworks_hwdep.c has the same
> bug. I'll post another patch for this bug.
This is an implementation detail and I believe it rather depends on
each driver. (But I should reread POSIX definition again before
concluding it.)
That said, it isn't wrong to return -EFAULT immediately, per se.
The problem here is that you change the existing behavior. Especially
a core code like this should be treated carefully even for such a
small behavior change.
Takashi
More information about the Alsa-devel
mailing list