[PATCH] ALSA: control: expand limitation on the number of user-defined control element set per card

Takashi Iwai tiwai at suse.de
Sun Jan 24 08:49:11 CET 2021


On Sun, 24 Jan 2021 06:52:25 +0100,
Takashi Sakamoto wrote:
> 
> Hi,
> 
> On Sat, Jan 23, 2021 at 10:10:20AM +0100, Takashi Iwai wrote:
> > On Sat, 23 Jan 2021 04:10:25 +0100,
> > Takashi Sakamoto wrote:
> > > 
> > > Hi,
> > > 
> > > On Fri, Jan 22, 2021 at 03:04:56PM +0100, Takashi Iwai wrote:
> > > > On Fri, 22 Jan 2021 09:20:32 +0100,
> > > > Takashi Sakamoto wrote:
> > > > > 
> > > > > ALSA control core allows usespace application to register control element
> > > > > set by call of ioctl(2) with SNDRV_CTL_IOCTL_ELEM_ADD request. The added
> > > > > control elements are called as 'user-defined'. Currently sound card has
> > > > > limitation on the number of the user-defined control element set up
> > > > > to 32.
> > > > > 
> > > > > The limitation is inconvenient to drivers in ALSA firewire stack since
> > > > > the drivers expect userspace applications to implement function to
> > > > > control device functionalities such as mixing and routing. As the
> > > > > userspace application, snd-firewire-ctl-services project starts:
> > > > > https://github.com/alsa-project/snd-firewire-ctl-services/
> > > > > 
> > > > > The project supports many devices supported by ALSA firewire stack. The
> > > > > limitation is mostly good since routing and mixing controls can be
> > > > > represented by control element set, which includes multiple control element
> > > > > with the same parameters. Nevertheless, it's actually inconvenient to
> > > > > device which has many varied functionalities. For example, plugin effect
> > > > > such as channel strip and reverb has many parameters. For the case, many
> > > > > control elements are required to configure the parameters and control
> > > > > element set cannot aggregates them for the parameters. At present, the
> > > > > implementations for below models requires more control element sets
> > > > > than 32:
> > > > > 
> > > > >  * snd-bebob-ctl-service
> > > > >    * Apogee Ensemble (31 sets for 34 elements)
> > > > >  * snd-dice-ctl-service
> > > > >    * TC Electronic Konnekt 24d (78 sets for 94 elements)
> > > > >    * TC Electronic Studio Konnekt 48 (98 sets for 114 elements)
> > > > >    * TC Electronic Konnekt Live (88 sets for 104 elements)
> > > > >    * TC Electronic Impact Twin (70 sets for 86 elements)
> > > > >    * Focusrite Liquid Saffire 56 (37 sets for 52 elements)
> > > > > 
> > > > > This commit expands the limitation according to requirement from the above
> > > > > applications. As a result, userspace applications can add control element
> > > > > sets up to 150 per sound card. It results in 154,200 user-defined control
> > > > > elements as maximum since one control element set can include 1028 control
> > > > > elements.
> > > > 
> > > > Thinking of this change again after reading your description, I find
> > > > that a more flexible and safer approach would be to limit the number
> > > > of total elements.  That is, count the number of items in each
> > > > element, and set the max to (32 * MAX_CONTROL_COUNT).  This will keep
> > > > the same max as the current implementation can achieve, while it
> > > > allows more elements as long as they contain lower number of items.
> > > > 
> > > > So, something like below (totally untested).
> > > > 
> > > > 
> > > > thanks,
> > > > 
> > > > Takashi
> > > > 
> > > > --- a/sound/core/control.c
> > > > +++ b/sound/core/control.c
> > > > @@ -18,10 +18,11 @@
> > > >  #include <sound/info.h>
> > > >  #include <sound/control.h>
> > > >  
> > > > -/* max number of user-defined controls */
> > > > -#define MAX_USER_CONTROLS	32
> > > >  #define MAX_CONTROL_COUNT	1028
> > > >  
> > > > +/* max number of user-defined controls */
> > > > +#define MAX_USER_CONTROLS	(32 * MAX_CONTROL_COUNT)
> > > > +
> > > >  struct snd_kctl_ioctl {
> > > >  	struct list_head list;		/* list of all ioctls */
> > > >  	snd_kctl_ioctl_func_t fioctl;
> > > > @@ -520,6 +521,7 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
> > > >  	struct snd_card *card = file->card;
> > > >  	struct snd_kcontrol *kctl;
> > > >  	int idx, ret;
> > > > +	int count;
> > > >  
> > > >  	down_write(&card->controls_rwsem);
> > > >  	kctl = snd_ctl_find_id(card, id);
> > > > @@ -536,10 +538,11 @@ static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
> > > >  			ret = -EBUSY;
> > > >  			goto error;
> > > >  		}
> > > > +	count = kctl->count;
> > > >  	ret = snd_ctl_remove(card, kctl);
> > > >  	if (ret < 0)
> > > >  		goto error;
> > > > -	card->user_ctl_count--;
> > > > +	card->user_ctl_count -= count;
> > > >  error:
> > > >  	up_write(&card->controls_rwsem);
> > > >  	return ret;
> > > > @@ -1435,18 +1438,18 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
> > > >  			return err;
> > > >  	}
> > > >  
> > > > +	/* Check the number of elements for this userspace control. */
> > > > +	count = info->owner;
> > > > +	if (count == 0)
> > > > +		count = 1;
> > > > +
> > > >  	/*
> > > >  	 * The number of userspace controls are counted control by control,
> > > >  	 * not element by element.
> > > >  	 */
> > > > -	if (card->user_ctl_count + 1 > MAX_USER_CONTROLS)
> > > > +	if (card->user_ctl_count + count > MAX_USER_CONTROLS)
> > > >  		return -ENOMEM;
> > > >  
> > > > -	/* Check the number of elements for this userspace control. */
> > > > -	count = info->owner;
> > > > -	if (count == 0)
> > > > -		count = 1;
> > > > -
> > > >  	/* Arrange access permissions if needed. */
> > > >  	access = info->access;
> > > >  	if (access == 0)
> > > > @@ -1535,7 +1538,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
> > > >  	 * which locks the element.
> > > >  	 */
> > > >  
> > > > -	card->user_ctl_count++;
> > > > +	card->user_ctl_count += count;
> > > >  
> > > >   unlock:
> > > >  	up_write(&card->controls_rwsem);
> > > 
> > > I have no objection to any change as long as it allows the service programs
> > > to add control elements enough for target device. However, it's unclear
> > > what is flexible and safe in the above patch.
> > > 
> > > When adding user-defined control element set, some objects are allocated
> > > for below structures with some variable-length members:
> > >  * struct snd_kcontrol (in include/sound/control.h)
> > >  * struct user_element (in sound/core/control.h)
> > > 
> > > Current implementation is to avoid too much allocation for the above
> > > against userspace applications with bugs or mis-programming. It's
> > > reasonable to limit the allocation according to count of added control
> > > element set for the purpose.
> > > 
> > > On the other hand, when counting the number of added control element for
> > > the limitation, the above becomes loose. In the worst case, the patch
> > > allows 32,896 sets to be allocated and against comments in my previous
> > > patch.
> > 
> > OK, my previous patch was too simplified (I forgot to take the
> > private_data into account), but the point is that what we want is to
> > cap the worst case memory consumption.
> > 
> > If I calculate correctly, user_element is 320 bytes, and the value is
> > up to 512 bytes for each item, and snd_kcontrol is 144 bytes, and
> > snd_kcontrol_volatile is 16 bytes per item.  And each element may
> > contain 1028 items.  So, the worst case scenario of the memory
> > consumption is:
> >   (320 + 512*1028 + 144 + 16*1024) * 32 = 17383936
> > That is, currently we allow 16MB at most.
> > 
> > By increasing MAX_USER_CONTROLS to 150, it'll become 77MB.
> > 
> > And, think what if you'd need to increase more in future, e.g. 512
> > elements.  The max consumption also increases linearly.
> > 
> > OTOH, imagine that we cap the memory consumption to 16MB instead of
> > limiting only the MAX_USER_CONTROLS.  This lets user still allow to
> > allocate more elements with smaller number of items (that is the
> > common use case).  In this way, we don't take a risk of higher memory
> > consumption while user can deploy the user elements more flexibly.
> 
> The memory object for data of Type-Length-Value style is underestimate in
> your calculation for the worst case. For user-defined control element set,
> the size is (1024 * 128) per control element set[1].
> 
> Of cource, it's possible to judge that usual userspace application don't
> use data of Type-Length-Value style so much. However, we are assuming
> the worst case now.

