[PATCH v4 07/12] usb: usbip: preallocate kernel threads for consistent attach operation
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date: 2021-03-04 15:27:32
Subsystem:
the rest, usb over ip driver, usb subsystem · Maintainers:
Linus Torvalds, Valentina Manea, Shuah Khan, Greg Kroah-Hartman
All usbip modules are doing
(1) socket lookup for ud->tcp_socket
(2) kthread_create() for ud->tcp_{tx,rx}
(3) get_task_struct() on ud->tcp_{tx,rx} if kthread_create() succeeded
(4) wake_up_process() on ud->tcp_{tx,rx} if kthread_create() succeeded
where (1) (2) (3) can be grouped as a common function.
Since we can't overwrite ud->tcp_socket and ud->tcp_{tx,rx} without
checking ud->status, define usbip_thread_info for holding temporary
resources and introduce usbip_prepare_threads()/usbip_unprepare_threads()
which respectively allocates/frees these temporary resources.
(4) is done by usbip_prepare_threads() callers after checking ud->status.
Tested-by: syzbot <redacted>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
drivers/usb/usbip/usbip_common.c | 50 ++++++++++++++++++++++++++++++++
drivers/usb/usbip/usbip_common.h | 12 ++++++++
2 files changed, 62 insertions(+)
diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index 2ab99244bc31..9f677c5a74e8 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c@@ -748,6 +748,56 @@ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) } EXPORT_SYMBOL_GPL(usbip_recv_xbuff); +int usbip_prepare_threads(struct usbip_thread_info *uti, + struct usbip_device *ud, int sockfd, + int (*tx_fn)(void *data), const char *tx_name, + int (*rx_fn)(void *data), const char *rx_name) +{ + int err; + struct socket *socket; + struct task_struct *tx; + struct task_struct *rx; + + /* Extract socket from fd. */ + socket = sockfd_lookup(sockfd, &err); + if (!socket) + return -EINVAL; + /* Create threads for this socket. */ + rx = kthread_create(rx_fn, ud, rx_name); + if (IS_ERR(rx)) { + err = PTR_ERR(rx); + goto out_socket; + } + tx = kthread_create(tx_fn, ud, tx_name); + if (IS_ERR(tx)) { + err = PTR_ERR(tx); + goto out_rx; + } + uti->tcp_socket = socket; + get_task_struct(rx); + uti->tcp_rx = rx; + get_task_struct(tx); + uti->tcp_tx = tx; + return 0; + out_rx: + kthread_stop(rx); + out_socket: + sockfd_put(socket); + return err; +} +EXPORT_SYMBOL_GPL(usbip_prepare_threads); + +void usbip_unprepare_threads(struct usbip_thread_info *uti) +{ + kthread_stop_put(uti->tcp_tx); + uti->tcp_tx = NULL; + kthread_stop_put(uti->tcp_rx); + uti->tcp_rx = NULL; + sockfd_put(uti->tcp_socket); + uti->tcp_socket = NULL; +} +EXPORT_SYMBOL_GPL(usbip_unprepare_threads); + static int __init usbip_core_init(void) { return usbip_init_eh();
diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h
index 6e2a80b1633d..6e5394dba2ec 100644
--- a/drivers/usb/usbip/usbip_common.h
+++ b/drivers/usb/usbip/usbip_common.h@@ -316,6 +316,18 @@ void usbip_header_correct_endian(struct usbip_header *pdu, int send); struct usbip_iso_packet_descriptor* usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen); +struct usbip_thread_info { + struct socket *tcp_socket; + struct task_struct *tcp_tx; + struct task_struct *tcp_rx; +}; + +int usbip_prepare_threads(struct usbip_thread_info *uti, + struct usbip_device *ud, int sockfd, + int (*tx_fn)(void *data), const char *tx_name, + int (*rx_fn)(void *data), const char *rx_name); +void usbip_unprepare_threads(struct usbip_thread_info *uti); + /* some members of urb must be substituted before. */ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb); void usbip_pad_iso(struct usbip_device *ud, struct urb *urb);
--
2.18.4