1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2003-2008 Takahiro Hirofuchi
4 * Copyright (C) 2015 Nobuo Iwata
7 #include <linux/kthread.h>
8 #include <linux/export.h>
9 #include <linux/slab.h>
10 #include <linux/workqueue.h>
12 #include "usbip_common.h"
15 struct list_head node
;
16 struct usbip_device
*ud
;
19 static DEFINE_SPINLOCK(event_lock
);
20 static LIST_HEAD(event_list
);
22 static void set_event(struct usbip_device
*ud
, unsigned long event
)
26 spin_lock_irqsave(&ud
->lock
, flags
);
28 spin_unlock_irqrestore(&ud
->lock
, flags
);
31 static void unset_event(struct usbip_device
*ud
, unsigned long event
)
35 spin_lock_irqsave(&ud
->lock
, flags
);
37 spin_unlock_irqrestore(&ud
->lock
, flags
);
40 static struct usbip_device
*get_event(void)
42 struct usbip_event
*ue
= NULL
;
43 struct usbip_device
*ud
= NULL
;
46 spin_lock_irqsave(&event_lock
, flags
);
47 if (!list_empty(&event_list
)) {
48 ue
= list_first_entry(&event_list
, struct usbip_event
, node
);
51 spin_unlock_irqrestore(&event_lock
, flags
);
60 static struct task_struct
*worker_context
;
62 static void event_handler(struct work_struct
*work
)
64 struct usbip_device
*ud
;
66 if (worker_context
== NULL
) {
67 worker_context
= current
;
70 while ((ud
= get_event()) != NULL
) {
71 usbip_dbg_eh("pending event %lx\n", ud
->event
);
74 * NOTE: shutdown must come first.
75 * Shutdown the device.
77 if (ud
->event
& USBIP_EH_SHUTDOWN
) {
78 ud
->eh_ops
.shutdown(ud
);
79 unset_event(ud
, USBIP_EH_SHUTDOWN
);
82 /* Reset the device. */
83 if (ud
->event
& USBIP_EH_RESET
) {
85 unset_event(ud
, USBIP_EH_RESET
);
88 /* Mark the device as unusable. */
89 if (ud
->event
& USBIP_EH_UNUSABLE
) {
90 ud
->eh_ops
.unusable(ud
);
91 unset_event(ud
, USBIP_EH_UNUSABLE
);
94 /* Stop the error handler. */
95 if (ud
->event
& USBIP_EH_BYE
)
96 usbip_dbg_eh("removed %p\n", ud
);
98 wake_up(&ud
->eh_waitq
);
102 int usbip_start_eh(struct usbip_device
*ud
)
104 init_waitqueue_head(&ud
->eh_waitq
);
108 EXPORT_SYMBOL_GPL(usbip_start_eh
);
110 void usbip_stop_eh(struct usbip_device
*ud
)
112 unsigned long pending
= ud
->event
& ~USBIP_EH_BYE
;
114 if (!(ud
->event
& USBIP_EH_BYE
))
115 usbip_dbg_eh("usbip_eh stopping but not removed\n");
118 usbip_dbg_eh("usbip_eh waiting completion %lx\n", pending
);
120 wait_event_interruptible(ud
->eh_waitq
, !(ud
->event
& ~USBIP_EH_BYE
));
121 usbip_dbg_eh("usbip_eh has stopped\n");
123 EXPORT_SYMBOL_GPL(usbip_stop_eh
);
125 #define WORK_QUEUE_NAME "usbip_event"
127 static struct workqueue_struct
*usbip_queue
;
128 static DECLARE_WORK(usbip_work
, event_handler
);
130 int usbip_init_eh(void)
132 usbip_queue
= create_singlethread_workqueue(WORK_QUEUE_NAME
);
133 if (usbip_queue
== NULL
) {
134 pr_err("failed to create usbip_event\n");
140 void usbip_finish_eh(void)
142 flush_workqueue(usbip_queue
);
143 destroy_workqueue(usbip_queue
);
147 void usbip_event_add(struct usbip_device
*ud
, unsigned long event
)
149 struct usbip_event
*ue
;
152 if (ud
->event
& USBIP_EH_BYE
)
155 set_event(ud
, event
);
157 spin_lock_irqsave(&event_lock
, flags
);
159 list_for_each_entry_reverse(ue
, &event_list
, node
) {
164 ue
= kmalloc(sizeof(struct usbip_event
), GFP_ATOMIC
);
170 list_add_tail(&ue
->node
, &event_list
);
171 queue_work(usbip_queue
, &usbip_work
);
174 spin_unlock_irqrestore(&event_lock
, flags
);
176 EXPORT_SYMBOL_GPL(usbip_event_add
);
178 int usbip_event_happened(struct usbip_device
*ud
)
183 spin_lock_irqsave(&ud
->lock
, flags
);
186 spin_unlock_irqrestore(&ud
->lock
, flags
);
190 EXPORT_SYMBOL_GPL(usbip_event_happened
);
192 int usbip_in_eh(struct task_struct
*task
)
194 if (task
== worker_context
)
199 EXPORT_SYMBOL_GPL(usbip_in_eh
);