@@ -85,7 +85,6 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
8585 *
8686 */
8787
88- static DEFINE_RWLOCK (snd_pcm_link_rwlock );
8988static DECLARE_RWSEM (snd_pcm_link_rwsem );
9089
9190/* Writer in rwsem may block readers even during its waiting in queue,
@@ -105,8 +104,24 @@ void snd_pcm_group_init(struct snd_pcm_group *group)
105104 spin_lock_init (& group -> lock );
106105 mutex_init (& group -> mutex );
107106 INIT_LIST_HEAD (& group -> substreams );
107+ refcount_set (& group -> refs , 0 );
108108}
109109
110+ /* define group lock helpers */
111+ #define DEFINE_PCM_GROUP_LOCK (action , mutex_action ) \
112+ static void snd_pcm_group_ ## action(struct snd_pcm_group *group, bool nonatomic) \
113+ { \
114+ if (nonatomic) \
115+ mutex_ ## mutex_action(&group->mutex); \
116+ else \
117+ spin_ ## action(&group->lock); \
118+ }
119+
120+ DEFINE_PCM_GROUP_LOCK (lock , lock );
121+ DEFINE_PCM_GROUP_LOCK (unlock , unlock );
122+ DEFINE_PCM_GROUP_LOCK (lock_irq , lock );
123+ DEFINE_PCM_GROUP_LOCK (unlock_irq , unlock );
124+
110125#define PCM_LOCK_DEFAULT 0
111126#define PCM_LOCK_IRQ 1
112127#define PCM_LOCK_IRQSAVE 2
@@ -116,21 +131,19 @@ static unsigned long __snd_pcm_stream_lock_mode(struct snd_pcm_substream *substr
116131{
117132 unsigned long flags = 0 ;
118133 if (substream -> pcm -> nonatomic ) {
119- down_read_nested (& snd_pcm_link_rwsem , SINGLE_DEPTH_NESTING );
120134 mutex_lock (& substream -> self_group .mutex );
121135 } else {
122136 switch (mode ) {
123137 case PCM_LOCK_DEFAULT :
124- read_lock ( & snd_pcm_link_rwlock );
138+ spin_lock ( & substream -> self_group . lock );
125139 break ;
126140 case PCM_LOCK_IRQ :
127- read_lock_irq ( & snd_pcm_link_rwlock );
141+ spin_lock_irq ( & substream -> self_group . lock );
128142 break ;
129143 case PCM_LOCK_IRQSAVE :
130- read_lock_irqsave ( & snd_pcm_link_rwlock , flags );
144+ spin_lock_irqsave ( & substream -> self_group . lock , flags );
131145 break ;
132146 }
133- spin_lock (& substream -> self_group .lock );
134147 }
135148 return flags ;
136149}
@@ -140,19 +153,16 @@ static void __snd_pcm_stream_unlock_mode(struct snd_pcm_substream *substream,
140153{
141154 if (substream -> pcm -> nonatomic ) {
142155 mutex_unlock (& substream -> self_group .mutex );
143- up_read (& snd_pcm_link_rwsem );
144156 } else {
145- spin_unlock (& substream -> self_group .lock );
146-
147157 switch (mode ) {
148158 case PCM_LOCK_DEFAULT :
149- read_unlock ( & snd_pcm_link_rwlock );
159+ spin_unlock ( & substream -> self_group . lock );
150160 break ;
151161 case PCM_LOCK_IRQ :
152- read_unlock_irq ( & snd_pcm_link_rwlock );
162+ spin_unlock_irq ( & substream -> self_group . lock );
153163 break ;
154164 case PCM_LOCK_IRQSAVE :
155- read_unlock_irqrestore ( & snd_pcm_link_rwlock , flags );
165+ spin_unlock_irqrestore ( & substream -> self_group . lock , flags );
156166 break ;
157167 }
158168 }
@@ -1138,35 +1148,77 @@ static void snd_pcm_group_assign(struct snd_pcm_substream *substream,
11381148 list_move (& substream -> link_list , & new_group -> substreams );
11391149}
11401150
1151+ /*
1152+ * Unref and unlock the group, but keep the stream lock;
1153+ * when the group becomes empty and no longer referred, destroy itself
1154+ */
1155+ static void snd_pcm_group_unref (struct snd_pcm_group * group ,
1156+ struct snd_pcm_substream * substream )
1157+ {
1158+ bool do_free ;
1159+
1160+ if (!group )
1161+ return ;
1162+ do_free = refcount_dec_and_test (& group -> refs ) &&
1163+ list_empty (& group -> substreams );
1164+ snd_pcm_group_unlock (group , substream -> pcm -> nonatomic );
1165+ if (do_free )
1166+ kfree (group );
1167+ }
1168+
1169+ /*
1170+ * Lock the group inside a stream lock and reference it;
1171+ * return the locked group object, or NULL if not linked
1172+ */
1173+ static struct snd_pcm_group *
1174+ snd_pcm_stream_group_ref (struct snd_pcm_substream * substream )
1175+ {
1176+ bool nonatomic = substream -> pcm -> nonatomic ;
1177+ struct snd_pcm_group * group ;
1178+ bool trylock ;
1179+
1180+ for (;;) {
1181+ if (!snd_pcm_stream_linked (substream ))
1182+ return NULL ;
1183+ group = substream -> group ;
1184+ /* block freeing the group object */
1185+ refcount_inc (& group -> refs );
1186+
1187+ trylock = nonatomic ? mutex_trylock (& group -> mutex ) :
1188+ spin_trylock (& group -> lock );
1189+ if (trylock )
1190+ break ; /* OK */
1191+
1192+ /* re-lock for avoiding ABBA deadlock */
1193+ snd_pcm_stream_unlock (substream );
1194+ snd_pcm_group_lock (group , nonatomic );
1195+ snd_pcm_stream_lock (substream );
1196+
1197+ /* check the group again; the above opens a small race window */
1198+ if (substream -> group == group )
1199+ break ; /* OK */
1200+ /* group changed, try again */
1201+ snd_pcm_group_unref (group , substream );
1202+ }
1203+ return group ;
1204+ }
1205+
11411206/*
11421207 * Note: call with stream lock
11431208 */
11441209static int snd_pcm_action (const struct action_ops * ops ,
11451210 struct snd_pcm_substream * substream ,
11461211 int state )
11471212{
1213+ struct snd_pcm_group * group ;
11481214 int res ;
11491215
1150- if (!snd_pcm_stream_linked (substream ))
1151- return snd_pcm_action_single (ops , substream , state );
1152-
1153- if (substream -> pcm -> nonatomic ) {
1154- if (!mutex_trylock (& substream -> group -> mutex )) {
1155- mutex_unlock (& substream -> self_group .mutex );
1156- mutex_lock (& substream -> group -> mutex );
1157- mutex_lock (& substream -> self_group .mutex );
1158- }
1216+ group = snd_pcm_stream_group_ref (substream );
1217+ if (group )
11591218 res = snd_pcm_action_group (ops , substream , state , 1 );
1160- mutex_unlock (& substream -> group -> mutex );
1161- } else {
1162- if (!spin_trylock (& substream -> group -> lock )) {
1163- spin_unlock (& substream -> self_group .lock );
1164- spin_lock (& substream -> group -> lock );
1165- spin_lock (& substream -> self_group .lock );
1166- }
1167- res = snd_pcm_action_group (ops , substream , state , 1 );
1168- spin_unlock (& substream -> group -> lock );
1169- }
1219+ else
1220+ res = snd_pcm_action_single (ops , substream , state );
1221+ snd_pcm_group_unref (group , substream );
11701222 return res ;
11711223}
11721224
@@ -1193,6 +1245,7 @@ static int snd_pcm_action_nonatomic(const struct action_ops *ops,
11931245{
11941246 int res ;
11951247
1248+ /* Guarantee the group members won't change during non-atomic action */
11961249 down_read (& snd_pcm_link_rwsem );
11971250 if (snd_pcm_stream_linked (substream ))
11981251 res = snd_pcm_action_group (ops , substream , state , 0 );
@@ -1821,6 +1874,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
18211874 struct snd_card * card ;
18221875 struct snd_pcm_runtime * runtime ;
18231876 struct snd_pcm_substream * s ;
1877+ struct snd_pcm_group * group ;
18241878 wait_queue_entry_t wait ;
18251879 int result = 0 ;
18261880 int nonblock = 0 ;
@@ -1837,7 +1891,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
18371891 } else if (substream -> f_flags & O_NONBLOCK )
18381892 nonblock = 1 ;
18391893
1840- down_read (& snd_pcm_link_rwsem );
18411894 snd_pcm_stream_lock_irq (substream );
18421895 /* resume pause */
18431896 if (runtime -> status -> state == SNDRV_PCM_STATE_PAUSED )
@@ -1862,6 +1915,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
18621915 }
18631916 /* find a substream to drain */
18641917 to_check = NULL ;
1918+ group = snd_pcm_stream_group_ref (substream );
18651919 snd_pcm_group_for_each_entry (s , substream ) {
18661920 if (s -> stream != SNDRV_PCM_STREAM_PLAYBACK )
18671921 continue ;
@@ -1871,12 +1925,12 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
18711925 break ;
18721926 }
18731927 }
1928+ snd_pcm_group_unref (group , substream );
18741929 if (!to_check )
18751930 break ; /* all drained */
18761931 init_waitqueue_entry (& wait , current );
18771932 add_wait_queue (& to_check -> sleep , & wait );
18781933 snd_pcm_stream_unlock_irq (substream );
1879- up_read (& snd_pcm_link_rwsem );
18801934 if (runtime -> no_period_wakeup )
18811935 tout = MAX_SCHEDULE_TIMEOUT ;
18821936 else {
@@ -1888,9 +1942,17 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
18881942 tout = msecs_to_jiffies (tout * 1000 );
18891943 }
18901944 tout = schedule_timeout_interruptible (tout );
1891- down_read ( & snd_pcm_link_rwsem );
1945+
18921946 snd_pcm_stream_lock_irq (substream );
1893- remove_wait_queue (& to_check -> sleep , & wait );
1947+ group = snd_pcm_stream_group_ref (substream );
1948+ snd_pcm_group_for_each_entry (s , substream ) {
1949+ if (s -> runtime == to_check ) {
1950+ remove_wait_queue (& to_check -> sleep , & wait );
1951+ break ;
1952+ }
1953+ }
1954+ snd_pcm_group_unref (group , substream );
1955+
18941956 if (card -> shutdown ) {
18951957 result = - ENODEV ;
18961958 break ;
@@ -1910,7 +1972,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
19101972
19111973 unlock :
19121974 snd_pcm_stream_unlock_irq (substream );
1913- up_read (& snd_pcm_link_rwsem );
19141975
19151976 return result ;
19161977}
@@ -1972,7 +2033,8 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
19722033 int res = 0 ;
19732034 struct snd_pcm_file * pcm_file ;
19742035 struct snd_pcm_substream * substream1 ;
1975- struct snd_pcm_group * group ;
2036+ struct snd_pcm_group * group , * target_group ;
2037+ bool nonatomic = substream -> pcm -> nonatomic ;
19762038 struct fd f = fdget (fd );
19772039
19782040 if (!f .file )
@@ -1989,8 +2051,8 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
19892051 goto _nolock ;
19902052 }
19912053 snd_pcm_group_init (group );
2054+
19922055 down_write_nonfifo (& snd_pcm_link_rwsem );
1993- write_lock_irq (& snd_pcm_link_rwlock );
19942056 if (substream -> runtime -> status -> state == SNDRV_PCM_STATE_OPEN ||
19952057 substream -> runtime -> status -> state != substream1 -> runtime -> status -> state ||
19962058 substream -> pcm -> nonatomic != substream1 -> pcm -> nonatomic ) {
@@ -2001,13 +2063,21 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
20012063 res = - EALREADY ;
20022064 goto _end ;
20032065 }
2066+
2067+ snd_pcm_stream_lock_irq (substream );
20042068 if (!snd_pcm_stream_linked (substream )) {
20052069 snd_pcm_group_assign (substream , group );
2006- group = NULL ;
2070+ group = NULL ; /* assigned, don't free this one below */
20072071 }
2008- snd_pcm_group_assign (substream1 , substream -> group );
2072+ target_group = substream -> group ;
2073+ snd_pcm_stream_unlock_irq (substream );
2074+
2075+ snd_pcm_group_lock_irq (target_group , nonatomic );
2076+ snd_pcm_stream_lock (substream1 );
2077+ snd_pcm_group_assign (substream1 , target_group );
2078+ snd_pcm_stream_unlock (substream1 );
2079+ snd_pcm_group_unlock_irq (target_group , nonatomic );
20092080 _end :
2010- write_unlock_irq (& snd_pcm_link_rwlock );
20112081 up_write (& snd_pcm_link_rwsem );
20122082 _nolock :
20132083 kfree (group );
@@ -2018,22 +2088,27 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
20182088
20192089static void relink_to_local (struct snd_pcm_substream * substream )
20202090{
2091+ snd_pcm_stream_lock (substream );
20212092 snd_pcm_group_assign (substream , & substream -> self_group );
2093+ snd_pcm_stream_unlock (substream );
20222094}
20232095
20242096static int snd_pcm_unlink (struct snd_pcm_substream * substream )
20252097{
20262098 struct snd_pcm_group * group ;
2099+ bool nonatomic = substream -> pcm -> nonatomic ;
2100+ bool do_free = false;
20272101 int res = 0 ;
20282102
20292103 down_write_nonfifo (& snd_pcm_link_rwsem );
2030- write_lock_irq ( & snd_pcm_link_rwlock );
2104+
20312105 if (!snd_pcm_stream_linked (substream )) {
20322106 res = - EALREADY ;
20332107 goto _end ;
20342108 }
20352109
20362110 group = substream -> group ;
2111+ snd_pcm_group_lock_irq (group , nonatomic );
20372112
20382113 relink_to_local (substream );
20392114
@@ -2042,11 +2117,14 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream)
20422117 relink_to_local (list_first_entry (& group -> substreams ,
20432118 struct snd_pcm_substream ,
20442119 link_list ));
2045- kfree ( group );
2120+ do_free = ! refcount_read ( & group -> refs );
20462121 }
20472122
2123+ snd_pcm_group_unlock_irq (group , nonatomic );
2124+ if (do_free )
2125+ kfree (group );
2126+
20482127 _end :
2049- write_unlock_irq (& snd_pcm_link_rwlock );
20502128 up_write (& snd_pcm_link_rwsem );
20512129 return res ;
20522130}
0 commit comments