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                /*
  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
  98int usbip_start_eh(struct usbip_device *ud)
  99{
 100        init_waitqueue_head(&ud->eh_waitq);
 101        ud->event = 0;
 102        return 0;
 103}
 104EXPORT_SYMBOL_GPL(usbip_start_eh);
 105
 106void 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}
 119EXPORT_SYMBOL_GPL(usbip_stop_eh);
 120
 121#define WORK_QUEUE_NAME "usbip_event"
 122
 123static struct workqueue_struct *usbip_queue;
 124static DECLARE_WORK(usbip_work, event_handler);
 125
 126int 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
 136void usbip_finish_eh(void)
 137{
 138        flush_workqueue(usbip_queue);
 139        destroy_workqueue(usbip_queue);
 140        usbip_queue = NULL;
 141}
 142
 143void 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
 169out:
 170        spin_unlock_irqrestore(&event_lock, flags);
 171}
 172EXPORT_SYMBOL_GPL(usbip_event_add);
 173
 174int 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}
 186EXPORT_SYMBOL_GPL(usbip_event_happened);
 187
 188int usbip_in_eh(struct task_struct *task)
 189{
 190        if (task == worker_context)
 191                return 1;
 192
 193        return 0;
 194}
 195EXPORT_SYMBOL_GPL(usbip_in_eh);
 196