Right, that should be taken into account.

> ```
> Objects linearly increased according to the number of user-defined control
> element sets:
>  * struct snd_kcontrol (144 bytes)
>  * struct user_element (320 bytes)
>  * data of TLV ((1024 * 128) bytes as maximum)
> 
> Objects linearly increased according to the number of control elements:
>  * data of values (max. 1024 bytes in System V ABI with LP64 data type)
>  * data of snd_kcontrol_volatile (16 bytes)
> 
> Memory consumption under current limitation:
>  (144 + 320 + 1024 * 128) * 32 + (1024 + 16) * 1028 = 5,278,272
> 
> Scenario for the worst case when appying the patch:
> * adding 32,896 control element sets
>  * for elements: (1024 + 16) * 1028 * 32 = 34,211,840
>  * for element sets: (144 + 320 + 1024 * 128) * 32896 = 4,327,008,256

Forget my previous patch.  The code change suggested there is
obviously not sufficient, but you need only consider about its idea.

> Scenario for the worst case when applying my patch:
> * adding 150 control element sets
>  * for elements: (1024 + 16) * 1028 * 150 = 160,368,000
>  * for element sets: (144 + 320 + 1024 * 128) * 150 = 19,730,400
> ```

See that your patch increases the memory consumption so much?
That's my point.

We may give more user elements without increasing the worst-case
memory footprint in normal scenarios.  So scratch MAX_USER_CONTROLS
but count each user element's memory consumption and define its total
limit instead.


Takashi


More information about the Alsa-devel mailing list