cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

usbip_event.c (4002B)


      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	destroy_workqueue(usbip_queue);
    141	usbip_queue = NULL;
    142}
    143
    144void usbip_event_add(struct usbip_device *ud, unsigned long event)
    145{
    146	struct usbip_event *ue;
    147	unsigned long flags;
    148
    149	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);
    172}
    173EXPORT_SYMBOL_GPL(usbip_event_add);
    174
    175int usbip_event_happened(struct usbip_device *ud)
    176{
    177	int happened = 0;
    178	unsigned long flags;
    179
    180	spin_lock_irqsave(&ud->lock, flags);
    181	if (ud->event != 0)
    182		happened = 1;
    183	spin_unlock_irqrestore(&ud->lock, flags);
    184
    185	return happened;
    186}
    187EXPORT_SYMBOL_GPL(usbip_event_happened);
    188
    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);