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

usb: gadget: uvc: rework to enqueue in pump worker from encoded queue

We install an kthread with pfifo priority that is iterating over all
prepared requests and keeps the isoc queue busy. This way it will be
scheduled with the same priority as the interrupt handler.

As the kthread is triggered with video_enable it will immediately
queue some zero length requests into the hw if there is no buffer data
available. It also watches the level of needed zero length requests in
the hardware not to fall under the UVCG_REQ_MAX_ZERO_COUNT threshold.
This way we can drop the function uvc_video_ep_queue_initial_requests
entirely.

By using the kthread to do the actual request handling the interrupt
handler will not be running into the time consuming and eventually
locking work of actually enqueueing the requests back into its own
pipeline. This work can now even be scheduled on another cpu.

Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
Link: https://lore.kernel.org/r/20240403-uvc_request_length_by_interval-v7-3-e224bb1035f0@pengutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Michael Grzeschik and committed by
Greg Kroah-Hartman
f0bbfbd1 dc97c956

+92 -103
+2
drivers/usb/gadget/function/f_uvc.c
··· 991 991 992 992 uvcg_info(f, "%s()\n", __func__); 993 993 994 + kthread_cancel_work_sync(&video->hw_submit); 995 + 994 996 if (video->async_wq) 995 997 destroy_workqueue(video->async_wq); 996 998
+3
drivers/usb/gadget/function/uvc.h
··· 94 94 struct work_struct pump; 95 95 struct workqueue_struct *async_wq; 96 96 97 + struct kthread_worker *kworker; 98 + struct kthread_work hw_submit; 99 + 97 100 atomic_t queued; 98 101 99 102 /* Frame parameters */
+87 -103
drivers/usb/gadget/function/uvc_video.c
··· 324 324 return 0; 325 325 } 326 326 327 - /* 328 - * Must only be called from uvcg_video_enable - since after that we only want to 329 - * queue requests to the endpoint from the uvc_video_complete complete handler. 330 - * This function is needed in order to 'kick start' the flow of requests from 331 - * gadget driver to the usb controller. 332 - */ 333 - static void uvc_video_ep_queue_initial_requests(struct uvc_video *video) 334 - { 335 - struct usb_request *req = NULL; 336 - unsigned long flags = 0; 337 - unsigned int count = 0; 338 - int ret = 0; 339 - 340 - /* 341 - * We only queue half of the free list since we still want to have 342 - * some free usb_requests in the free list for the video_pump async_wq 343 - * thread to encode uvc buffers into. Otherwise we could get into a 344 - * situation where the free list does not have any usb requests to 345 - * encode into - we always end up queueing 0 length requests to the 346 - * end point. 347 - */ 348 - unsigned int half_list_size = video->uvc_num_requests / 2; 349 - 350 - spin_lock_irqsave(&video->req_lock, flags); 351 - /* 352 - * Take these requests off the free list and queue them all to the 353 - * endpoint. Since we queue 0 length requests with the req_lock held, 354 - * there isn't any 'data' race involved here with the complete handler. 355 - */ 356 - while (count < half_list_size) { 357 - req = list_first_entry(&video->req_free, struct usb_request, 358 - list); 359 - list_del(&req->list); 360 - req->length = 0; 361 - ret = uvcg_video_ep_queue(video, req); 362 - if (ret < 0) { 363 - uvcg_queue_cancel(&video->queue, 0); 364 - break; 365 - } 366 - count++; 367 - } 368 - spin_unlock_irqrestore(&video->req_lock, flags); 369 - } 370 - 371 327 static void 372 328 uvc_video_complete(struct usb_ep *ep, struct usb_request *req) 373 329 { ··· 331 375 struct uvc_video *video = ureq->video; 332 376 struct uvc_video_queue *queue = &video->queue; 333 377 struct uvc_buffer *last_buf; 334 - struct usb_request *to_queue = req; 335 378 unsigned long flags; 336 - bool is_bulk = video->max_payload_size; 337 - int ret = 0; 338 379 339 380 spin_lock_irqsave(&video->req_lock, flags); 340 381 atomic_dec(&video->queued); ··· 394 441 return; 395 442 } 396 443 444 + list_add_tail(&req->list, &video->req_free); 397 445 /* 398 - * Here we check whether any request is available in the ready 399 - * list. If it is, queue it to the ep and add the current 400 - * usb_request to the req_free list - for video_pump to fill in. 401 - * Otherwise, just use the current usb_request to queue a 0 402 - * length request to the ep. Since we always add to the req_free 403 - * list if we dequeue from the ready list, there will never 404 - * be a situation where the req_free list is completely out of 405 - * requests and cannot recover. 446 + * Queue work to the wq as well since it is possible that a 447 + * buffer may not have been completely encoded with the set of 448 + * in-flight usb requests for whih the complete callbacks are 449 + * firing. 450 + * In that case, if we do not queue work to the worker thread, 451 + * the buffer will never be marked as complete - and therefore 452 + * not be returned to userpsace. As a result, 453 + * dequeue -> queue -> dequeue flow of uvc buffers will not 454 + * happen. Since there are is a new free request wake up the pump. 406 455 */ 407 - to_queue->length = 0; 408 - if (!list_empty(&video->req_ready)) { 409 - to_queue = list_first_entry(&video->req_ready, 410 - struct usb_request, list); 411 - list_del(&to_queue->list); 412 - list_add_tail(&req->list, &video->req_free); 413 - /* 414 - * Queue work to the wq as well since it is possible that a 415 - * buffer may not have been completely encoded with the set of 416 - * in-flight usb requests for whih the complete callbacks are 417 - * firing. 418 - * In that case, if we do not queue work to the worker thread, 419 - * the buffer will never be marked as complete - and therefore 420 - * not be returned to userpsace. As a result, 421 - * dequeue -> queue -> dequeue flow of uvc buffers will not 422 - * happen. 423 - */ 424 - queue_work(video->async_wq, &video->pump); 425 - } else if (atomic_read(&video->queued) > UVCG_REQ_MAX_ZERO_COUNT) { 426 - list_add_tail(&to_queue->list, &video->req_free); 427 - /* 428 - * There is a new free request - wake up the pump. 429 - */ 430 - queue_work(video->async_wq, &video->pump); 431 - 432 - spin_unlock_irqrestore(&video->req_lock, flags); 433 - 434 - return; 435 - } 436 - /* 437 - * Queue to the endpoint. The actual queueing to ep will 438 - * only happen on one thread - the async_wq for bulk endpoints 439 - * and this thread for isoc endpoints. 440 - */ 441 - ret = uvcg_video_usb_req_queue(video, to_queue, !is_bulk); 442 - if (ret < 0) { 443 - /* 444 - * Endpoint error, but the stream is still enabled. 445 - * Put request back in req_free for it to be cleaned 446 - * up later. 447 - */ 448 - list_add_tail(&to_queue->list, &video->req_free); 449 - /* 450 - * There is a new free request - wake up the pump. 451 - */ 452 - queue_work(video->async_wq, &video->pump); 453 - } 456 + queue_work(video->async_wq, &video->pump); 454 457 455 458 spin_unlock_irqrestore(&video->req_lock, flags); 459 + 460 + kthread_queue_work(video->kworker, &video->hw_submit); 461 + } 462 + 463 + static void uvcg_video_hw_submit(struct kthread_work *work) 464 + { 465 + struct uvc_video *video = container_of(work, struct uvc_video, hw_submit); 466 + bool is_bulk = video->max_payload_size; 467 + unsigned long flags; 468 + struct usb_request *req; 469 + int ret = 0; 470 + 471 + while (true) { 472 + if (!video->ep->enabled) 473 + return; 474 + spin_lock_irqsave(&video->req_lock, flags); 475 + /* 476 + * Here we check whether any request is available in the ready 477 + * list. If it is, queue it to the ep and add the current 478 + * usb_request to the req_free list - for video_pump to fill in. 479 + * Otherwise, just use the current usb_request to queue a 0 480 + * length request to the ep. Since we always add to the req_free 481 + * list if we dequeue from the ready list, there will never 482 + * be a situation where the req_free list is completely out of 483 + * requests and cannot recover. 484 + */ 485 + if (!list_empty(&video->req_ready)) { 486 + req = list_first_entry(&video->req_ready, 487 + struct usb_request, list); 488 + } else { 489 + if (list_empty(&video->req_free) || 490 + (atomic_read(&video->queued) > UVCG_REQ_MAX_ZERO_COUNT)) { 491 + spin_unlock_irqrestore(&video->req_lock, flags); 492 + 493 + return; 494 + } 495 + req = list_first_entry(&video->req_free, struct usb_request, 496 + list); 497 + req->length = 0; 498 + } 499 + list_del(&req->list); 500 + 501 + /* 502 + * Queue to the endpoint. The actual queueing to ep will 503 + * only happen on one thread - the async_wq for bulk endpoints 504 + * and this thread for isoc endpoints. 505 + */ 506 + ret = uvcg_video_usb_req_queue(video, req, !is_bulk); 507 + if (ret < 0) { 508 + /* 509 + * Endpoint error, but the stream is still enabled. 510 + * Put request back in req_free for it to be cleaned 511 + * up later. 512 + */ 513 + list_add_tail(&req->list, &video->req_free); 514 + /* 515 + * There is a new free request - wake up the pump. 516 + */ 517 + queue_work(video->async_wq, &video->pump); 518 + 519 + } 520 + 521 + spin_unlock_irqrestore(&video->req_lock, flags); 522 + } 456 523 } 457 524 458 525 static int ··· 744 771 745 772 atomic_set(&video->queued, 0); 746 773 747 - uvc_video_ep_queue_initial_requests(video); 774 + kthread_queue_work(video->kworker, &video->hw_submit); 748 775 queue_work(video->async_wq, &video->pump); 749 776 750 777 return ret; ··· 766 793 video->async_wq = alloc_workqueue("uvcgadget", WQ_UNBOUND | WQ_HIGHPRI, 0); 767 794 if (!video->async_wq) 768 795 return -EINVAL; 796 + 797 + /* Allocate a kthread for asynchronous hw submit handler. */ 798 + video->kworker = kthread_create_worker(0, "UVCG"); 799 + if (IS_ERR(video->kworker)) { 800 + uvcg_err(&video->uvc->func, "failed to create UVCG kworker\n"); 801 + return PTR_ERR(video->kworker); 802 + } 803 + 804 + kthread_init_work(&video->hw_submit, uvcg_video_hw_submit); 805 + 806 + sched_set_fifo(video->kworker->task); 769 807 770 808 video->uvc = uvc; 771 809 video->fcc = V4L2_PIX_FMT_YUYV;