Skip to content

Commit bbbc7e8

Browse files
committed
ALSA: hda - Allocate hda_pcm objects dynamically
So far, the hda_codec object kept the hda_pcm list in an array, and the codec driver was expected to assign the array. However, this makes the object life cycle management harder, because the assigned array is freed at the codec driver detach while it might be still accessed by the opened streams. In this patch, we allocate each hda_pcm object dynamically and manage it as a linked list. Each object has a kref refcount, and both the codec driver binder and the PCM open/close touches it, so that the object won't be freed while in use. Signed-off-by: Takashi Iwai <tiwai@suse.de>
1 parent f4de8fe commit bbbc7e8

File tree

10 files changed

+145
-103
lines changed

10 files changed

+145
-103
lines changed

sound/pci/hda/hda_codec.c

Lines changed: 76 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,60 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid)
11161116
return p;
11171117
}
11181118

1119+
/*
1120+
* PCM device
1121+
*/
1122+
static void release_pcm(struct kref *kref)
1123+
{
1124+
struct hda_pcm *pcm = container_of(kref, struct hda_pcm, kref);
1125+
1126+
if (pcm->pcm)
1127+
snd_device_free(pcm->codec->card, pcm->pcm);
1128+
clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits);
1129+
kfree(pcm->name);
1130+
kfree(pcm);
1131+
}
1132+
1133+
void snd_hda_codec_pcm_put(struct hda_pcm *pcm)
1134+
{
1135+
kref_put(&pcm->kref, release_pcm);
1136+
}
1137+
EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put);
1138+
1139+
struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
1140+
const char *fmt, ...)
1141+
{
1142+
struct hda_pcm *pcm;
1143+
va_list args;
1144+
1145+
va_start(args, fmt);
1146+
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
1147+
if (!pcm)
1148+
return NULL;
1149+
1150+
pcm->codec = codec;
1151+
kref_init(&pcm->kref);
1152+
pcm->name = kvasprintf(GFP_KERNEL, fmt, args);
1153+
if (!pcm->name) {
1154+
kfree(pcm);
1155+
return NULL;
1156+
}
1157+
1158+
list_add_tail(&pcm->list, &codec->pcm_list_head);
1159+
return pcm;
1160+
}
1161+
EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new);
1162+
1163+
static void codec_release_pcms(struct hda_codec *codec)
1164+
{
1165+
struct hda_pcm *pcm, *n;
1166+
1167+
list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) {
1168+
list_del_init(&pcm->list);
1169+
snd_hda_codec_pcm_put(pcm);
1170+
}
1171+
}
1172+
11191173
/*
11201174
* codec destructor
11211175
*/
@@ -1124,6 +1178,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
11241178
if (!codec)
11251179
return;
11261180
cancel_delayed_work_sync(&codec->jackpoll_work);
1181+
codec_release_pcms(codec);
11271182
if (device_is_registered(hda_codec_dev(codec)))
11281183
device_del(hda_codec_dev(codec));
11291184
snd_hda_jack_tbl_clear(codec);
@@ -1251,6 +1306,7 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
12511306
snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
12521307
snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
12531308
INIT_LIST_HEAD(&codec->conn_list);
1309+
INIT_LIST_HEAD(&codec->pcm_list_head);
12541310

12551311
INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
12561312
codec->depop_delay = -1;
@@ -2370,9 +2426,8 @@ int snd_hda_lock_devices(struct hda_bus *bus)
23702426
goto err_clear;
23712427

