]> Git Repo - linux.git/blob - drivers/usb/usbip/usbip_event.c
Merge tag 'acpi-fix-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[linux.git] / drivers / usb / usbip / usbip_event.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2003-2008 Takahiro Hirofuchi
4  * Copyright (C) 2015 Nobuo Iwata
5  */
6
7 #include <linux/kthread.h>
8 #include <linux/export.h>
9 #include <linux/slab.h>
10 #include <linux/workqueue.h>
11
12 #include "usbip_common.h"
13
14 struct usbip_event {
15         struct list_head node;
16         struct usbip_device *ud;
17 };
18
19 static DEFINE_SPINLOCK(event_lock);
20 static LIST_HEAD(event_list);
21
22 static 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
31 static 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
40 static 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
60 static struct task_struct *worker_context;
61
62 static 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) {
71                 usbip_dbg_eh("pending event %lx\n", ud->event);
72
73                 /*
74                  * NOTE: shutdown must come first.
75                  * Shutdown the device.
76                  */
77                 if (ud->event & USBIP_EH_SHUTDOWN) {
78                         ud->eh_ops.shutdown(ud);
79                         unset_event(ud, USBIP_EH_SHUTDOWN);
80                 }
81
82                 /* Reset the device. */
83                 if (ud->event & USBIP_EH_RESET) {
84                         ud->eh_ops.reset(ud);
85                         unset_event(ud, USBIP_EH_RESET);
86                 }
87
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);
92                 }
93
94                 /* Stop the error handler. */
95                 if (ud->event & USBIP_EH_BYE)
96                         usbip_dbg_eh("removed %p\n", ud);
97
98                 wake_up(&ud->eh_waitq);
99         }
100 }
101
102 int usbip_start_eh(struct usbip_device *ud)
103 {
104         init_waitqueue_head(&ud->eh_waitq);
105         ud->event = 0;
106         return 0;
107 }
108 EXPORT_SYMBOL_GPL(usbip_start_eh);
109
110 void usbip_stop_eh(struct usbip_device *ud)
111 {
112         unsigned long pending = ud->event & ~USBIP_EH_BYE;
113
114         if (!(ud->event & USBIP_EH_BYE))
115                 usbip_dbg_eh("usbip_eh stopping but not removed\n");
116
117         if (pending)
118                 usbip_dbg_eh("usbip_eh waiting completion %lx\n", pending);
119
120         wait_event_interruptible(ud->eh_waitq, !(ud->event & ~USBIP_EH_BYE));
121         usbip_dbg_eh("usbip_eh has stopped\n");
122 }
123 EXPORT_SYMBOL_GPL(usbip_stop_eh);
124
125 #define WORK_QUEUE_NAME "usbip_event"
126
127 static struct workqueue_struct *usbip_queue;
128 static DECLARE_WORK(usbip_work, event_handler);
129
130 int usbip_init_eh(void)
131 {
132         usbip_queue = create_singlethread_workqueue(WORK_QUEUE_NAME);
133         if (usbip_queue == NULL) {
134                 pr_err("failed to create usbip_event\n");
135                 return -ENOMEM;
136         }
137         return 0;
138 }
139
140 void usbip_finish_eh(void)
141 {
142         flush_workqueue(usbip_queue);
143         destroy_workqueue(usbip_queue);
144         usbip_queue = NULL;
145 }
146
147 void usbip_event_add(struct usbip_device *ud, unsigned long event)
148 {
149         struct usbip_event *ue;
150         unsigned long flags;
151
152         if (ud->event & USBIP_EH_BYE)
153                 return;
154
155         set_event(ud, event);
156
157         spin_lock_irqsave(&event_lock, flags);
158
159         list_for_each_entry_reverse(ue, &event_list, node) {
160                 if (ue->ud == ud)
161                         goto out;
162         }
163
164         ue = kmalloc(sizeof(struct usbip_event), GFP_ATOMIC);
165         if (ue == NULL)
166                 goto out;
167
168         ue->ud = ud;
169
170         list_add_tail(&ue->node, &event_list);
171         queue_work(usbip_queue, &usbip_work);
172
173 out:
174         spin_unlock_irqrestore(&event_lock, flags);
175 }
176 EXPORT_SYMBOL_GPL(usbip_event_add);
177
178 int usbip_event_happened(struct usbip_device *ud)
179 {
180         int happened = 0;
181         unsigned long flags;
182
183         spin_lock_irqsave(&ud->lock, flags);
184         if (ud->event != 0)
185                 happened = 1;
186         spin_unlock_irqrestore(&ud->lock, flags);
187
188         return happened;
189 }
190 EXPORT_SYMBOL_GPL(usbip_event_happened);
191
192 int usbip_in_eh(struct task_struct *task)
193 {
194         if (task == worker_context)
195                 return 1;
196
197         return 0;
198 }
199 EXPORT_SYMBOL_GPL(usbip_in_eh);
This page took 0.043497 seconds and 4 git commands to generate.