linux/drivers/usb/usbip/usbip_event.c
<<
>>
Prefs
   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
  14struct usbip_event {
  15        struct list_head node;
  16        struct usbip_device *ud;
  17};
  18
  19static 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) {
  71                usbip_dbg_eh("pending event %lx\n", ud->event);
  72
  73                mutex_lock(&ud->sysfs_lock);
  74                /*
  75                 * NOTE: shutdown must come first.
  76                 * Shutdown the device.
  77                 */
  78                if (ud->event & USBIP_EH_SHUTDOWN) {
  79                        ud->eh_ops.shutdown(ud);
  80                        unset_event(ud, USBIP_EH_SHUTDOWN);
  81                }
  82
  83                /* Reset the device. */
  84                if (ud->event & USBIP_EH_RESET) {
  85                        ud->eh_ops.reset(ud);
  86                        unset_event(ud, USBIP_EH_RESET);
  87                }
  88
  89                /* Mark the device as unusable. */
  90                if (ud->event & USBIP_EH_UNUSABLE) {
  91                        ud->eh_ops.unusable(ud);
  92                        unset_event(ud, USBIP_EH_UNUSABLE);
  93                }
  94                mutex_unlock(&ud->sysfs_lock);
  95
  96                wake_up(&ud->eh_waitq);
  97        }
  98}
  99
 100int usbip_start_eh(struct usbip_device *ud)
 101{
 102        init_waitqueue_head(&ud->eh_waitq);
 103        ud->event = 0;
 104        return 0;
 105}
 106EXPORT_SYMBOL_GPL(usbip_start_eh);
 107
 108void usbip_stop_eh(struct usbip_device *ud)
 109{
 110        unsigned long pending = ud->event & ~USBIP_EH_BYE;
 111
 112        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");
 120}
 121EXPORT_SYMBOL_GPL(usbip_stop_eh);
 122
 123#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{
 140        flush_workqueue(usbip_queue);
 141        destroy_workqueue(usbip_queue);
 142        usbip_queue = NULL;
 143}
 144
 145void usbip_event_add(struct usbip_device *ud, unsigned long event)
 146{
 147        struct usbip_event *ue;
 148        unsigned long flags;
 149
 150        if (ud->event & USBIP_EH_BYE)
 151                return;
 152
 153        set_event(ud, event);
 154
 155        spin_lock_irqsave(&event_lock, flags);
 156
 157        list_for_each_entry_reverse(ue, &event_list, node) {
 158                if (ue->ud == ud)
 159                        goto out;
 160        }
 161
 162        ue = kmalloc(sizeof(struct usbip_event), GFP_ATOMIC);
 163        if (ue == NULL)
 164                goto out;
 165
 166        ue->ud = ud;
 167
 168        list_add_tail(&ue->node, &event_list);
 169        queue_work(usbip_queue, &usbip_work);
 170
 171out:
 172        spin_unlock_irqrestore(&event_lock, flags);
 173}
 174EXPORT_SYMBOL_GPL(usbip_event_add);
 175
 176int usbip_event_happened(struct usbip_device *ud)
 177{
 178        int happened = 0;
 179        unsigned long flags;
 180
 181        spin_lock_irqsave(&ud->lock, flags);
 182        if (ud->event != 0)
 183                happened = 1;
 184        spin_unlock_irqrestore(&ud->lock, flags);
 185
 186        return happened;
 187}
 188EXPORT_SYMBOL_GPL(usbip_event_happened);
 189
 190int usbip_in_eh(struct task_struct *task)
 191{
 192        if (task == worker_context)
 193                return 1;
 194
 195        return 0;
 196}
 197EXPORT_SYMBOL_GPL(usbip_in_eh);
 198