blob: 26513540bcdbdd4ce19bf7b6e1f0d954e46f0deb [file] [log] [blame]
Greg Kroah-Hartman5fd54ac2017-11-03 11:28:30 +01001// SPDX-License-Identifier: GPL-2.0+
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -06002/*
3 * Copyright (C) 2003-2008 Takahiro Hirofuchi
Nobuo Iwatabb7871a2016-03-24 10:50:59 +09004 * Copyright (C) 2015 Nobuo Iwata
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -06005 */
6
Brian G. Merrellb8868e42009-07-21 00:46:13 -06007#include <linux/kthread.h>
Paul Gortmaker8e336a72011-07-10 13:09:12 -04008#include <linux/export.h>
Nobuo Iwatabb7871a2016-03-24 10:50:59 +09009#include <linux/slab.h>
10#include <linux/workqueue.h>
matt mooney7aaacb42011-05-11 22:33:43 -070011
matt mooney0f798472011-05-06 03:47:47 -070012#include "usbip_common.h"
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -060013
Nobuo Iwatabb7871a2016-03-24 10:50:59 +090014struct usbip_event {
15 struct list_head node;
16 struct usbip_device *ud;
17};
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -060018
Nobuo Iwatabb7871a2016-03-24 10:50:59 +090019static DEFINE_SPINLOCK(event_lock);
20static LIST_HEAD(event_list);
21
22static void set_event(struct usbip_device *ud, unsigned long event)
23{
24 unsigned long flags;
25
26 spin_lock_irqsave(&ud->lock, flags);
27 ud->event |= event;
28 spin_unlock_irqrestore(&ud->lock, flags);
29}
30
31static void unset_event(struct usbip_device *ud, unsigned long event)
32{
33 unsigned long flags;
34
35 spin_lock_irqsave(&ud->lock, flags);
36 ud->event &= ~event;
37 spin_unlock_irqrestore(&ud->lock, flags);
38}
39
40static struct usbip_device *get_event(void)
41{
42 struct usbip_event *ue = NULL;
43 struct usbip_device *ud = NULL;
44 unsigned long flags;
45
46 spin_lock_irqsave(&event_lock, flags);
47 if (!list_empty(&event_list)) {
48 ue = list_first_entry(&event_list, struct usbip_event, node);
49 list_del(&ue->node);
50 }
51 spin_unlock_irqrestore(&event_lock, flags);
52
53 if (ue) {
54 ud = ue->ud;
55 kfree(ue);
56 }
57 return ud;
58}
59
60static struct task_struct *worker_context;
61
62static void event_handler(struct work_struct *work)
63{
64 struct usbip_device *ud;
65
66 if (worker_context == NULL) {
67 worker_context = current;
68 }
69
70 while ((ud = get_event()) != NULL) {
Brian G. Merrellb8868e42009-07-21 00:46:13 -060071 usbip_dbg_eh("pending event %lx\n", ud->event);
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -060072
Shuah Khan363eaa32021-03-29 19:36:51 -060073 mutex_lock(&ud->sysfs_lock);
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -060074 /*
75 * NOTE: shutdown must come first.
76 * Shutdown the device.
77 */
78 if (ud->event & USBIP_EH_SHUTDOWN) {
79 ud->eh_ops.shutdown(ud);
Nobuo Iwatabb7871a2016-03-24 10:50:59 +090080 unset_event(ud, USBIP_EH_SHUTDOWN);
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -060081 }
82
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -060083 /* Reset the device. */
84 if (ud->event & USBIP_EH_RESET) {
85 ud->eh_ops.reset(ud);
Nobuo Iwatabb7871a2016-03-24 10:50:59 +090086 unset_event(ud, USBIP_EH_RESET);
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -060087 }
88
89 /* Mark the device as unusable. */
90 if (ud->event & USBIP_EH_UNUSABLE) {
91 ud->eh_ops.unusable(ud);
Nobuo Iwatabb7871a2016-03-24 10:50:59 +090092 unset_event(ud, USBIP_EH_UNUSABLE);
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -060093 }
Shuah Khan363eaa32021-03-29 19:36:51 -060094 mutex_unlock(&ud->sysfs_lock);
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -060095
Nobuo Iwatabb7871a2016-03-24 10:50:59 +090096 wake_up(&ud->eh_waitq);
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -060097 }
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -060098}
99
Brian G. Merrellb8868e42009-07-21 00:46:13 -0600100int usbip_start_eh(struct usbip_device *ud)
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -0600101{
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -0600102 init_waitqueue_head(&ud->eh_waitq);
103 ud->event = 0;
Brian G. Merrellb8868e42009-07-21 00:46:13 -0600104 return 0;
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -0600105}
106EXPORT_SYMBOL_GPL(usbip_start_eh);
107
108void usbip_stop_eh(struct usbip_device *ud)
109{
Nobuo Iwatabb7871a2016-03-24 10:50:59 +0900110 unsigned long pending = ud->event & ~USBIP_EH_BYE;
Eric Lescouetd01f42a2010-04-24 02:55:24 +0200111
Nobuo Iwatabb7871a2016-03-24 10:50:59 +0900112 if (!(ud->event & USBIP_EH_BYE))
113 usbip_dbg_eh("usbip_eh stopping but not removed\n");
114
115 if (pending)
116 usbip_dbg_eh("usbip_eh waiting completion %lx\n", pending);
117
118 wait_event_interruptible(ud->eh_waitq, !(ud->event & ~USBIP_EH_BYE));
119 usbip_dbg_eh("usbip_eh has stopped\n");
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -0600120}
121EXPORT_SYMBOL_GPL(usbip_stop_eh);
122
Nobuo Iwatabb7871a2016-03-24 10:50:59 +0900123#define WORK_QUEUE_NAME "usbip_event"
124
125static struct workqueue_struct *usbip_queue;
126static DECLARE_WORK(usbip_work, event_handler);
127
128int usbip_init_eh(void)
129{
130 usbip_queue = create_singlethread_workqueue(WORK_QUEUE_NAME);
131 if (usbip_queue == NULL) {
132 pr_err("failed to create usbip_event\n");
133 return -ENOMEM;
134 }
135 return 0;
136}
137
138void usbip_finish_eh(void)
139{
Nobuo Iwatabb7871a2016-03-24 10:50:59 +0900140 destroy_workqueue(usbip_queue);
141 usbip_queue = NULL;
142}
143
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -0600144void usbip_event_add(struct usbip_device *ud, unsigned long event)
145{
Nobuo Iwatabb7871a2016-03-24 10:50:59 +0900146 struct usbip_event *ue;
Harvey Yangdcf147792013-01-22 13:31:30 +0800147 unsigned long flags;
148
Nobuo Iwatabb7871a2016-03-24 10:50:59 +0900149 if (ud->event & USBIP_EH_BYE)
150 return;
151
152 set_event(ud, event);
153
154 spin_lock_irqsave(&event_lock, flags);
155
156 list_for_each_entry_reverse(ue, &event_list, node) {
157 if (ue->ud == ud)
158 goto out;
159 }
160
161 ue = kmalloc(sizeof(struct usbip_event), GFP_ATOMIC);
162 if (ue == NULL)
163 goto out;
164
165 ue->ud = ud;
166
167 list_add_tail(&ue->node, &event_list);
168 queue_work(usbip_queue, &usbip_work);
169
170out:
171 spin_unlock_irqrestore(&event_lock, flags);
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -0600172}
173EXPORT_SYMBOL_GPL(usbip_event_add);
174
Brian G. Merrellb8868e42009-07-21 00:46:13 -0600175int usbip_event_happened(struct usbip_device *ud)
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -0600176{
Brian G. Merrellb8868e42009-07-21 00:46:13 -0600177 int happened = 0;
Andrew Goodbody21619792016-02-02 17:36:39 +0000178 unsigned long flags;
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -0600179
Andrew Goodbody21619792016-02-02 17:36:39 +0000180 spin_lock_irqsave(&ud->lock, flags);
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -0600181 if (ud->event != 0)
Brian G. Merrellb8868e42009-07-21 00:46:13 -0600182 happened = 1;
Andrew Goodbody21619792016-02-02 17:36:39 +0000183 spin_unlock_irqrestore(&ud->lock, flags);
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -0600184
Brian G. Merrellb8868e42009-07-21 00:46:13 -0600185 return happened;
Takahiro Hirofuchi05a1f282008-07-09 14:56:51 -0600186}
Brian G. Merrellb8868e42009-07-21 00:46:13 -0600187EXPORT_SYMBOL_GPL(usbip_event_happened);
Nobuo Iwatabb7871a2016-03-24 10:50:59 +0900188
189int usbip_in_eh(struct task_struct *task)
190{
191 if (task == worker_context)
192 return 1;
193
194 return 0;
195}
196EXPORT_SYMBOL_GPL(usbip_in_eh);