From nobody Wed Sep 30 10:00:52 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1962B3033DE; Mon, 10 Aug 2026 13:37:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786369044; cv=none; b=tPvMF5L7/BYk4rceyQRIgvwFJH1lqG80b5Ksw6zGMTrsWdl8GB97QHFNnamY82FKaUP2BZug64dHA4Ha5CteL1FUyIxHJ/X9c0T0iYFOE5M96tr985JzVuXOMgVfOp+KaTqA8zajpQStbaxcuFfSGLrHLcUq81nrYyLvLPCU59E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786369044; c=relaxed/simple; bh=M3h8HxzXdzNoOY9kcoDJ0w2zRo3xtKjRROoBYkJV87Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=t4dRc1WLtvutoLtiVIfa4Vs14kwBpokba96LjdzpbWdSA7B+Bw9HmU0m/ju3wiJblrt9BWNv2/7KfvRAx5NPwQ51IKYbnxv5Fz+JSHeajjqZkVwgdFo6ryMsi9H0jHkKBFKGGoFjhbPPrx+uVVHiVxDhG6Z+SKhdRlWb4p8Q2Y8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=suse.de; spf=pass smtp.mailfrom=suse.de; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b=jvObYTQ3; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b=ESropR7T; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b=PT1LTMpc; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b=XgIM55Sv; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=suse.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b="jvObYTQ3"; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b="ESropR7T"; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b="PT1LTMpc"; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b="XgIM55Sv" Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 0497E3E10; Mon, 10 Aug 2026 13:37:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1786369037; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=gc5K4lTRZvylPSjIeNywj88GTWlk2PS2z7nR+anATL4=; b=jvObYTQ3ZeLkZ78dbWR+qz9jdI16fSzUqQlTiRBuvf7sk8uI77k7WShJcFLhIVCCQlaJes uDYFSKABRQbwS+SKFJqDZngcWb6eUFNbBePcKLU1d9fVydBBLDVAwlv92l4ZL52vuxdbx7 3tcgn5PXJYrMGT7uJhdtrjf/WyO+rAc= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1786369037; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=gc5K4lTRZvylPSjIeNywj88GTWlk2PS2z7nR+anATL4=; b=ESropR7TelEBzNWyByYyHsN+NuJsa3yHZ3mcI5UQdjbZNj7mU3rtOIm903dQa/JpihHT5e ZRcAcchOBHG4KkDQ== Authentication-Results: smtp-out2.suse.de; dkim=pass header.d=suse.de header.s=susede2_rsa header.b=PT1LTMpc; dkim=pass header.d=suse.de header.s=susede2_ed25519 header.b=XgIM55Sv DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1786369033; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=gc5K4lTRZvylPSjIeNywj88GTWlk2PS2z7nR+anATL4=; b=PT1LTMpcWS51JLEbaRVtThLQw0qdVwizfT2Taa7Ms+35DlMuv3Kck/Q7TOigNUNO9NxWRz X9cp/IAniJHSsPADPKDw8btTReS5WqynWw/aKRJs9bjTXi59tDMPmPyoIweUYji56r79IY S/gZWClTsRaAzbOyh+1Zeb3kcjSv5iA= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1786369033; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=gc5K4lTRZvylPSjIeNywj88GTWlk2PS2z7nR+anATL4=; b=XgIM55SvN9OAm3C4MljXr/PzQtFibg5PRBN+Jk26c4W+bMSV2X02zAur6v6QdjOP9LLdlq g1lrrLu5I3X0E+Bg== Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id D7443779B6; Mon, 10 Aug 2026 13:37:12 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id 4ARMMwjUeWq3HwAAD6G6ig (envelope-from ); Mon, 10 Aug 2026 13:37:12 +0000 From: Takashi Iwai To: linux-sound@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [PATCH 1/5] ALSA: seq: Use RCU for the port subscriber list Date: Mon, 10 Aug 2026 15:37:02 +0200 Message-ID: <20260810133711.42483-2-tiwai@suse.de> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260810133711.42483-1-tiwai@suse.de> References: <20260810133711.42483-1-tiwai@suse.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spam-Score: -3.01 X-Spam-Level: X-Rspamd-Action: no action X-Rspamd-Queue-Id: 0497E3E10 X-Spamd-Result: default: False [-3.01 / 50.00]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; R_DKIM_ALLOW(-0.20)[suse.de:s=susede2_rsa,suse.de:s=susede2_ed25519]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; MX_GOOD(-0.01)[]; ARC_NA(0.00)[]; MIME_TRACE(0.00)[0:+]; DBL_BLOCKED_OPENRESOLVER(0.00)[imap1.dmz-prg2.suse.org:helo,imap1.dmz-prg2.suse.org:rdns,suse.de:mid,suse.de:email,suse.de:dkim]; RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[2a07:de40:b281:106:10:150:64:167:received]; RCVD_VIA_SMTP_AUTH(0.00)[]; RCPT_COUNT_TWO(0.00)[2]; DKIM_SIGNED(0.00)[suse.de:s=susede2_rsa,suse.de:s=susede2_ed25519]; FROM_EQ_ENVFROM(0.00)[]; FROM_HAS_DN(0.00)[]; DNSWL_BLOCKED(0.00)[2a07:de40:b281:104:10:150:64:97:from,2a07:de40:b281:106:10:150:64:167:received]; RCVD_TLS_ALL(0.00)[]; TO_DN_NONE(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; TO_MATCH_ENVRCPT_ALL(0.00)[]; DKIM_TRACE(0.00)[suse.de:+] X-Rspamd-Server: rspamd1.dmz-prg2.suse.org X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" Each sequencer port keeps two subscriber groups (c_src and c_dest), each protected by both an rwlock (list_lock) and a rw_semaphore (list_mutex). The rwlock is taken read-side in the event delivery hot path (__deliver_to_subscribers()) for delivering every event to subscribers, while the mutex serializes subscribe/unsubscribe and covers the sleepable delivery and query walks. Subscriptions change rarely but delivery happens constantly, so this is a textbook read-mostly case. Convert the subscriber list traversal to RCU and drop the rwlock entirely while keeping the existing list_mutex for serializing the writers. The atomic delivery path now runs lock-free under rcu_read_lock() instead of contending on the shared rwlock. Along with the conversion to RCU, the subscriber lists are switched from list_head to hlist so that removal can use hlist_del_init_rcu(): it keeps the ->next pointer intact for concurrent readers while clearing ->pprev, which lets the double-deletion guard (added in commit 13d5e5d4725c) keep detecting an already-removed entry via hlist_unhashed(). Dropping write_lock_irq() from the writers is safe: no writer runs in atomic/IRQ context, and the sole atomic reader now uses RCU, which is IRQ-safe. Port lifetime handling (use_lock/closing drain in port_delete()) is orthogonal and unchanged. Note that the conversion to RCU has another merit: it automatically "fixes" the (rather false) lockdep warnings for the doubly read-locks of the same subscriber list, too. Signed-off-by: Takashi Iwai --- sound/core/seq/seq_clientmgr.c | 25 ++++++++-------- sound/core/seq/seq_ports.c | 52 ++++++++++++++++------------------ sound/core/seq/seq_ports.h | 8 +++--- 3 files changed, 41 insertions(+), 44 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 23ec239640c3..77f5020f1873 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -717,10 +717,11 @@ static int __deliver_to_subscribers(struct snd_seq_cl= ient *client, =09 /* lock list */ if (atomic) - read_lock(&grp->list_lock); + rcu_read_lock(); else down_read_nested(&grp->list_mutex, hop); - list_for_each_entry(subs, &grp->list_head, src_list) { + hlist_for_each_entry_rcu(subs, &grp->list_head, src_list, + lockdep_is_held(&grp->list_mutex)) { /* both ports ready? */ if (atomic_read(&subs->ref_count) !=3D 2) continue; @@ -741,7 +742,7 @@ static int __deliver_to_subscribers(struct snd_seq_clie= nt *client, memcpy(event, &event_saved, saved_size); } if (atomic) - read_unlock(&grp->list_lock); + rcu_read_unlock(); else up_read(&grp->list_mutex); memcpy(event, &event_saved, saved_size); @@ -1938,7 +1939,7 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_cl= ient *client, void *arg) { struct snd_seq_query_subs *subs =3D arg; struct snd_seq_port_subs_info *group; - struct list_head *p; + struct hlist_node *p; int i; =20 struct snd_seq_client *cptr __free(snd_seq_client) =3D @@ -1965,15 +1966,15 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_= client *client, void *arg) /* search for the subscriber */ subs->num_subs =3D group->count; i =3D 0; - list_for_each(p, &group->list_head) { + hlist_for_each(p, &group->list_head) { if (i++ =3D=3D subs->index) { /* found! */ struct snd_seq_subscribers *s; if (subs->type =3D=3D SNDRV_SEQ_QUERY_SUBS_READ) { - s =3D list_entry(p, struct snd_seq_subscribers, src_list); + s =3D hlist_entry(p, struct snd_seq_subscribers, src_list); subs->addr =3D s->info.dest; } else { - s =3D list_entry(p, struct snd_seq_subscribers, dest_list); + s =3D hlist_entry(p, struct snd_seq_subscribers, dest_list); subs->addr =3D s->info.sender; } subs->flags =3D s->info.flags; @@ -2529,19 +2530,19 @@ static void snd_seq_info_dump_subscribers(struct sn= d_info_buffer *buffer, struct snd_seq_port_subs_info *group, int is_src, char *msg) { - struct list_head *p; + struct hlist_node *p; struct snd_seq_subscribers *s; int count =3D 0; =20 guard(rwsem_read)(&group->list_mutex); - if (list_empty(&group->list_head)) + if (hlist_empty(&group->list_head)) return; snd_iprintf(buffer, msg); - list_for_each(p, &group->list_head) { + hlist_for_each(p, &group->list_head) { if (is_src) - s =3D list_entry(p, struct snd_seq_subscribers, src_list); + s =3D hlist_entry(p, struct snd_seq_subscribers, src_list); else - s =3D list_entry(p, struct snd_seq_subscribers, dest_list); + s =3D hlist_entry(p, struct snd_seq_subscribers, dest_list); if (count++) snd_iprintf(buffer, ", "); snd_iprintf(buffer, "%d:%d", diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 6612e92d801f..357c72ed0d3b 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -98,10 +98,9 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(s= truct snd_seq_client *cl /* initialize snd_seq_port_subs_info */ static void port_subs_info_init(struct snd_seq_port_subs_info *grp) { - INIT_LIST_HEAD(&grp->list_head); + INIT_HLIST_HEAD(&grp->list_head); grp->count =3D 0; grp->exclusive =3D 0; - rwlock_init(&grp->list_lock); init_rwsem(&grp->list_mutex); grp->open =3D NULL; grp->close =3D NULL; @@ -202,12 +201,12 @@ static void delete_and_unsubscribe_port(struct snd_se= q_client *client, bool is_src, bool ack); =20 static inline struct snd_seq_subscribers * -get_subscriber(struct list_head *p, bool is_src) +get_subscriber(struct hlist_node *p, bool is_src) { if (is_src) - return list_entry(p, struct snd_seq_subscribers, src_list); + return hlist_entry(p, struct snd_seq_subscribers, src_list); else - return list_entry(p, struct snd_seq_subscribers, dest_list); + return hlist_entry(p, struct snd_seq_subscribers, dest_list); } =20 /* @@ -219,9 +218,9 @@ static void clear_subscriber_list(struct snd_seq_client= *client, struct snd_seq_port_subs_info *grp, int is_src) { - struct list_head *p, *n; + struct hlist_node *p, *n; =20 - list_for_each_safe(p, n, &grp->list_head) { + hlist_for_each_safe(p, n, &grp->list_head) { struct snd_seq_subscribers *subs; =20 subs =3D get_subscriber(p, is_src); @@ -238,13 +237,13 @@ static void clear_subscriber_list(struct snd_seq_clie= nt *client, * remove the subscriber info */ if (atomic_dec_and_test(&subs->ref_count)) - kfree(subs); + kfree_rcu(subs, rcu); continue; } =20 /* ok we got the connected port */ delete_and_unsubscribe_port(c, aport, subs, !is_src, true); - kfree(subs); + kfree_rcu(subs, rcu); } } =20 @@ -499,20 +498,20 @@ static int check_and_subscribe_port(struct snd_seq_cl= ient *client, bool is_src, bool exclusive, bool ack) { struct snd_seq_port_subs_info *grp; - struct list_head *p; + struct hlist_node *p; struct snd_seq_subscribers *s; int err; =20 grp =3D is_src ? &port->c_src : &port->c_dest; guard(rwsem_write)(&grp->list_mutex); if (exclusive) { - if (!list_empty(&grp->list_head)) + if (!hlist_empty(&grp->list_head)) return -EBUSY; } else { if (grp->exclusive) return -EBUSY; /* check whether already exists */ - list_for_each(p, &grp->list_head) { + hlist_for_each(p, &grp->list_head) { s =3D get_subscriber(p, is_src); if (match_subs_info(&subs->info, &s->info)) return -EBUSY; @@ -526,11 +525,10 @@ static int check_and_subscribe_port(struct snd_seq_cl= ient *client, } =20 /* add to list */ - guard(write_lock_irq)(&grp->list_lock); if (is_src) - list_add_tail(&subs->src_list, &grp->list_head); + hlist_add_tail_rcu(&subs->src_list, &grp->list_head); else - list_add_tail(&subs->dest_list, &grp->list_head); + hlist_add_tail_rcu(&subs->dest_list, &grp->list_head); grp->exclusive =3D exclusive; atomic_inc(&subs->ref_count); =20 @@ -544,17 +542,15 @@ static void __delete_and_unsubscribe_port(struct snd_= seq_client *client, bool is_src, bool ack) { struct snd_seq_port_subs_info *grp; - struct list_head *list; + struct hlist_node *list; bool empty; =20 grp =3D is_src ? &port->c_src : &port->c_dest; list =3D is_src ? &subs->src_list : &subs->dest_list; - scoped_guard(write_lock_irq, &grp->list_lock) { - empty =3D list_empty(list); - if (!empty) - list_del_init(list); - grp->exclusive =3D 0; - } + empty =3D hlist_unhashed(list); + if (!empty) + hlist_del_init_rcu(list); + grp->exclusive =3D 0; =20 if (!empty) unsubscribe_port(client, port, grp, &subs->info, ack); @@ -590,8 +586,8 @@ int snd_seq_port_connect(struct snd_seq_client *connect= or, =20 subs->info =3D *info; atomic_set(&subs->ref_count, 0); - INIT_LIST_HEAD(&subs->src_list); - INIT_LIST_HEAD(&subs->dest_list); + INIT_HLIST_NODE(&subs->src_list); + INIT_HLIST_NODE(&subs->dest_list); =20 exclusive =3D !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE); =20 @@ -612,7 +608,7 @@ int snd_seq_port_connect(struct snd_seq_client *connect= or, delete_and_unsubscribe_port(src_client, src_port, subs, true, connector->number !=3D src_client->number); error: - kfree(subs); + kfree_rcu(subs, rcu); return err; } =20 @@ -633,7 +629,7 @@ int snd_seq_port_disconnect(struct snd_seq_client *conn= ector, */ scoped_guard(rwsem_write, &dest->list_mutex) { /* look for the connection */ - list_for_each_entry(subs, &dest->list_head, dest_list) { + hlist_for_each_entry(subs, &dest->list_head, dest_list) { if (match_subs_info(info, &subs->info)) { __delete_and_unsubscribe_port(dest_client, dest_port, subs, false, @@ -648,7 +644,7 @@ int snd_seq_port_disconnect(struct snd_seq_client *conn= ector, =20 delete_and_unsubscribe_port(src_client, src_port, subs, true, connector->number !=3D src_client->number); - kfree(subs); + kfree_rcu(subs, rcu); return 0; } =20 @@ -662,7 +658,7 @@ int snd_seq_port_get_subscription(struct snd_seq_port_s= ubs_info *src_grp, int err =3D -ENOENT; =20 guard(rwsem_read)(&src_grp->list_mutex); - list_for_each_entry(s, &src_grp->list_head, src_list) { + hlist_for_each_entry(s, &src_grp->list_head, src_list) { if (addr_match(dest_addr, &s->info.dest)) { *subs =3D s->info; err =3D 0; diff --git a/sound/core/seq/seq_ports.h b/sound/core/seq/seq_ports.h index b689c0f4867c..12ad86bf1489 100644 --- a/sound/core/seq/seq_ports.h +++ b/sound/core/seq/seq_ports.h @@ -28,17 +28,17 @@ =20 struct snd_seq_subscribers { struct snd_seq_port_subscribe info; /* additional info */ - struct list_head src_list; /* link of sources */ - struct list_head dest_list; /* link of destinations */ + struct hlist_node src_list; /* link of sources */ + struct hlist_node dest_list; /* link of destinations */ atomic_t ref_count; + struct rcu_head rcu; /* for deferred free */ }; =20 struct snd_seq_port_subs_info { - struct list_head list_head; /* list of subscribed ports */ + struct hlist_head list_head; /* list of subscribed ports */ unsigned int count; /* count of subscribers */ unsigned int exclusive: 1; /* exclusive mode */ struct rw_semaphore list_mutex; - rwlock_t list_lock; int (*open)(void *private_data, struct snd_seq_port_subscribe *info); int (*close)(void *private_data, struct snd_seq_port_subscribe *info); }; --=20 2.55.0 From nobody Wed Sep 30 10:00:52 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0BA902DC32C; Mon, 10 Aug 2026 13:37:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786369044; cv=none; b=ldHffdLmA5C3L7rvpblHIW8Rq1AUHuHh0PBBA1GnvFoIA9SzkAZM2wOaLrchl8cgIfFNeUn/9LMvZ0e9Zfq4XjxRsCDm26wVFmq06Q8X2iap7kSSPJQh0IMyB6oqbSAXQXMax2fjXUmCgixITkqE5yugStVRcJfomGbjolRf/FA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786369044; c=relaxed/simple; bh=TZDVHc5j4+oyWvqOscD4LQTMs7IfcqdEtCY8hm+zNPM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YEgeBmNhtqzQyYbzw/jSXY68U1+nnF3osZl9NyLRSufVbk0t+2AYAfkz5ekgbPtZhgpKBzuWtQA/WSnpfbRUx6eeyuPTYwBntPkVZoa5sy+HTJV3tAu0qij6iso+vIvj+Vsx9ZfoZzJG/pBKdRDXIhNeB84ny2RQCa3oaVq5AkE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=suse.de; spf=pass smtp.mailfrom=suse.de; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b=FHADlORP; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b=cJHf829u; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b=FuWqW9Ee; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b=+XvZukvf; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=suse.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b="FHADlORP"; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b="cJHf829u"; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b="FuWqW9Ee"; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b="+XvZukvf" Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 2911D3E09; Mon, 10 Aug 2026 13:37:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1786369037; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sv77p15HyUuGr7x04M+yNI5E14CsPjoA/vBDic7aPMg=; b=FHADlORP4VPqED8npve2L4ZP7LFQj5APejoCLts3dxSR/qiL6/wjIY67PoiWD49iVfxtXz RfzYeunWztF7ggsnfLmBw8kzZWml3Zb63xCNia5/ZL93aNlKlgZrkBoLp4BuA3wv3LTMSg VVtSNkn0sUQP89qx9QsxN21GqXM3MVo= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1786369037; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sv77p15HyUuGr7x04M+yNI5E14CsPjoA/vBDic7aPMg=; b=cJHf829uwdOjG5QbsEfB+sHw9HxFm3RjBp1iPHU0mpRTlGlZ+bh33Rbi1Uu92JQj6B2cO/ H6SEcjX7Ckz765Cg== Authentication-Results: smtp-out2.suse.de; dkim=pass header.d=suse.de header.s=susede2_rsa header.b=FuWqW9Ee; dkim=pass header.d=suse.de header.s=susede2_ed25519 header.b=+XvZukvf DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1786369033; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sv77p15HyUuGr7x04M+yNI5E14CsPjoA/vBDic7aPMg=; b=FuWqW9EenPYDZamQ6whexq710v3kylf/kSsRRVUvInkqODTiANvn8WOIz248kyCwK6JQVt AxmWwfUQY5LbTtZZ8xzozYeVuSf1ogcXP5zVhKlwE5J4rhPa0TDcG9O/DI7ph9og50YYmE NXMY9Cs7+62dODDqIFNk52OR5PIiE6Y= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1786369033; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sv77p15HyUuGr7x04M+yNI5E14CsPjoA/vBDic7aPMg=; b=+XvZukvfGQ2rSLQ8F2eokIOrFW2XSE/ZkISHS8CcHb2dotC2kDVB9FCWM47uVo27NWaNei jG6hjKQvcbIuPsDA== Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 07B49779BC; Mon, 10 Aug 2026 13:37:13 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id 0IimAAnUeWq3HwAAD6G6ig (envelope-from ); Mon, 10 Aug 2026 13:37:13 +0000 From: Takashi Iwai To: linux-sound@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [PATCH 2/5] ALSA: seq: Use RCU for the client port list Date: Mon, 10 Aug 2026 15:37:03 +0200 Message-ID: <20260810133711.42483-3-tiwai@suse.de> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260810133711.42483-1-tiwai@suse.de> References: <20260810133711.42483-1-tiwai@suse.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spamd-Result: default: False [-3.01 / 50.00]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; R_DKIM_ALLOW(-0.20)[suse.de:s=susede2_rsa,suse.de:s=susede2_ed25519]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; MX_GOOD(-0.01)[]; ARC_NA(0.00)[]; RCVD_VIA_SMTP_AUTH(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; TO_DN_NONE(0.00)[]; MIME_TRACE(0.00)[0:+]; FROM_HAS_DN(0.00)[]; TO_MATCH_ENVRCPT_ALL(0.00)[]; DNSWL_BLOCKED(0.00)[2a07:de40:b281:106:10:150:64:167:received,2a07:de40:b281:104:10:150:64:97:from]; RCVD_TLS_ALL(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.de:email,suse.de:mid,suse.de:dkim,imap1.dmz-prg2.suse.org:rdns,imap1.dmz-prg2.suse.org:helo]; DKIM_SIGNED(0.00)[suse.de:s=susede2_rsa,suse.de:s=susede2_ed25519]; RCPT_COUNT_TWO(0.00)[2]; DKIM_TRACE(0.00)[suse.de:+] X-Spam-Flag: NO X-Spam-Score: -3.01 X-Spam-Level: X-Rspamd-Queue-Id: 2911D3E09 X-Rspamd-Server: rspamd2.dmz-prg2.suse.org X-Rspamd-Action: no action Content-Type: text/plain; charset="utf-8" Each sequencer client keeps a list of its ports (ports_list_head) protected by both an rwlock (ports_lock) and a mutex (ports_mutex). The rwlock is taken read-side on the event delivery hot path: snd_seq_port_use_ptr() walks the list to resolve a port on every dispatched event, while the mutex serializes port creation/deletion. Ports change rarely but delivery happens constantly, so this is the another read-mostly case as the port subscriber list. Convert the port list traversal to RCU and drop the rwlock entirely; the existing ports_mutex keeps serializing the writers. The atomic delivery path (snd_seq_port_use_ptr(), snd_seq_port_query_nearest()) now runs lock-free under rcu_read_lock() instead of contending on the shared rwlock. The writers switch to list_add_tail_rcu()/list_del_rcu(). snd_seq_insert_port() now stores the port number and name before publishing the node so RCU readers only ever observe a fully initialized port. One drawback is that snd_seq_delete_all_ports() drops the O(1) splice trick and unlinks each port individually, though: the splice repointed the last port's ->next away from the list head, which would send a concurrent lockless reader off the end of the list. Unlike the subscriber objects, ports are not freed via kfree_rcu(): port_delete() must drain outstanding use_lock references (and run private_free()) synchronously. The rwlock previously guaranteed that no reader could take a new use_lock reference once the port was unlinked -- list_del under write_lock excluded snd_use_lock_use() under read_lock. list_del_rcu() offers no such exclusion, so a reader still traversing the list can grab a reference after the unlink. port_delete() therefore calls synchronize_rcu() after the port has been unlinked and before snd_use_lock_sync(): once the grace period elapses no new reference can appear, and the existing drain then frees the port safely. Dropping write_lock_irq() from the writers is safe: no writer runs in atomic/IRQ context, and the sole atomic reader now uses RCU, which is IRQ-safe. Signed-off-by: Takashi Iwai --- sound/core/seq/seq_clientmgr.c | 1 - sound/core/seq/seq_clientmgr.h | 1 - sound/core/seq/seq_ports.c | 51 +++++++++++++++------------------- 3 files changed, 23 insertions(+), 30 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 77f5020f1873..b7cf14e3ddb3 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -212,7 +212,6 @@ static struct snd_seq_client *seq_create_client1(int cl= ient_index, int poolsize) } client->type =3D NO_CLIENT; snd_use_lock_init(&client->use_lock); - rwlock_init(&client->ports_lock); mutex_init(&client->ports_mutex); INIT_LIST_HEAD(&client->ports_list_head); mutex_init(&client->ioctl_mutex); diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h index feea8bb7d987..d7ffc5c1ed61 100644 --- a/sound/core/seq/seq_clientmgr.h +++ b/sound/core/seq/seq_clientmgr.h @@ -49,7 +49,6 @@ struct snd_seq_client { /* ports */ int num_ports; /* number of ports */ struct list_head ports_list_head; - rwlock_t ports_lock; struct mutex ports_mutex; struct mutex ioctl_mutex; int convert32; /* convert 32->64bit */ diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 357c72ed0d3b..eb67eb0eeb14 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -48,8 +48,8 @@ struct snd_seq_client_port *snd_seq_port_use_ptr(struct s= nd_seq_client *client, =20 if (client =3D=3D NULL) return NULL; - guard(read_lock)(&client->ports_lock); - list_for_each_entry(port, &client->ports_list_head, list) { + guard(rcu)(); + list_for_each_entry_rcu(port, &client->ports_list_head, list) { if (port->addr.port =3D=3D num) { if (port->closing) break; /* deleting now */ @@ -71,8 +71,8 @@ struct snd_seq_client_port *snd_seq_port_query_nearest(st= ruct snd_seq_client *cl =20 num =3D pinfo->addr.port; found =3D NULL; - guard(read_lock)(&client->ports_lock); - list_for_each_entry(port, &client->ports_list_head, list) { + guard(rcu)(); + list_for_each_entry_rcu(port, &client->ports_list_head, list) { if ((port->capability & SNDRV_SEQ_PORT_CAP_INACTIVE) && !check_inactive) continue; /* skip inactive ports */ @@ -153,7 +153,6 @@ int snd_seq_insert_port(struct snd_seq_client *client, = int port, =20 num =3D max(port, 0); guard(mutex)(&client->ports_mutex); - guard(write_lock_irq)(&client->ports_lock); struct list_head *insert_before =3D &client->ports_list_head; list_for_each_entry(p, &client->ports_list_head, list) { if (p->addr.port =3D=3D port) @@ -165,12 +164,13 @@ int snd_seq_insert_port(struct snd_seq_client *client= , int port, if (port < 0) /* auto-probe mode */ num =3D p->addr.port + 1; } - /* insert the new port */ - list_add_tail(&new_port->list, insert_before); - client->num_ports++; + /* finish initializing the port before publishing it to RCU readers */ new_port->addr.port =3D num; /* store the port number in the port */ if (!new_port->name[0]) sprintf(new_port->name, "port-%d", num); + /* insert the new port */ + list_add_tail_rcu(&new_port->list, insert_before); + client->num_ports++; =20 return num; } @@ -253,7 +253,13 @@ static int port_delete(struct snd_seq_client *client, { /* set closing flag and wait for all port access are gone */ port->closing =3D 1; - snd_use_lock_sync(&port->use_lock);=20 + /* the port has already been unlinked from the client's port list; + * wait for a grace period so that RCU readers still traversing the + * list can no longer take a new use_lock reference, then drain the + * outstanding references before freeing + */ + synchronize_rcu(); + snd_use_lock_sync(&port->use_lock); =20 /* clear subscribers info */ clear_subscriber_list(client, port, &port->c_src, true); @@ -276,11 +282,10 @@ int snd_seq_delete_port(struct snd_seq_client *client= , int port) struct snd_seq_client_port *found =3D NULL, *p; =20 scoped_guard(mutex, &client->ports_mutex) { - guard(write_lock_irq)(&client->ports_lock); list_for_each_entry(p, &client->ports_list_head, list) { if (p->addr.port =3D=3D port) { /* ok found. delete from the list at first */ - list_del(&p->list); + list_del_rcu(&p->list); client->num_ports--; found =3D p; break; @@ -296,26 +301,16 @@ int snd_seq_delete_port(struct snd_seq_client *client= , int port) /* delete the all ports belonging to the given client */ int snd_seq_delete_all_ports(struct snd_seq_client *client) { - struct list_head deleted_list; struct snd_seq_client_port *port, *tmp; -=09 - /* move the port list to deleted_list, and - * clear the port list in the client data. + + /* unlink and delete each port; port_delete() waits for an RCU grace + * period before draining the port, so concurrent lockless readers can + * no longer take a new use_lock reference on it */ guard(mutex)(&client->ports_mutex); - scoped_guard(write_lock_irq, &client->ports_lock) { - if (!list_empty(&client->ports_list_head)) { - list_add(&deleted_list, &client->ports_list_head); - list_del_init(&client->ports_list_head); - } else { - INIT_LIST_HEAD(&deleted_list); - } - client->num_ports =3D 0; - } - - /* remove each port in deleted_list */ - list_for_each_entry_safe(port, tmp, &deleted_list, list) { - list_del(&port->list); + list_for_each_entry_safe(port, tmp, &client->ports_list_head, list) { + list_del_rcu(&port->list); + client->num_ports--; snd_seq_system_client_ev_port_exit(port->addr.client, port->addr.port); port_delete(client, port); } --=20 2.55.0 From nobody Wed Sep 30 10:00:52 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7E9D228033B; Mon, 10 Aug 2026 13:37:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786369045; cv=none; b=tktXWecl21aPzgx0n0V7EpSrdylwn42a9m+ziGn4tvshk2JCiBDKSxUdtAPMQicYbuwUdRa3BdgSPmdOD4vdxhd6Ka6kzPW3m8gmfPEKpky9FEUalxjNQo2pyYoAZ889lII6zfgdAcMu5ob9Gy7UZOjm0v5W6J7PKwoWy0mLJfc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786369045; c=relaxed/simple; bh=cjX7TpjnkvqEVzqm96CVJSUc1akOzijhjboTvIkyn9M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GjISh0rJqQlWC6lF//2sLJv2sh+WVWV+MJkZnQlaCB2WEVlbbs9NFr7xvb4SQGR/5y2517sJ5GoZxuLF3286V0lJOb/na1Er4Wp7wPMcC2lxzKb5F2tSWpTrv982cxul166pQJQtA+Yf3gb4k5nCtthflRazHOeEbp7TIVF7ynA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=suse.de; spf=pass smtp.mailfrom=suse.de; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b=oTp4qA/a; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b=sCkPUOcI; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b=VShSWvOo; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b=4WicXtyF; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=suse.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b="oTp4qA/a"; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b="sCkPUOcI"; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b="VShSWvOo"; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b="4WicXtyF" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 4B6297F8F8; Mon, 10 Aug 2026 13:37:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1786369037; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4VpCaDZbXR8VKdP0UIa7LRit4mlFKw5BdaJMULWDr4g=; b=oTp4qA/azjdTHzWbLg7QNKOLMYhslmD+zpE2jBYIJVr/JzOleaJVputcdZqdnAqjHMPeQ0 m86P+6DXV3LbRKqrW3De/7M9X3D6Pn6gViiNZuLP2gVB1llX2EuzW89JO2JPETVBw/n30q QJcR9aHpanNMpNZYzebX+PUKFwYCgqQ= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1786369037; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4VpCaDZbXR8VKdP0UIa7LRit4mlFKw5BdaJMULWDr4g=; b=sCkPUOcIEoKDE/ZDe+fjo+oidzS6h2jzaI8i22+jpmfdSsMhJ7x+WjlAuNFFMUyW467/7Z JAaV/AchiPsBTvCg== Authentication-Results: smtp-out1.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1786369033; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4VpCaDZbXR8VKdP0UIa7LRit4mlFKw5BdaJMULWDr4g=; b=VShSWvOookm0Lwi1JzvGTyaGpYiCXV3M1FNud7LJCq1zSbBJU1GTDsVvVuI+1fcTpE6XY2 NNw/82EYsZiVJ+kbde3zFix/q2iEfM4Q53Q4arSt8ZtjQ5y7WDm+M0yzszqOXjCD6+9tfr qtyckbUzwJXZRqkX8wp5+cm1usuLdZQ= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1786369033; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4VpCaDZbXR8VKdP0UIa7LRit4mlFKw5BdaJMULWDr4g=; b=4WicXtyFH+9RkpH8KvvE8mbkanAOpSX4tYpi42R6bg8tLy/XLrENViiT4DeP+KsXiLlNe0 pXmJ7HuoE3d6f4Ag== Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 2A309779BF; Mon, 10 Aug 2026 13:37:13 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id qCEoCQnUeWq3HwAAD6G6ig (envelope-from ); Mon, 10 Aug 2026 13:37:13 +0000 From: Takashi Iwai To: linux-sound@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [PATCH 3/5] ALSA: seq: Use RCU for the client table Date: Mon, 10 Aug 2026 15:37:04 +0200 Message-ID: <20260810133711.42483-4-tiwai@suse.de> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260810133711.42483-1-tiwai@suse.de> References: <20260810133711.42483-1-tiwai@suse.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spamd-Result: default: False [-2.80 / 50.00]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; RCPT_COUNT_TWO(0.00)[2]; RCVD_VIA_SMTP_AUTH(0.00)[]; ARC_NA(0.00)[]; DKIM_SIGNED(0.00)[suse.de:s=susede2_rsa,suse.de:s=susede2_ed25519]; DBL_BLOCKED_OPENRESOLVER(0.00)[imap1.dmz-prg2.suse.org:helo,suse.de:mid,suse.de:email]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_HAS_DN(0.00)[]; MIME_TRACE(0.00)[0:+]; FROM_EQ_ENVFROM(0.00)[]; TO_DN_NONE(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; RCVD_TLS_ALL(0.00)[] X-Spam-Flag: NO X-Spam-Score: -2.80 X-Spam-Level: Content-Type: text/plain; charset="utf-8" The sequencer keeps a global table of clients (clienttab[]) indexed by client id, protected by the global clients_lock spinlock. The lookup snd_seq_client_use_ptr() reads a slot and takes a use_lock reference on the client, and this runs on the event delivery hot path: every dispatched event resolves its destination (and often source) client through it. The spinlock's only job on the read side is to make the "pointer is non-NULL" test and the reference increment indivisible with respect to the writer that nulls the slot and then drains the refcount. Clients come and go rarely but delivery happens constantly, so this is yet another read-mostly case as the port and subscriber lists. Convert the table to RCU: the read side now runs lock-free under rcu_read_lock() and takes the use_lock reference via rcu_dereference(), removing contention on the single global spinlock from the delivery path. The writers keep clients_lock (still needed to serialize slot allocation) and publish / unpublish via rcu_assign_pointer(); creation and destruction remain serialized at a higher level by register_mutex. As with the ports, the client is not freed via kfree_rcu(): its lifetime is governed by the use_lock refcount drained in seq_free_client1(). list_del under the old spinlock excluded a concurrent lookup from taking a new reference once the slot was nulled; rcu_assign_pointer(NULL) offers no such exclusion, so a reader still holding the old pointer can grab a reference after the unpublish. seq_free_client1() therefore calls synchronize_rcu() after nulling the slot and before snd_use_lock_sync(): once the grace period elapses no new reference can appear, and the existing drain then frees the client safely. clienttablock[] keeps its slot-reservation role (create/free are serialized by register_mutex); its read on the lookup path only gates module autoload, so a lockless read is harmless. Dropping the spinlock from the read path is safe: clients_lock is now taken only by the process-context writers, and the sole atomic reader uses RCU, which is IRQ-safe. Signed-off-by: Takashi Iwai --- sound/core/seq/seq_clientmgr.c | 43 ++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index b7cf14e3ddb3..d4cac594bc8f 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -59,7 +59,7 @@ static DEFINE_MUTEX(register_mutex); * client table */ static char clienttablock[SNDRV_SEQ_MAX_CLIENTS]; -static struct snd_seq_client *clienttab[SNDRV_SEQ_MAX_CLIENTS]; +static struct snd_seq_client __rcu *clienttab[SNDRV_SEQ_MAX_CLIENTS]; static struct snd_seq_usage client_usage; =20 /* @@ -95,15 +95,23 @@ static inline int snd_seq_write_pool_allocated(struct s= nd_seq_client *client) return snd_seq_total_cells(client->pool) > 0; } =20 -/* return pointer to client structure for specified id */ -static struct snd_seq_client *clientptr(int clientid) +/* return pointer to client structure for specified id; call under RCU rea= d-lock */ +static struct snd_seq_client *__clientptr(int clientid) { if (clientid < 0 || clientid >=3D SNDRV_SEQ_MAX_CLIENTS) { pr_debug("ALSA: seq: oops. Trying to get pointer to client %d\n", clientid); return NULL; } - return clienttab[clientid]; + return rcu_dereference_check(clienttab[clientid], + lockdep_is_held(&clients_lock)); +} + +/* return pointer to client structure for specified id */ +static struct snd_seq_client *clientptr(int clientid) +{ + guard(rcu)(); + return __clientptr(clientid); } =20 static struct snd_seq_client *client_use_ptr(int clientid, bool load_modul= e) @@ -115,8 +123,8 @@ static struct snd_seq_client *client_use_ptr(int client= id, bool load_module) clientid); return NULL; } - scoped_guard(spinlock_irqsave, &clients_lock) { - client =3D clientptr(clientid); + scoped_guard(rcu) { + client =3D __clientptr(clientid); if (client) return snd_seq_client_ref(client); if (clienttablock[clientid]) @@ -150,8 +158,8 @@ static struct snd_seq_client *client_use_ptr(int client= id, bool load_module) snd_seq_device_load_drivers(); } } - scoped_guard(spinlock_irqsave, &clients_lock) { - client =3D clientptr(clientid); + scoped_guard(rcu) { + client =3D __clientptr(clientid); if (client) return snd_seq_client_ref(client); } @@ -223,14 +231,17 @@ static struct snd_seq_client *seq_create_client1(int = client_index, int poolsize) for (c =3D SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN; c < SNDRV_SEQ_MAX_CLIENTS; c++) { - if (clienttab[c] || clienttablock[c]) + if (rcu_access_pointer(clienttab[c]) || clienttablock[c]) continue; - clienttab[client->number =3D c] =3D client; + client->number =3D c; + rcu_assign_pointer(clienttab[c], client); return client; } } else { - if (clienttab[client_index] =3D=3D NULL && !clienttablock[client_index]= ) { - clienttab[client->number =3D client_index] =3D client; + if (rcu_access_pointer(clienttab[client_index]) =3D=3D NULL && + !clienttablock[client_index]) { + client->number =3D client_index; + rcu_assign_pointer(clienttab[client_index], client); return client; } } @@ -248,10 +259,16 @@ static int seq_free_client1(struct snd_seq_client *cl= ient) return 0; scoped_guard(spinlock_irq, &clients_lock) { clienttablock[client->number] =3D 1; - clienttab[client->number] =3D NULL; + rcu_assign_pointer(clienttab[client->number], NULL); } snd_seq_delete_all_ports(client); snd_seq_queue_client_leave(client->number); + /* the client has been unpublished from the table; wait for a grace + * period so that lockless readers (snd_seq_client_use_ptr()) that + * observed the old pointer can no longer take a new use_lock + * reference, then drain the outstanding references before freeing + */ + synchronize_rcu(); snd_use_lock_sync(&client->use_lock); if (client->pool) snd_seq_pool_delete(&client->pool); --=20 2.55.0 From nobody Wed Sep 30 10:00:52 2026 Received: from smtp-out1.suse.de (smtp-out1.suse.de [195.135.223.130]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7EAB33033E1; Mon, 10 Aug 2026 13:37:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.130 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786369046; cv=none; b=OR0zxksSijasGgosFgYV71ePN7UVdSBa6Vn03m+ZecxAa79Cq5o/knKZWP5DgGvum5kX74VfDKkte0ZvpBgSi+q9cFswBJxk65X74zb27EbDTY1hgif+9ce4s7KPQJLxpkbdkHfD9xwMznPMSSJyBcsl316M9iYCWnRskaPKp4g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786369046; c=relaxed/simple; bh=+FWBDScucpziU2z9A9x2iC1wOtqm67OIkq4r0gHMT6g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Au2DyHigyZADdixKx2clWF57cE+UNPjnjkNu6eMRg9cjortC86YgzLQD7DvjtMnpcjDYasZjyADyeogqzp4Oif97HmKXneOIkzAsuyfT8JJhRaXQfVL6bdiQCc0103E/xjcushHQAdVLTxTDmOaWwbtc7o56Gh8AvUbXwY8bA6o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=suse.de; spf=pass smtp.mailfrom=suse.de; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b=ToLaXIGU; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b=tgswSL1U; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b=H5wc9nCl; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b=NoLo2Wj/; arc=none smtp.client-ip=195.135.223.130 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=suse.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b="ToLaXIGU"; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b="tgswSL1U"; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b="H5wc9nCl"; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b="NoLo2Wj/" Received: from imap1.dmz-prg2.suse.org (unknown [10.150.64.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out1.suse.de (Postfix) with ESMTPS id 766BC80B87; Mon, 10 Aug 2026 13:37:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1786369037; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZXPz0TSCQKboRGn8TAG6fDM7fbs06XQsUwIy57kHkaY=; b=ToLaXIGUXx6id1gwN9QA8RarCtPg+Jf3TCIwgetnp6bfovLRaQdHD00FpDOVfmlcPRIUUL 5F6XvHQmvBrGmDZxVieOXQ2p/H03eWc0oVhsXJ8jKlM4tN5e1doJ6oEuexW9EzYm3EtJe6 /ekksDOxnIijiQOBvHsiQ/6jH8G28jI= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1786369037; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZXPz0TSCQKboRGn8TAG6fDM7fbs06XQsUwIy57kHkaY=; b=tgswSL1UGWaF9G1FSn8iFq9iECOaY4iTKsHCVT08GkQCHWRUXQT6Mh5UsMUFIpCOeZvRd+ /O5omISOWniyWmAg== Authentication-Results: smtp-out1.suse.de; none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1786369033; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZXPz0TSCQKboRGn8TAG6fDM7fbs06XQsUwIy57kHkaY=; b=H5wc9nCl2MCRdfOQRYLZUp4uacavTSDmWfW91Vqz64j35pqTlb/P4pbIm+imPoaNemCQby drzHLSqTIi8NL6fVRulz3lSy5AlUYtMNiEaqB7rfVMfr+Gp0P8JzQY4fAxHg6KOebMRc+d +PlQ2cc8xlaK7ZeUA6DNncZvynpAtrg= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1786369033; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZXPz0TSCQKboRGn8TAG6fDM7fbs06XQsUwIy57kHkaY=; b=NoLo2Wj/VwHCL+9Y6pz/SgScGC2c6KjdDB5lCGDjNmrNZJzXJ4J1WwZH4qrHIJetbbdJj/ jCR7lqizv6XQFgAQ== Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 4D05E779C0; Mon, 10 Aug 2026 13:37:13 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id WHmuEQnUeWq3HwAAD6G6ig (envelope-from ); Mon, 10 Aug 2026 13:37:13 +0000 From: Takashi Iwai To: linux-sound@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [PATCH 4/5] ALSA: seq: Use RCU for the virmidi file list Date: Mon, 10 Aug 2026 15:37:05 +0200 Message-ID: <20260810133711.42483-5-tiwai@suse.de> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260810133711.42483-1-tiwai@suse.de> References: <20260810133711.42483-1-tiwai@suse.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spamd-Result: default: False [-2.80 / 50.00]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; RCPT_COUNT_TWO(0.00)[2]; RCVD_VIA_SMTP_AUTH(0.00)[]; ARC_NA(0.00)[]; DKIM_SIGNED(0.00)[suse.de:s=susede2_rsa,suse.de:s=susede2_ed25519]; DBL_BLOCKED_OPENRESOLVER(0.00)[suse.de:mid,suse.de:email,imap1.dmz-prg2.suse.org:helo]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_HAS_DN(0.00)[]; MIME_TRACE(0.00)[0:+]; FROM_EQ_ENVFROM(0.00)[]; TO_DN_NONE(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; RCVD_TLS_ALL(0.00)[] X-Spam-Flag: NO X-Spam-Score: -2.80 X-Spam-Level: Content-Type: text/plain; charset="utf-8" Each virmidi device keeps a list of its opened input files (filelist) protected by both an rwlock (filelist_lock) and a rw_semaphore (filelist_sem). snd_virmidi_dev_receive_event() walks the list on the sequencer event input path -- read_lock() when the event is delivered in atomic context, down_read() otherwise -- decoding each incoming event into the file's rawmidi buffer. The writers (input open/close) take both locks to add/remove entries. This is another typical dual-lock read-mostly pattern as the port subscriber list: files are opened/closed rarely while the receive callback runs per event. Let's convert the traversal to RCU and drop the rwlock; the existing filelist_sem keeps serializing the writers. The atomic input path now runs lock-free under rcu_read_lock(), and both readers share a single list_for_each_entry_rcu() (valid under the rwsem via lockdep_is_held()). The writers switch to list_add_tail_rcu() / list_del_rcu(). snd_virmidi_input_close() freed the entry (parser and struct) immediately after list_del. A concurrent lockless reader in the atomic path may still be dereferencing it, so the close path now waits for an RCU grace period after list_del_rcu() before freeing; synchronize_rcu() is used rather than kfree_rcu() because the parser must also be released after the grace period, not just the struct. Non-atomic readers are already excluded by the down_write, so only the atomic RCU readers need the grace period. Dropping write_lock_irq() from the writers is safe: no writer runs in atomic/IRQ context, and the sole atomic reader now uses RCU, which is IRQ-safe. Signed-off-by: Takashi Iwai --- include/sound/seq_virmidi.h | 1 - sound/core/seq/seq_virmidi.c | 19 +++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/include/sound/seq_virmidi.h b/include/sound/seq_virmidi.h index 56a3f38df8c3..359cb363369d 100644 --- a/include/sound/seq_virmidi.h +++ b/include/sound/seq_virmidi.h @@ -46,7 +46,6 @@ struct snd_virmidi_dev { int client; /* created/attached client */ int port; /* created/attached port */ unsigned int flags; /* SNDRV_VIRMIDI_* */ - rwlock_t filelist_lock; struct rw_semaphore filelist_sem; struct list_head filelist; }; diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 982828650d41..6208bf7f57bf 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -78,10 +78,11 @@ static int snd_virmidi_dev_receive_event(struct snd_vir= midi_dev *rdev, int len; =20 if (atomic) - read_lock(&rdev->filelist_lock); + rcu_read_lock(); else down_read(&rdev->filelist_sem); - list_for_each_entry(vmidi, &rdev->filelist, list) { + list_for_each_entry_rcu(vmidi, &rdev->filelist, list, + lockdep_is_held(&rdev->filelist_sem)) { if (!READ_ONCE(vmidi->trigger)) continue; if (ev->type =3D=3D SNDRV_SEQ_EVENT_SYSEX) { @@ -96,7 +97,7 @@ static int snd_virmidi_dev_receive_event(struct snd_virmi= di_dev *rdev, } } if (atomic) - read_unlock(&rdev->filelist_lock); + rcu_read_unlock(); else up_read(&rdev->filelist_sem); =20 @@ -200,8 +201,7 @@ static int snd_virmidi_input_open(struct snd_rawmidi_su= bstream *substream) vmidi->port =3D rdev->port;=09 runtime->private_data =3D vmidi; scoped_guard(rwsem_write, &rdev->filelist_sem) { - guard(write_lock_irq)(&rdev->filelist_lock); - list_add_tail(&vmidi->list, &rdev->filelist); + list_add_tail_rcu(&vmidi->list, &rdev->filelist); } vmidi->rdev =3D rdev; return 0; @@ -243,9 +243,13 @@ static int snd_virmidi_input_close(struct snd_rawmidi_= substream *substream) struct snd_virmidi *vmidi =3D substream->runtime->private_data; =20 scoped_guard(rwsem_write, &rdev->filelist_sem) { - guard(write_lock_irq)(&rdev->filelist_lock); - list_del(&vmidi->list); + list_del_rcu(&vmidi->list); } + /* wait for a grace period so that lockless readers in the atomic + * delivery path (snd_virmidi_dev_receive_event()) are no longer + * traversing this entry before its parser and memory are freed + */ + synchronize_rcu(); snd_midi_event_free(vmidi->parser); substream->runtime->private_data =3D NULL; kfree(vmidi); @@ -508,7 +512,6 @@ int snd_virmidi_new(struct snd_card *card, int device, = struct snd_rawmidi **rrmi rdev->device =3D device; rdev->client =3D -1; init_rwsem(&rdev->filelist_sem); - rwlock_init(&rdev->filelist_lock); INIT_LIST_HEAD(&rdev->filelist); rdev->seq_mode =3D SNDRV_VIRMIDI_SEQ_DISPATCH; rmidi->private_data =3D rdev; --=20 2.55.0 From nobody Wed Sep 30 10:00:52 2026 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.223.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4B9FF28033B; Mon, 10 Aug 2026 13:37:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=195.135.223.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786369052; cv=none; b=nPqHKDCP9uyM5zOTvaYRXUSTanFwCwhnQLMh+tlDzwQWXUbP5AUZ8IOS1nxpd72rl+lmW3ka0FYsetU9siJmdWVjPcFABC1ZIGOYW9cfvemmULybCESb22zuTJ1bU1GnNl0kPasXPUEB7FXmquLq94F25ZVxBZdKLuEGltp3wg4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786369052; c=relaxed/simple; bh=Zjq1Q8CeoiFpm5gURvKJ6CGk/x0CqTDUS1AkevgWyLQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Dpb5XGQwdaEK712hA423q9Lj0O8UdKjUzCoyo83ltJe3NpXXSLXoWxGGnV6RonVu9FexmJM7iXIdSSQ6UhLo9K8yo1AiCEpO71UvV2LCXMsulBX40pe7HQBaNw8BIaHaekiXaZjZSxRl5dSrnILso1fzOIiYvZP4BZEoJG84xBk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=suse.de; spf=pass smtp.mailfrom=suse.de; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b=e0cJB/I0; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b=5/jtI7ss; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b=0v81DiJa; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b=/QW1tjKh; arc=none smtp.client-ip=195.135.223.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=suse.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=suse.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b="e0cJB/I0"; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b="5/jtI7ss"; dkim=pass (1024-bit key) header.d=suse.de header.i=@suse.de header.b="0v81DiJa"; dkim=permerror (0-bit key) header.d=suse.de header.i=@suse.de header.b="/QW1tjKh" Received: from imap1.dmz-prg2.suse.org (imap1.dmz-prg2.suse.org [IPv6:2a07:de40:b281:104:10:150:64:97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 377433DF6; Mon, 10 Aug 2026 13:37:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1786369045; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1HfLb0hKzmLBwjDNiuOQe4KtAHV9fyQ/UPZ2Erumlck=; b=e0cJB/I0trzBcK4KTHnZ8ePNDfjRBj+TTU7IJ6U5JnBlDb+lWQeRLpf9gaqjVwAsJ3Et+w gkmVGDm7EGY7EAPGS5Cwar1J0l66iLvDx80uIKysuP6pTtIrVG7L3YbhbEwQ+StT4ulrjB RZcv0GG31BbpVISW6im1MBVPr2O5C/I= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1786369045; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1HfLb0hKzmLBwjDNiuOQe4KtAHV9fyQ/UPZ2Erumlck=; b=5/jtI7ssI4paAfyxruyJ95eLNDWvY0XWNIq3AgG6wl3aRlFh/tJfhO4pzjiiFiKwcAWWhp TPNfYNLVnsTnQGCQ== Authentication-Results: smtp-out2.suse.de; dkim=pass header.d=suse.de header.s=susede2_rsa header.b=0v81DiJa; dkim=pass header.d=suse.de header.s=susede2_ed25519 header.b="/QW1tjKh" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1786369041; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1HfLb0hKzmLBwjDNiuOQe4KtAHV9fyQ/UPZ2Erumlck=; b=0v81DiJajFMDRRe3xP/ZwNQ6yfdTzV9lDB8qf/k2QbXm7ENy0WV/TZlPCnh6W7jBGft0gB CdIitBeltxI11eEEhXkKcNySbSWCpyx1Cl4ZeQop7zECpD+79v91GQW7Yu5O+oJUY18gIz tTF5LTPpn79s9hcWDfNAooJ+Y2m3bW8= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1786369041; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1HfLb0hKzmLBwjDNiuOQe4KtAHV9fyQ/UPZ2Erumlck=; b=/QW1tjKh+xvp9enIKeDDx8sMF/gFlcLtGt/ERi/xh2RyRh0J6IrbL4WHIsEYyHNgMzTt7d 7uxol6VTYt823pDA== Received: from imap1.dmz-prg2.suse.org (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by imap1.dmz-prg2.suse.org (Postfix) with ESMTPS id 71FB5779C3; Mon, 10 Aug 2026 13:37:13 +0000 (UTC) Received: from dovecot-director2.suse.de ([2a07:de40:b281:106:10:150:64:167]) by imap1.dmz-prg2.suse.org with ESMTPSA id MBisGgnUeWq3HwAAD6G6ig (envelope-from ); Mon, 10 Aug 2026 13:37:13 +0000 From: Takashi Iwai To: linux-sound@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [PATCH 5/5] ALSA: seq: Use RCU for the UMP client output substream Date: Mon, 10 Aug 2026 15:37:06 +0200 Message-ID: <20260810133711.42483-6-tiwai@suse.de> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260810133711.42483-1-tiwai@suse.de> References: <20260810133711.42483-1-tiwai@suse.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Spam-Score: -3.01 X-Spam-Level: X-Rspamd-Action: no action X-Rspamd-Queue-Id: 377433DF6 X-Spamd-Result: default: False [-3.01 / 50.00]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000]; R_MISSING_CHARSET(0.50)[]; R_DKIM_ALLOW(-0.20)[suse.de:s=susede2_rsa,suse.de:s=susede2_ed25519]; NEURAL_HAM_SHORT(-0.20)[-1.000]; MIME_GOOD(-0.10)[text/plain]; MX_GOOD(-0.01)[]; RECEIVED_SPAMHAUS_BLOCKED_OPENRESOLVER(0.00)[2a07:de40:b281:106:10:150:64:167:received]; DNSWL_BLOCKED(0.00)[2a07:de40:b281:106:10:150:64:167:received,2a07:de40:b281:104:10:150:64:97:from]; ARC_NA(0.00)[]; RCVD_VIA_SMTP_AUTH(0.00)[]; MIME_TRACE(0.00)[0:+]; DBL_BLOCKED_OPENRESOLVER(0.00)[imap1.dmz-prg2.suse.org:helo,imap1.dmz-prg2.suse.org:rdns,suse.de:mid,suse.de:email,suse.de:dkim]; RCPT_COUNT_TWO(0.00)[2]; TO_MATCH_ENVRCPT_ALL(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; FROM_HAS_DN(0.00)[]; DKIM_SIGNED(0.00)[suse.de:s=susede2_rsa,suse.de:s=susede2_ed25519]; RCVD_TLS_ALL(0.00)[]; TO_DN_NONE(0.00)[]; RCVD_COUNT_TWO(0.00)[2]; DKIM_TRACE(0.00)[suse.de:+] X-Rspamd-Server: rspamd1.dmz-prg2.suse.org X-Spam-Flag: NO Content-Type: text/plain; charset="utf-8" The UMP sequencer client protects its output rawmidi file (out_rfile) with an rwlock (output_lock). seq_ump_process_event(), the port's event_input callback, reads out_rfile.output under read_lock on every delivered UMP event, while the open/close paths (serialized by ump->open_mutex) publish and clear out_rfile under write_lock. Output is opened/closed only on the subscribe/use lifecycle while delivery happens per event, so this is another read-mostly hot path. Convert it to RCU and drop the rwlock. out_rfile is an embedded struct rather than a pointer, so instead of restructuring it, add an RCU-protected shadow of the substream (out_substream) for the reader; out_rfile itself becomes writer-only state accessed solely under open_mutex. The reader now runs lock-free under rcu_read_lock() via rcu_dereference(), and open publishes the substream with rcu_assign_pointer(). On close the substream is cleared with rcu_assign_pointer(NULL) and the rawmidi is released only after synchronize_rcu(), so no reader in the delivery path can still be writing to the substream when snd_rawmidi_kernel_release() runs. Dropping write_lock_irqsave() from the writers is safe: they run in process context under open_mutex, and the sole atomic reader now uses RCU, which is IRQ-safe. Signed-off-by: Takashi Iwai --- sound/core/seq/seq_ump_client.c | 38 +++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_clien= t.c index ccd93599b493..4c1e81db376f 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -37,8 +37,11 @@ struct seq_ump_client { struct snd_ump_endpoint *ump; /* assigned endpoint */ int seq_client; /* sequencer client id */ int opened[2]; /* current opens for each direction */ - rwlock_t output_lock; /* protects out_rfile output access */ struct snd_rawmidi_file out_rfile; /* rawmidi for output */ + /* RCU-protected shadow of out_rfile.output for the delivery hot path; + * out_rfile itself is only touched by open/close under open_mutex + */ + struct snd_rawmidi_substream __rcu *out_substream; struct seq_ump_input_buffer input; /* input parser context */ void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_inf= o */ struct work_struct group_notify_work; /* FB change notification */ @@ -89,8 +92,8 @@ static int seq_ump_process_event(struct snd_seq_event *ev= , int direct, unsigned char type; int len; =20 - guard(read_lock_irqsave)(&client->output_lock); - substream =3D client->out_rfile.output; + guard(rcu)(); + substream =3D rcu_dereference(client->out_substream); if (!substream) return -ENODEV; if (!snd_seq_ev_is_ump(ev)) @@ -108,19 +111,22 @@ static int seq_ump_process_event(struct snd_seq_event= *ev, int direct, static int seq_ump_client_open(struct seq_ump_client *client, int dir) { struct snd_ump_endpoint *ump =3D client->ump; - struct snd_rawmidi_file rfile =3D {}; int err; =20 guard(mutex)(&ump->open_mutex); if (dir =3D=3D STR_OUT && !client->opened[dir]) { + /* out_rfile is only accessed under open_mutex; the delivery + * path reads out_substream via RCU, so open into out_rfile + * directly and publish the substream afterwards + */ err =3D snd_rawmidi_kernel_open(&ump->core, 0, SNDRV_RAWMIDI_LFLG_OUTPUT | SNDRV_RAWMIDI_LFLG_APPEND, - &rfile); + &client->out_rfile); if (err < 0) return err; - scoped_guard(write_lock_irqsave, &client->output_lock) - client->out_rfile =3D rfile; + rcu_assign_pointer(client->out_substream, + client->out_rfile.output); } client->opened[dir]++; return 0; @@ -130,17 +136,18 @@ static int seq_ump_client_open(struct seq_ump_client = *client, int dir) static int seq_ump_client_close(struct seq_ump_client *client, int dir) { struct snd_ump_endpoint *ump =3D client->ump; - struct snd_rawmidi_file rfile =3D {}; =20 guard(mutex)(&ump->open_mutex); if (!--client->opened[dir]) { - if (dir =3D=3D STR_OUT) { - scoped_guard(write_lock_irqsave, &client->output_lock) { - rfile =3D client->out_rfile; - client->out_rfile =3D (struct snd_rawmidi_file){}; - } - if (rfile.rmidi) - snd_rawmidi_kernel_release(&rfile); + if (dir =3D=3D STR_OUT && client->out_rfile.rmidi) { + rcu_assign_pointer(client->out_substream, NULL); + /* wait for a grace period so that no reader in the + * delivery path is still writing to the substream + * before it is released + */ + synchronize_rcu(); + snd_rawmidi_kernel_release(&client->out_rfile); + client->out_rfile =3D (struct snd_rawmidi_file){}; } } return 0; @@ -480,7 +487,6 @@ static int snd_seq_ump_probe(struct snd_seq_device *dev) =20 INIT_WORK(&client->group_notify_work, handle_group_notify); client->ump =3D ump; - rwlock_init(&client->output_lock); =20 client->seq_client =3D snd_seq_create_kernel_client(card, ump->core.device, --=20 2.55.0