root/drivers/usb/usbip/usbip_event.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. set_event
  2. unset_event
  3. get_event
  4. event_handler
  5. usbip_start_eh
  6. usbip_stop_eh
  7. usbip_init_eh
  8. usbip_finish_eh
  9. usbip_event_add
  10. usbip_event_happened
  11. usbip_in_eh

   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                 wake_up(&ud->eh_waitq);
  95         }
  96 }
  97 
  98 int usbip_start_eh(struct usbip_device *ud)
  99 {
 100         init_waitqueue_head(&ud->eh_waitq);
 101         ud->event = 0;
 102         return 0;
 103 }
 104 EXPORT_SYMBOL_GPL(usbip_start_eh);
 105 
 106 void usbip_stop_eh(struct usbip_device *ud)
 107 {
 108         unsigned long pending = ud->event & ~USBIP_EH_BYE;
 109 
 110         if (!(ud->event & USBIP_EH_BYE))
 111                 usbip_dbg_eh("usbip_eh stopping but not removed\n");
 112 
 113         if (pending)
 114                 usbip_dbg_eh("usbip_eh waiting completion %lx\n", pending);
 115 
 116         wait_event_interruptible(ud->eh_waitq, !(ud->event & ~USBIP_EH_BYE));
 117         usbip_dbg_eh("usbip_eh has stopped\n");
 118 }
 119 EXPORT_SYMBOL_GPL(usbip_stop_eh);
 120 
 121 #define WORK_QUEUE_NAME "usbip_event"
 122 
 123 static struct workqueue_struct *usbip_queue;
 124 static DECLARE_WORK(usbip_work, event_handler);
 125 
 126 int usbip_init_eh(void)
 127 {
 128         usbip_queue = create_singlethread_workqueue(WORK_QUEUE_NAME);
 129         if (usbip_queue == NULL) {
 130                 pr_err("failed to create usbip_event\n");
 131                 return -ENOMEM;
 132         }
 133         return 0;
 134 }
 135 
 136 void usbip_finish_eh(void)
 137 {
 138         flush_workqueue(usbip_queue);
 139         destroy_workqueue(usbip_queue);
 140         usbip_queue = NULL;
 141 }
 142 
 143 void usbip_event_add(struct usbip_device *ud, unsigned long event)
 144 {
 145         struct usbip_event *ue;
 146         unsigned long flags;
 147 
 148         if (ud->event & USBIP_EH_BYE)
 149                 return;
 150 
 151         set_event(ud, event);
 152 
 153         spin_lock_irqsave(&event_lock, flags);
 154 
 155         list_for_each_entry_reverse(ue, &event_list, node) {
 156                 if (ue->ud == ud)
 157                         goto out;
 158         }
 159 
 160         ue = kmalloc(sizeof(struct usbip_event), GFP_ATOMIC);
 161         if (ue == NULL)
 162                 goto out;
 163 
 164         ue->ud = ud;
 165 
 166         list_add_tail(&ue->node, &event_list);
 167         queue_work(usbip_queue, &usbip_work);
 168 
 169 out:
 170         spin_unlock_irqrestore(&event_lock, flags);
 171 }
 172 EXPORT_SYMBOL_GPL(usbip_event_add);
 173 
 174 int usbip_event_happened(struct usbip_device *ud)
 175 {
 176         int happened = 0;
 177         unsigned long flags;
 178 
 179         spin_lock_irqsave(&ud->lock, flags);
 180         if (ud->event != 0)
 181                 happened = 1;
 182         spin_unlock_irqrestore(&ud->lock, flags);
 183 
 184         return happened;
 185 }
 186 EXPORT_SYMBOL_GPL(usbip_event_happened);
 187 
 188 int usbip_in_eh(struct task_struct *task)
 189 {
 190         if (task == worker_context)
 191                 return 1;
 192 
 193         return 0;
 194 }
 195 EXPORT_SYMBOL_GPL(usbip_in_eh);

/* [<][>][^][v][top][bottom][index][help] */