23722428
list_for_each_entry(codec, &bus->codec_list, list) {
2373-
int pcm;
2374-
for (pcm = 0; pcm < codec->num_pcms; pcm++) {
2375-
struct hda_pcm *cpcm = &codec->pcm_info[pcm];
2429+
struct hda_pcm *cpcm;
2430+
list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
23762431
if (!cpcm->pcm)
23772432
continue;
23782433
if (cpcm->pcm->streams[0].substream_opened ||
@@ -2419,8 +2474,6 @@ EXPORT_SYMBOL_GPL(snd_hda_unlock_devices);
24192474
int snd_hda_codec_reset(struct hda_codec *codec)
24202475
{
24212476
struct hda_bus *bus = codec->bus;
2422-
struct snd_card *card = codec->card;
2423-
int i;
24242477

24252478
if (snd_hda_lock_devices(bus) < 0)
24262479
return -EBUSY;
@@ -2429,14 +2482,7 @@ int snd_hda_codec_reset(struct hda_codec *codec)
24292482
cancel_delayed_work_sync(&codec->jackpoll_work);
24302483
flush_workqueue(bus->workq);
24312484
snd_hda_ctls_clear(codec);
2432-
/* release PCMs */
2433-
for (i = 0; i < codec->num_pcms; i++) {
2434-
if (codec->pcm_info[i].pcm) {
2435-
snd_device_free(card, codec->pcm_info[i].pcm);
2436-
clear_bit(codec->pcm_info[i].device,
2437-
bus->pcm_dev_bits);
2438-
}
2439-
}
2485+
codec_release_pcms(codec);
24402486
snd_hda_detach_beep_device(codec);
24412487
if (device_is_registered(hda_codec_dev(codec)))
24422488
device_del(hda_codec_dev(codec));
@@ -2454,8 +2500,6 @@ int snd_hda_codec_reset(struct hda_codec *codec)
24542500
snd_array_free(&codec->cvt_setups);
24552501
snd_array_free(&codec->spdif_out);
24562502
snd_array_free(&codec->verbs);
2457-
codec->num_pcms = 0;
2458-
codec->pcm_info = NULL;
24592503
codec->preset = NULL;
24602504
codec->slave_dig_outs = NULL;
24612505
codec->spdif_status_reset = 0;
@@ -3952,12 +3996,12 @@ static void hda_call_codec_resume(struct hda_codec *codec)
39523996
static int hda_codec_runtime_suspend(struct device *dev)
39533997
{
39543998
struct hda_codec *codec = dev_to_hda_codec(dev);
3999+
struct hda_pcm *pcm;
39554000
unsigned int state;
3956-
int i;
39574001

39584002
cancel_delayed_work_sync(&codec->jackpoll_work);
3959-
for (i = 0; i < codec->num_pcms; i++)
3960-
snd_pcm_suspend_all(codec->pcm_info[i].pcm);
4003+
list_for_each_entry(pcm, &codec->pcm_list_head, list)
4004+
snd_pcm_suspend_all(pcm->pcm);
39614005
state = hda_call_codec_suspend(codec);
39624006
if (codec->d3_stop_clk && codec->epss && (state & AC_PWRST_CLK_STOP_OK))
39634007
clear_bit(codec->addr, &codec->bus->codec_powered);
@@ -4018,22 +4062,21 @@ EXPORT_SYMBOL_GPL(snd_hda_build_controls);
40184062
*/
40194063
static int add_std_chmaps(struct hda_codec *codec)
40204064
{
4021-
int i, str, err;
4065+
struct hda_pcm *pcm;
4066+
int str, err;
40224067

4023-
for (i = 0; i < codec->num_pcms; i++) {
4068+
list_for_each_entry(pcm, &codec->pcm_list_head, list) {
40244069
for (str = 0; str < 2; str++) {
4025-
struct snd_pcm *pcm = codec->pcm_info[i].pcm;
4026-
struct hda_pcm_stream *hinfo =
4027-
&codec->pcm_info[i].stream[str];
4070+
struct hda_pcm_stream *hinfo = &pcm->stream[str];
40284071
struct snd_pcm_chmap *chmap;
40294072
const struct snd_pcm_chmap_elem *elem;
40304073

4031-
if (codec->pcm_info[i].own_chmap)
4074+
if (pcm->own_chmap)
40324075
continue;
40334076
if (!pcm || !hinfo->substreams)
40344077
continue;
40354078
elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps;
4036-
err = snd_pcm_add_chmap_ctls(pcm, str, elem,
4079+
err = snd_pcm_add_chmap_ctls(pcm->pcm, str, elem,
40374080
hinfo->channels_max,
40384081
0, &chmap);
40394082
if (err < 0)
@@ -4564,10 +4607,10 @@ static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type)
45644607
/* call build_pcms ops of the given codec and set up the default parameters */
45654608
int snd_hda_codec_parse_pcms(struct hda_codec *codec)
45664609
{
4567-
unsigned int pcm;
4610+
struct hda_pcm *cpcm;
45684611
int err;
45694612

4570-
if (codec->num_pcms)
4613+
if (!list_empty(&codec->pcm_list_head))
45714614
return 0; /* already parsed */
45724615

45734616
if (!codec->patch_ops.build_pcms)
@@ -4580,17 +4623,14 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec)
45804623
return err;
45814624
}
45824625

4583-
for (pcm = 0; pcm < codec->num_pcms; pcm++) {
4584-
struct hda_pcm *cpcm = &codec->pcm_info[pcm];
4626+
list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
45854627
int stream;
45864628

45874629
for (stream = 0; stream < 2; stream++) {
45884630
struct hda_pcm_stream *info = &cpcm->stream[stream];
45894631

45904632
if (!info->substreams)
45914633
continue;
4592-
if (snd_BUG_ON(!cpcm->name))
4593-
return -EINVAL;
45944634
err = set_pcm_default_values(codec, info);
45954635
if (err < 0) {
45964636
codec_warn(codec,
@@ -4608,7 +4648,7 @@ int snd_hda_codec_parse_pcms(struct hda_codec *codec)
46084648
int snd_hda_codec_build_pcms(struct hda_codec *codec)
46094649
{
46104650
struct hda_bus *bus = codec->bus;
4611-
unsigned int pcm;
4651+
struct hda_pcm *cpcm;
46124652
int dev, err;
46134653

46144654
if (snd_BUG_ON(!bus->ops.attach_pcm))
@@ -4621,9 +4661,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
46214661
}
46224662

46234663
/* attach a new PCM streams */
4624-
for (pcm = 0; pcm < codec->num_pcms; pcm++) {
4625-
struct hda_pcm *cpcm = &codec->pcm_info[pcm];
4626-
4664+
list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
46274665
if (cpcm->pcm)
46284666
continue; /* already attached */
46294667
if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
@@ -4651,11 +4689,9 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
46514689
*
46524690
* Create PCM information for each codec included in the bus.
46534691
*
4654-
* The build_pcms codec patch is requested to set up codec->num_pcms and
4655-
* codec->pcm_info properly. The array is referred by the top-level driver
4656-
* to create its PCM instances.
4657-
* The allocated codec->pcm_info should be released in codec->patch_ops.free
4658-
* callback.
4692+
* The build_pcms codec patch is requested to create and assign new
4693+
* hda_pcm objects. The codec is responsible to call snd_hda_codec_pcm_new()
4694+
* and fills the fields. Later they are instantiated by this function.
46594695
*
46604696
* At least, substreams, channels_min and channels_max must be filled for
46614697
* each stream. substreams = 0 indicates that the stream doesn't exist.

sound/pci/hda/hda_codec.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#ifndef __SOUND_HDA_CODEC_H
2222
#define __SOUND_HDA_CODEC_H
2323

24+
#include <linux/kref.h>
2425
#include <sound/info.h>
2526
#include <sound/control.h>
2627
#include <sound/pcm.h>
@@ -268,6 +269,10 @@ struct hda_pcm {
268269
int device; /* device number to assign */
269270
struct snd_pcm *pcm; /* assigned PCM instance */
270271
bool own_chmap; /* codec driver provides own channel maps */
272+
/* private: */
273+
struct hda_codec *codec;
274+
struct kref kref;
275+
struct list_head list;
271276
};
272277

273278
/* codec information */
@@ -301,8 +306,7 @@ struct hda_codec {
301306
struct hda_codec_ops patch_ops;
302307

303308
/* PCM to create, set by patch_ops.build_pcms callback */
304-
unsigned int num_pcms;
305-
struct hda_pcm *pcm_info;
309+
struct list_head pcm_list_head;
306310

307311
/* codec specific info */
308312
void *spec;
@@ -521,6 +525,16 @@ int snd_hda_build_pcms(struct hda_bus *bus);
521525
int snd_hda_codec_parse_pcms(struct hda_codec *codec);
522526
int snd_hda_codec_build_pcms(struct hda_codec *codec);
523527

528+
__printf(2, 3)
529+
struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec,
530+
const char *fmt, ...);
531+
532+
static inline void snd_hda_codec_pcm_get(struct hda_pcm *pcm)
533+
{
534+
kref_get(&pcm->kref);
535+
}
536+
void snd_hda_codec_pcm_put(struct hda_pcm *pcm);
537+
524538
int snd_hda_codec_prepare(struct hda_codec *codec,
525539
struct hda_pcm_stream *hinfo,
526540
unsigned int stream,

sound/pci/hda/hda_generic.c

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4644,7 +4644,7 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
46444644
err = snd_hda_create_dig_out_ctls(codec,
46454645
spec->multiout.dig_out_nid,
46464646
spec->multiout.dig_out_nid,
4647-
spec->pcm_rec[1].pcm_type);
4647+
spec->pcm_rec[1]->pcm_type);
46484648
if (err < 0)
46494649
return err;
46504650
if (!spec->no_analog) {
@@ -5115,20 +5115,20 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx,
51155115
int snd_hda_gen_build_pcms(struct hda_codec *codec)
51165116
{
51175117
struct hda_gen_spec *spec = codec->spec;
5118-
struct hda_pcm *info = spec->pcm_rec;
5118+
struct hda_pcm *info;
51195119
const struct hda_pcm_stream *p;
51205120
bool have_multi_adcs;
51215121

5122-
codec->num_pcms = 1;
5123-
codec->pcm_info = info;
5124-
51255122
if (spec->no_analog)
51265123
goto skip_analog;
51275124

51285125
fill_pcm_stream_name(spec->stream_name_analog,
51295126
sizeof(spec->stream_name_analog),
51305127
" Analog", codec->chip_name);
5131-
info->name = spec->stream_name_analog;
5128+
info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog);
5129+
if (!info)
5130+
return -ENOMEM;
5131+
spec->pcm_rec[0] = info;
51325132

51335133
if (spec->multiout.num_dacs > 0) {
51345134
p = spec->stream_analog_playback;
@@ -5161,10 +5161,12 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
51615161
fill_pcm_stream_name(spec->stream_name_digital,
51625162
sizeof(spec->stream_name_digital),
51635163
" Digital", codec->chip_name);
5164-
codec->num_pcms = 2;
5164+
info = snd_hda_codec_pcm_new(codec, "%s",
5165+
spec->stream_name_digital);
5166+
if (!info)
5167+
return -ENOMEM;
51655168
codec->slave_dig_outs = spec->multiout.slave_dig_outs;
5166-
info = spec->pcm_rec + 1;
5167-
info->name = spec->stream_name_digital;
5169+
spec->pcm_rec[1] = info;
51685170
if (spec->dig_out_type)
51695171
info->pcm_type = spec->dig_out_type;
51705172
else
@@ -5198,9 +5200,11 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
51985200
fill_pcm_stream_name(spec->stream_name_alt_analog,
51995201
sizeof(spec->stream_name_alt_analog),
52005202
" Alt Analog", codec->chip_name);
5201-
codec->num_pcms = 3;
5202-
info = spec->pcm_rec + 2;
5203-
info->name = spec->stream_name_alt_analog;
5203+
info = snd_hda_codec_pcm_new(codec, "%s",
5204+
spec->stream_name_alt_analog);
5205+
if (!info)
5206+
return -ENOMEM;
5207+
spec->pcm_rec[2] = info;
52045208
if (spec->alt_dac_nid) {
52055209
p = spec->stream_analog_alt_playback;
52065210
if (!p)

sound/pci/hda/hda_generic.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ struct hda_gen_spec {
144144
int const_channel_count; /* channel count for all */
145145

146146
/* PCM information */
147-
struct hda_pcm pcm_rec[3]; /* used in build_pcms() */
147+
struct hda_pcm *pcm_rec[3]; /* used in build_pcms() */
148148

149149
/* dynamic controls, init_verbs and input_mux */
150150
struct auto_pin_cfg autocfg;

sound/pci/hda/hda_proc.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,10 @@ static void print_nid_array(struct snd_info_buffer *buffer,
9999
static void print_nid_pcms(struct snd_info_buffer *buffer,
100100
struct hda_codec *codec, hda_nid_t nid)
101101
{
102-
int pcm, type;
102+
int type;
103103
struct hda_pcm *cpcm;
104-
for (pcm = 0; pcm < codec->num_pcms; pcm++) {
105-
cpcm = &codec->pcm_info[pcm];
104+
105+
list_for_each_entry(cpcm, &codec->pcm_list_head, list) {
106106
for (type = 0; type < 2; type++) {
107107
if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
108108
continue;

0 commit comments

Comments
 (0)