Commit 2225e79b authored by Takashi Sakamoto's avatar Takashi Sakamoto Committed by Takashi Iwai

ALSA: core: reduce stack usage related to snd_ctl_new()

The callers of snd_ctl_new() need to have 'struct snd_kcontrol' data,
and pass the data as template. Then, the function allocates the structure
data again and copy from the template. This is a waste of resources.
Especially, the callers use large stack for the template.

This commit removes a need of template for the function, thus, changes
the prototype of snd_ctl_new(). Furthermore, this commit changes
the code of callers, snd_ctl_new1() and snd_ctl_elem_add() for better
shape.
Signed-off-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 4ed56666
...@@ -192,36 +192,43 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, ...@@ -192,36 +192,43 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
EXPORT_SYMBOL(snd_ctl_notify); EXPORT_SYMBOL(snd_ctl_notify);
/** /**
* snd_ctl_new - create a control instance from the template * snd_ctl_new - create a new control instance with some elements
* @control: the control template * @kctl: the pointer to store new control instance
* @access: the default control access * @count: the number of elements in this control
* @access: the default access flags for elements in this control
* @file: given when locking these elements
* *
* Allocates a new struct snd_kcontrol instance and copies the given template * Allocates a memory object for a new control instance. The instance has
* to the new instance. It does not copy volatile data (access). * elements as many as the given number (@count). Each element has given
* access permissions (@access). Each element is locked when @file is given.
* *
* Return: The pointer of the new instance, or %NULL on failure. * Return: 0 on success, error code on failure
*/ */
static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count,
unsigned int access) unsigned int access, struct snd_ctl_file *file)
{ {
struct snd_kcontrol *kctl; unsigned int size;
unsigned int idx; unsigned int idx;
if (snd_BUG_ON(!control || !control->count)) if (count == 0 || count > MAX_CONTROL_COUNT)
return NULL; return -EINVAL;
if (control->count > MAX_CONTROL_COUNT) size = sizeof(struct snd_kcontrol);
return NULL; size += sizeof(struct snd_kcontrol_volatile) * count;
kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL); *kctl = kzalloc(size, GFP_KERNEL);
if (kctl == NULL) { if (*kctl == NULL) {
pr_err("ALSA: Cannot allocate control instance\n"); pr_err("ALSA: Cannot allocate control instance\n");
return NULL; return -ENOMEM;
} }
*kctl = *control;
for (idx = 0; idx < kctl->count; idx++) for (idx = 0; idx < count; idx++) {
kctl->vd[idx].access = access; (*kctl)->vd[idx].access = access;
return kctl; (*kctl)->vd[idx].owner = file;
}
(*kctl)->count = count;
return 0;
} }
/** /**
...@@ -238,37 +245,53 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, ...@@ -238,37 +245,53 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
void *private_data) void *private_data)
{ {
struct snd_kcontrol kctl; struct snd_kcontrol *kctl;
unsigned int count;
unsigned int access; unsigned int access;
int err;
if (snd_BUG_ON(!ncontrol || !ncontrol->info)) if (snd_BUG_ON(!ncontrol || !ncontrol->info))
return NULL; return NULL;
memset(&kctl, 0, sizeof(kctl));
kctl.id.iface = ncontrol->iface; count = ncontrol->count;
kctl.id.device = ncontrol->device; if (count == 0)
kctl.id.subdevice = ncontrol->subdevice; count = 1;
access = ncontrol->access;
if (access == 0)
access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_VOLATILE |
SNDRV_CTL_ELEM_ACCESS_INACTIVE |
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
err = snd_ctl_new(&kctl, count, access, NULL);
if (err < 0)
return NULL;
/* The 'numid' member is decided when calling snd_ctl_add(). */
kctl->id.iface = ncontrol->iface;
kctl->id.device = ncontrol->device;
kctl->id.subdevice = ncontrol->subdevice;
if (ncontrol->name) { if (ncontrol->name) {
strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)); strlcpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name));
if (strcmp(ncontrol->name, kctl.id.name) != 0) if (strcmp(ncontrol->name, kctl->id.name) != 0)
pr_warn("ALSA: Control name '%s' truncated to '%s'\n", pr_warn("ALSA: Control name '%s' truncated to '%s'\n",
ncontrol->name, kctl.id.name); ncontrol->name, kctl->id.name);
} }
kctl.id.index = ncontrol->index; kctl->id.index = ncontrol->index;
kctl.count = ncontrol->count ? ncontrol->count : 1;
access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : kctl->info = ncontrol->info;
(ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| kctl->get = ncontrol->get;
SNDRV_CTL_ELEM_ACCESS_VOLATILE| kctl->put = ncontrol->put;
SNDRV_CTL_ELEM_ACCESS_INACTIVE| kctl->tlv.p = ncontrol->tlv.p;
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND| kctl->private_value = ncontrol->private_value;
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)); kctl->private_data = private_data;
kctl.info = ncontrol->info;
kctl.get = ncontrol->get; return kctl;
kctl.put = ncontrol->put;
kctl.tlv.p = ncontrol->tlv.p;
kctl.private_value = ncontrol->private_value;
kctl.private_data = private_data;
return snd_ctl_new(&kctl, access);
} }
EXPORT_SYMBOL(snd_ctl_new1); EXPORT_SYMBOL(snd_ctl_new1);
...@@ -1179,44 +1202,48 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, ...@@ -1179,44 +1202,48 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
[SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64, [SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64,
}; };
struct snd_card *card = file->card; struct snd_card *card = file->card;
struct snd_kcontrol kctl, *_kctl; struct snd_kcontrol *kctl;
unsigned int count;
unsigned int access; unsigned int access;
long private_size; long private_size;
struct user_element *ue; struct user_element *ue;
int idx, err; int err;
access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
SNDRV_CTL_ELEM_ACCESS_INACTIVE|
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
info->id.numid = 0;
memset(&kctl, 0, sizeof(kctl));
/* Delete a control to replace them if needed. */
if (replace) { if (replace) {
info->id.numid = 0;
err = snd_ctl_remove_user_ctl(file, &info->id); err = snd_ctl_remove_user_ctl(file, &info->id);
if (err) if (err)
return err; return err;
} }
if (card->user_ctl_count >= MAX_USER_CONTROLS) /*
* The number of userspace controls are counted control by control,
* not element by element.
*/
if (card->user_ctl_count + 1 > MAX_USER_CONTROLS)
return -ENOMEM; return -ENOMEM;
memcpy(&kctl.id, &info->id, sizeof(info->id)); /* Check the number of elements for this userspace control. */
kctl.count = info->owner ? info->owner : 1; count = info->owner;
access |= SNDRV_CTL_ELEM_ACCESS_USER; if (count == 0)
if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) count = 1;
kctl.info = snd_ctl_elem_user_enum_info;
else /* Arrange access permissions if needed. */
kctl.info = snd_ctl_elem_user_info; access = info->access;
if (access & SNDRV_CTL_ELEM_ACCESS_READ) if (access == 0)
kctl.get = snd_ctl_elem_user_get; access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
kctl.put = snd_ctl_elem_user_put; SNDRV_CTL_ELEM_ACCESS_INACTIVE |
if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) { SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE);
kctl.tlv.c = snd_ctl_elem_user_tlv; if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)
access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
} access |= SNDRV_CTL_ELEM_ACCESS_USER;
/*
* Check information and calculate the size of data specific to
* this userspace control.
*/
if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN || if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64)
return -EINVAL; return -EINVAL;
...@@ -1226,11 +1253,27 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, ...@@ -1226,11 +1253,27 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
if (info->count < 1 || if (info->count < 1 ||
info->count > max_value_counts[info->type]) info->count > max_value_counts[info->type])
return -EINVAL; return -EINVAL;
private_size = value_sizes[info->type] * info->count; private_size = value_sizes[info->type] * info->count;
ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL);
if (ue == NULL) /*
* Keep memory object for this userspace control. After passing this
* code block, the instance should be freed by snd_ctl_free_one().
*
* Note that these elements in this control are locked.
*/
err = snd_ctl_new(&kctl, count, access, file);
if (err < 0)
return err;
kctl->private_data = kzalloc(sizeof(struct user_element) + private_size,
GFP_KERNEL);
if (kctl->private_data == NULL) {
kfree(kctl);
return -ENOMEM; return -ENOMEM;
}
kctl->private_free = snd_ctl_elem_user_free;
/* Set private data for this userspace control. */
ue = (struct user_element *)kctl->private_data;
ue->card = card; ue->card = card;
ue->info = *info; ue->info = *info;
ue->info.access = 0; ue->info.access = 0;
...@@ -1239,21 +1282,25 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, ...@@ -1239,21 +1282,25 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
err = snd_ctl_elem_init_enum_names(ue); err = snd_ctl_elem_init_enum_names(ue);
if (err < 0) { if (err < 0) {
kfree(ue); snd_ctl_free_one(kctl);
return err; return err;
} }
} }
kctl.private_free = snd_ctl_elem_user_free;
_kctl = snd_ctl_new(&kctl, access); /* Set callback functions. */
if (_kctl == NULL) { if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
kfree(ue->priv_data); kctl->info = snd_ctl_elem_user_enum_info;
kfree(ue); else
return -ENOMEM; kctl->info = snd_ctl_elem_user_info;
} if (access & SNDRV_CTL_ELEM_ACCESS_READ)
_kctl->private_data = ue; kctl->get = snd_ctl_elem_user_get;
for (idx = 0; idx < _kctl->count; idx++) if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
_kctl->vd[idx].owner = file; kctl->put = snd_ctl_elem_user_put;
err = snd_ctl_add(card, _kctl); if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)
kctl->tlv.c = snd_ctl_elem_user_tlv;
/* This function manage to free the instance on failure. */
err = snd_ctl_add(card, kctl);
if (err < 0) if (err < 0)
return err; return err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment