diff mbox

[2/2] ALSA: ctl: refactoring for read operation

Message ID 1428512108-1852-3-git-send-email-o-takashi@sakamocchi.jp (mailing list archive)
State New, archived
Headers show

Commit Message

Takashi Sakamoto April 8, 2015, 4:55 p.m. UTC
snd_ctl_read() has nested loop and complicated condition for return
status. This is not better for reading.

This commit applies refactoring with two loops.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/core/control.c | 74 ++++++++++++++++++++++++++++++++--------------------
 1 file changed, 46 insertions(+), 28 deletions(-)

Comments

Takashi Iwai April 9, 2015, 5:33 a.m. UTC | #1
At Thu,  9 Apr 2015 01:55:08 +0900,
Takashi Sakamoto wrote:
> 
> snd_ctl_read() has nested loop and complicated condition for return
> status. This is not better for reading.
> 
> This commit applies refactoring with two loops.
> 
> Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
> ---
>  sound/core/control.c | 74 ++++++++++++++++++++++++++++++++--------------------
>  1 file changed, 46 insertions(+), 28 deletions(-)
> 
> diff --git a/sound/core/control.c b/sound/core/control.c
> index de19d56..6870baf 100644
> --- a/sound/core/control.c
> +++ b/sound/core/control.c
> @@ -1520,58 +1520,76 @@ 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;
> +	size_t result;
> +	int err;
>  
>  	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);
> +	while (list_empty(&ctl->events)) {
> +		if (file->f_flags & O_NONBLOCK) {
> +			spin_unlock_irq(&ctl->read_lock);
> +			return -EAGAIN;

It's better not to spread the unlock call allover the places but just
"goto unlock".

> +		}
> +
> +		/* The card was disconnected. The queued events are dropped. */
> +		if (ctl->card->shutdown) {
>  			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);
> +			return -ENODEV;
>  		}
> +
> +
> +		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;
> +			break;
>  		}
> -		spin_lock_irq(&ctl->read_lock);
> +
>  		buffer += sizeof(struct snd_ctl_event);
>  		count -= sizeof(struct snd_ctl_event);
>  		result += sizeof(struct snd_ctl_event);
>  	}
> -      __end_lock:
> +
>  	spin_unlock_irq(&ctl->read_lock);
> -      __end:
> -      	return result > 0 ? result : err;
> +	return result;

You can't ignore the error.  -EFAULT should be still returned when it
happens at the first read.


Takashi
Takashi Sakamoto April 9, 2015, 7:35 a.m. UTC | #2
On Apr 09 2015 14:33, Takashi Iwai wrote:
> At Thu,  9 Apr 2015 01:55:08 +0900,
> Takashi Sakamoto wrote:
>>
>> snd_ctl_read() has nested loop and complicated condition for return
>> status. This is not better for reading.
>>
>> This commit applies refactoring with two loops.
>>
>> Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
>> ---
>>   sound/core/control.c | 74 ++++++++++++++++++++++++++++++++--------------------
>>   1 file changed, 46 insertions(+), 28 deletions(-)
>>
>> diff --git a/sound/core/control.c b/sound/core/control.c
>> index de19d56..6870baf 100644
>> --- a/sound/core/control.c
>> +++ b/sound/core/control.c
>> @@ -1520,58 +1520,76 @@ 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;
>> +	size_t result;
>> +	int err;
>>
>>   	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);
>> +	while (list_empty(&ctl->events)) {
>> +		if (file->f_flags & O_NONBLOCK) {
>> +			spin_unlock_irq(&ctl->read_lock);
>> +			return -EAGAIN;
>
> It's better not to spread the unlock call allover the places but just
> "goto unlock".
>
>> +		}
>> +
>> +		/* The card was disconnected. The queued events are dropped. */
>> +		if (ctl->card->shutdown) {
>>   			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);
>> +			return -ENODEV;
>>   		}
>> +
>> +
>> +		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;
>> +			break;
>>   		}
>> -		spin_lock_irq(&ctl->read_lock);
>> +
>>   		buffer += sizeof(struct snd_ctl_event);
>>   		count -= sizeof(struct snd_ctl_event);
>>   		result += sizeof(struct snd_ctl_event);
>>   	}
>> -      __end_lock:
>> +
>>   	spin_unlock_irq(&ctl->read_lock);
>> -      __end:
>> -      	return result > 0 ? result : err;
>> +	return result;
>
> You can't ignore the error.  -EFAULT should be still returned when it
> happens at the first read.

