Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: usb-audio: Set callbacks via snd_usb_endpoint_set_callback()

The prepare_data_urb and retire_data_urb fields of the endpoint object
are set dynamically at PCM trigger start/stop. Those are evaluated in
the endpoint handler, but there can be a race, especially if two
different PCM substreams are handling the same endpoint for the
implicit feedback case. Also, the data_subs field of the endpoint is
set and accessed dynamically, too, which has the same risk.

As a slight improvement for the concurrency, this patch introduces the
function to set the callbacks and the data in a shot with the memory
barrier. In the reader side, it's also fetched with the memory
barrier.

There is still a room of race if prepare and retire callbacks are set
during executing the URB completion. But such an inconsistency may
happen only for the implicit fb source, i.e. it's only about the
capture stream. And luckily, the capture stream never sets the
prepare callback, hence the problem doesn't happen practically.

Tested-by: Keith Milner <kamilner@superlative.org>
Tested-by: Dylan Robinson <dylan_robinson@motu.com>
Link: https://lore.kernel.org/r/20201123085347.19667-23-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>

+66 -34
+41 -19
sound/usb/endpoint.c
··· 169 169 return ret; 170 170 } 171 171 172 + static void call_retire_callback(struct snd_usb_endpoint *ep, 173 + struct urb *urb) 174 + { 175 + struct snd_usb_substream *data_subs; 176 + 177 + data_subs = READ_ONCE(ep->data_subs); 178 + if (data_subs && ep->retire_data_urb) 179 + ep->retire_data_urb(data_subs, urb); 180 + } 181 + 172 182 static void retire_outbound_urb(struct snd_usb_endpoint *ep, 173 183 struct snd_urb_ctx *urb_ctx) 174 184 { 175 - if (ep->retire_data_urb) 176 - ep->retire_data_urb(ep->data_subs, urb_ctx->urb); 185 + call_retire_callback(ep, urb_ctx->urb); 177 186 } 178 187 179 188 static void retire_inbound_urb(struct snd_usb_endpoint *ep, ··· 198 189 if (ep->sync_slave) 199 190 snd_usb_handle_sync_urb(ep->sync_slave, ep, urb); 200 191 201 - if (ep->retire_data_urb) 202 - ep->retire_data_urb(ep->data_subs, urb); 192 + call_retire_callback(ep, urb); 203 193 } 204 194 205 195 static void prepare_silent_urb(struct snd_usb_endpoint *ep, ··· 252 244 { 253 245 struct urb *urb = ctx->urb; 254 246 unsigned char *cp = urb->transfer_buffer; 247 + struct snd_usb_substream *data_subs; 255 248 256 249 urb->dev = ep->chip->dev; /* we need to set this at each time */ 257 250 258 251 switch (ep->type) { 259 252 case SND_USB_ENDPOINT_TYPE_DATA: 260 - if (ep->prepare_data_urb) { 261 - ep->prepare_data_urb(ep->data_subs, urb); 262 - } else { 263 - /* no data provider, so send silence */ 253 + data_subs = READ_ONCE(ep->data_subs); 254 + if (data_subs && ep->prepare_data_urb) 255 + ep->prepare_data_urb(data_subs, urb); 256 + else /* no data provider, so send silence */ 264 257 prepare_silent_urb(ep, ctx); 265 - } 266 258 break; 267 259 268 260 case SND_USB_ENDPOINT_TYPE_SYNC: ··· 389 381 { 390 382 struct snd_urb_ctx *ctx = urb->context; 391 383 struct snd_usb_endpoint *ep = ctx->ep; 392 - struct snd_pcm_substream *substream; 384 + struct snd_usb_substream *data_subs; 393 385 unsigned long flags; 394 386 int err; 395 387 ··· 438 430 return; 439 431 440 432 usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err); 441 - if (ep->data_subs && ep->data_subs->pcm_substream) { 442 - substream = ep->data_subs->pcm_substream; 443 - snd_pcm_stop_xrun(substream); 444 - } 433 + data_subs = READ_ONCE(ep->data_subs); 434 + if (data_subs && data_subs->pcm_substream) 435 + snd_pcm_stop_xrun(data_subs->pcm_substream); 445 436 446 437 exit_clear: 447 438 clear_bit(ctx->index, &ep->active_mask); ··· 540 533 } 541 534 542 535 /* 536 + * Set data endpoint callbacks and the assigned data stream 537 + * 538 + * Called at PCM trigger and cleanups. 539 + * Pass NULL to deactivate each callback. 540 + */ 541 + void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep, 542 + void (*prepare)(struct snd_usb_substream *subs, 543 + struct urb *urb), 544 + void (*retire)(struct snd_usb_substream *subs, 545 + struct urb *urb), 546 + struct snd_usb_substream *data_subs) 547 + { 548 + ep->prepare_data_urb = prepare; 549 + ep->retire_data_urb = retire; 550 + WRITE_ONCE(ep->data_subs, data_subs); 551 + } 552 + 553 + /* 543 554 * wait until all urbs are processed. 544 555 */ 545 556 static int wait_clear_urbs(struct snd_usb_endpoint *ep) ··· 579 554 alive, ep->ep_num); 580 555 clear_bit(EP_FLAG_STOPPING, &ep->flags); 581 556 582 - ep->data_subs = NULL; 583 557 ep->sync_slave = NULL; 584 - ep->retire_data_urb = NULL; 585 - ep->prepare_data_urb = NULL; 558 + snd_usb_endpoint_set_callback(ep, NULL, NULL, NULL); 586 559 587 560 return 0; 588 561 } ··· 630 607 int i; 631 608 632 609 /* route incoming urbs to nirvana */ 633 - ep->retire_data_urb = NULL; 634 - ep->prepare_data_urb = NULL; 610 + snd_usb_endpoint_set_callback(ep, NULL, NULL, NULL); 635 611 636 612 /* stop urbs */ 637 613 deactivate_urbs(ep, force);
+7
sound/usb/endpoint.h
··· 20 20 struct audioformat *fmt, 21 21 struct snd_usb_endpoint *sync_ep); 22 22 23 + void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep, 24 + void (*prepare)(struct snd_usb_substream *subs, 25 + struct urb *urb), 26 + void (*retire)(struct snd_usb_substream *subs, 27 + struct urb *urb), 28 + struct snd_usb_substream *data_subs); 29 + 23 30 int snd_usb_endpoint_start(struct snd_usb_endpoint *ep); 24 31 void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep); 25 32 void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
+18 -15
sound/usb/pcm.c
··· 232 232 if (!test_and_set_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) { 233 233 struct snd_usb_endpoint *ep = subs->data_endpoint; 234 234 235 - ep->data_subs = subs; 236 235 err = snd_usb_endpoint_start(ep); 237 236 if (err < 0) { 238 237 clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags); ··· 1829 1830 subs->trigger_tstamp_pending_update = true; 1830 1831 fallthrough; 1831 1832 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 1832 - subs->data_endpoint->prepare_data_urb = prepare_playback_urb; 1833 - subs->data_endpoint->retire_data_urb = retire_playback_urb; 1833 + snd_usb_endpoint_set_callback(subs->data_endpoint, 1834 + prepare_playback_urb, 1835 + retire_playback_urb, 1836 + subs); 1834 1837 subs->running = 1; 1835 1838 return 0; 1836 1839 case SNDRV_PCM_TRIGGER_STOP: 1837 1840 stop_endpoints(subs); 1841 + snd_usb_endpoint_set_callback(subs->data_endpoint, 1842 + NULL, NULL, NULL); 1838 1843 subs->running = 0; 1839 1844 return 0; 1840 1845 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 1841 - subs->data_endpoint->prepare_data_urb = NULL; 1842 1846 /* keep retire_data_urb for delay calculation */ 1843 - subs->data_endpoint->retire_data_urb = retire_playback_urb; 1847 + snd_usb_endpoint_set_callback(subs->data_endpoint, 1848 + NULL, 1849 + retire_playback_urb, 1850 + subs); 1844 1851 subs->running = 0; 1845 1852 return 0; 1846 1853 case SNDRV_PCM_TRIGGER_SUSPEND: ··· 1872 1867 err = start_endpoints(subs); 1873 1868 if (err < 0) 1874 1869 return err; 1875 - 1876 - subs->data_endpoint->retire_data_urb = retire_capture_urb; 1870 + fallthrough; 1871 + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 1872 + snd_usb_endpoint_set_callback(subs->data_endpoint, 1873 + NULL, retire_capture_urb, 1874 + subs); 1877 1875 subs->running = 1; 1878 1876 return 0; 1879 1877 case SNDRV_PCM_TRIGGER_STOP: 1880 1878 stop_endpoints(subs); 1881 - subs->data_endpoint->retire_data_urb = NULL; 1882 - subs->running = 0; 1883 - return 0; 1879 + fallthrough; 1884 1880 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 1885 - subs->data_endpoint->retire_data_urb = NULL; 1881 + snd_usb_endpoint_set_callback(subs->data_endpoint, 1882 + NULL, NULL, NULL); 1886 1883 subs->running = 0; 1887 - return 0; 1888 - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 1889 - subs->data_endpoint->retire_data_urb = retire_capture_urb; 1890 - subs->running = 1; 1891 1884 return 0; 1892 1885 case SNDRV_PCM_TRIGGER_SUSPEND: 1893 1886 if (subs->stream->chip->setup_fmt_after_resume_quirk) {