Exactly. It's my fault. I should have assign it to result, instead of err.

Well, I'd like to post revised version, but it's just before a week to 
open merge window for Linux 4.01. Should I postpone the posting a few 
weeks later?


Thanks.

Takashi Sakamoto
Takashi Iwai April 9, 2015, 8:17 a.m. UTC | #3
At Thu, 09 Apr 2015 16:35:06 +0900,
Takashi Sakamoto wrote:
> 
> On Apr 09 2015 14:33, Takashi Iwai wrote:
> > At Thu,  9 Apr 2015 01:55:08 +0900,
> > Takashi Sakamoto wrote:
> >>
> >> snd_ctl_read() has nested loop and complicated condition for return
> >> status. This is not better for reading.
> >>
> >> This commit applies refactoring with two loops.
> >>
> >> Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
> >> ---
> >>   sound/core/control.c | 74 ++++++++++++++++++++++++++++++++--------------------
> >>   1 file changed, 46 insertions(+), 28 deletions(-)
> >>
> >> diff --git a/sound/core/control.c b/sound/core/control.c
> >> index de19d56..6870baf 100644
> >> --- a/sound/core/control.c
> >> +++ b/sound/core/control.c
> >> @@ -1520,58 +1520,76 @@ 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;
> >> +	size_t result;
> >> +	int err;
> >>
> >>   	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);
> >> +	while (list_empty(&ctl->events)) {
> >> +		if (file->f_flags & O_NONBLOCK) {
> >> +			spin_unlock_irq(&ctl->read_lock);
> >> +			return -EAGAIN;
> >
> > It's better not to spread the unlock call allover the places but just
> > "goto unlock".
> >
> >> +		}
> >> +
> >> +		/* The card was disconnected. The queued events are dropped. */
> >> +		if (ctl->card->shutdown) {
> >>   			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);
> >> +			return -ENODEV;
> >>   		}
> >> +
> >> +
> >> +		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;
> >> +			break;
> >>   		}
> >> -		spin_lock_irq(&ctl->read_lock);
> >> +
> >>   		buffer += sizeof(struct snd_ctl_event);
> >>   		count -= sizeof(struct snd_ctl_event);
> >>   		result += sizeof(struct snd_ctl_event);
> >>   	}
> >> -      __end_lock:
> >> +
> >>   	spin_unlock_irq(&ctl->read_lock);
> >> -      __end:
> >> -      	return result > 0 ? result : err;
> >> +	return result;
> >
> > You can't ignore the error.  -EFAULT should be still returned when it
> > happens at the first read.
> 
> Exactly. It's my fault. I should have assign it to result, instead of err.
> 
> Well, I'd like to post revised version, but it's just before a week to 
> open merge window for Linux 4.01. Should I postpone the posting a few 
> weeks later?

These two patches are fine to take now as they are no intrusive
changes.


thanks,

Takashi
diff mbox

Patch

diff --git a/sound/core/control.c b/sound/core/control.c
index de19d56..6870baf 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -1520,58 +1520,76 @@  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;
+	size_t result;
+	int err;
 
 	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);
+	while (list_empty(&ctl->events)) {
+		if (file->f_flags & O_NONBLOCK) {
+			spin_unlock_irq(&ctl->read_lock);
+			return -EAGAIN;
+		}
+
+		/* The card was disconnected. The queued events are dropped. */
+		if (ctl->card->shutdown) {
 			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);
+			return -ENODEV;
 		}
+
+
+		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;
+			break;
 		}
-		spin_lock_irq(&ctl->read_lock);
+
 		buffer += sizeof(struct snd_ctl_event);
 		count -= sizeof(struct snd_ctl_event);
 		result += sizeof(struct snd_ctl_event);
 	}
-      __end_lock:
+
 	spin_unlock_irq(&ctl->read_lock);
-      __end:
-      	return result > 0 ? result : err;
+	return result;
 }
 
 static unsigned int snd_ctl_poll(struct file *file, poll_table * wait)