1219820Sjeff/*-
2219820Sjeff * Copyright (c) 2010 Isilon Systems, Inc.
3219820Sjeff * Copyright (c) 2010 iX Systems, Inc.
4219820Sjeff * Copyright (c) 2010 Panasas, Inc.
5219820Sjeff * All rights reserved.
6219820Sjeff *
7219820Sjeff * Redistribution and use in source and binary forms, with or without
8219820Sjeff * modification, are permitted provided that the following conditions
9219820Sjeff * are met:
10219820Sjeff * 1. Redistributions of source code must retain the above copyright
11219820Sjeff *    notice unmodified, this list of conditions, and the following
12219820Sjeff *    disclaimer.
13219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright
14219820Sjeff *    notice, this list of conditions and the following disclaimer in the
15219820Sjeff *    documentation and/or other materials provided with the distribution.
16219820Sjeff *
17219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18219820Sjeff * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19219820Sjeff * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20219820Sjeff * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21219820Sjeff * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22219820Sjeff * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23219820Sjeff * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24219820Sjeff * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25219820Sjeff * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26219820Sjeff * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27219820Sjeff */
28219820Sjeff#ifndef	_LINUX_WORKQUEUE_H_
29219820Sjeff#define	_LINUX_WORKQUEUE_H_
30219820Sjeff
31219820Sjeff#include <linux/types.h>
32219820Sjeff#include <linux/kernel.h>
33219820Sjeff#include <linux/timer.h>
34219820Sjeff#include <linux/slab.h>
35219820Sjeff
36219820Sjeff#include <sys/taskqueue.h>
37219820Sjeff
38219820Sjeffstruct workqueue_struct {
39219820Sjeff	struct taskqueue	*taskqueue;
40219820Sjeff};
41219820Sjeff
42219820Sjeffstruct work_struct {
43219820Sjeff	struct	task 		work_task;
44219820Sjeff	struct	taskqueue	*taskqueue;
45219820Sjeff	void			(*fn)(struct work_struct *);
46219820Sjeff};
47219820Sjeff
48219820Sjeffstruct delayed_work {
49219820Sjeff	struct work_struct	work;
50219820Sjeff	struct callout		timer;
51219820Sjeff};
52219820Sjeff
53219820Sjeffstatic inline struct delayed_work *
54219820Sjeffto_delayed_work(struct work_struct *work)
55219820Sjeff{
56219820Sjeff
57219820Sjeff 	return container_of(work, struct delayed_work, work);
58219820Sjeff}
59219820Sjeff
60219820Sjeff
61219820Sjeffstatic inline void
62219820Sjeff_work_fn(void *context, int pending)
63219820Sjeff{
64219820Sjeff	struct work_struct *work;
65219820Sjeff
66219820Sjeff	work = context;
67219820Sjeff	work->fn(work);
68219820Sjeff}
69219820Sjeff
70219820Sjeff#define	INIT_WORK(work, func) 	 					\
71219820Sjeffdo {									\
72219820Sjeff	(work)->fn = (func);						\
73219820Sjeff	(work)->taskqueue = NULL;					\
74219820Sjeff	TASK_INIT(&(work)->work_task, 0, _work_fn, (work));		\
75219820Sjeff} while (0)
76219820Sjeff
77219820Sjeff#define	INIT_DELAYED_WORK(_work, func)					\
78219820Sjeffdo {									\
79219820Sjeff	INIT_WORK(&(_work)->work, func);				\
80219820Sjeff	callout_init(&(_work)->timer, CALLOUT_MPSAFE);			\
81219820Sjeff} while (0)
82219820Sjeff
83255932Salfred#define	INIT_DEFERRABLE_WORK	INIT_DELAYED_WORK
84219820Sjeff
85219820Sjeff#define	schedule_work(work)						\
86219820Sjeffdo {									\
87219820Sjeff	(work)->taskqueue = taskqueue_thread;				\
88219820Sjeff	taskqueue_enqueue(taskqueue_thread, &(work)->work_task);	\
89219820Sjeff} while (0)
90219820Sjeff
91219820Sjeff#define	flush_scheduled_work()	flush_taskqueue(taskqueue_thread)
92219820Sjeff
93219820Sjeff#define	queue_work(q, work)						\
94219820Sjeffdo {									\
95219820Sjeff	(work)->taskqueue = (q)->taskqueue;				\
96219820Sjeff	taskqueue_enqueue((q)->taskqueue, &(work)->work_task);		\
97219820Sjeff} while (0)
98219820Sjeff
99219820Sjeffstatic inline void
100219820Sjeff_delayed_work_fn(void *arg)
101219820Sjeff{
102219820Sjeff	struct delayed_work *work;
103219820Sjeff
104219820Sjeff	work = arg;
105219820Sjeff	taskqueue_enqueue(work->work.taskqueue, &work->work.work_task);
106219820Sjeff}
107219820Sjeff
108219820Sjeffstatic inline int
109219820Sjeffqueue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work,
110219820Sjeff    unsigned long delay)
111219820Sjeff{
112219820Sjeff	int pending;
113219820Sjeff
114219820Sjeff	pending = work->work.work_task.ta_pending;
115219820Sjeff	work->work.taskqueue = wq->taskqueue;
116219820Sjeff	if (delay != 0)
117219820Sjeff		callout_reset(&work->timer, delay, _delayed_work_fn, work);
118219820Sjeff	else
119219820Sjeff		_delayed_work_fn((void *)work);
120219820Sjeff
121219820Sjeff	return (!pending);
122219820Sjeff}
123219820Sjeff
124255932Salfredstatic inline bool schedule_delayed_work(struct delayed_work *dwork,
125255932Salfred                                         unsigned long delay)
126255932Salfred{
127255932Salfred        struct workqueue_struct wq;
128255932Salfred        wq.taskqueue = taskqueue_thread;
129255932Salfred        return queue_delayed_work(&wq, dwork, delay);
130255932Salfred}
131255932Salfred
132219820Sjeffstatic inline struct workqueue_struct *
133219820Sjeff_create_workqueue_common(char *name, int cpus)
134219820Sjeff{
135219820Sjeff	struct workqueue_struct *wq;
136219820Sjeff
137219820Sjeff	wq = kmalloc(sizeof(*wq), M_WAITOK);
138219820Sjeff	wq->taskqueue = taskqueue_create((name), M_WAITOK,
139219820Sjeff	    taskqueue_thread_enqueue,  &wq->taskqueue);
140237563Snp	taskqueue_start_threads(&wq->taskqueue, cpus, PWAIT, "%s", name);
141219820Sjeff
142219820Sjeff	return (wq);
143219820Sjeff}
144219820Sjeff
145219820Sjeff
146219820Sjeff#define	create_singlethread_workqueue(name)				\
147219820Sjeff	_create_workqueue_common(name, 1)
148219820Sjeff
149219820Sjeff#define	create_workqueue(name)						\
150219820Sjeff	_create_workqueue_common(name, MAXCPU)
151219820Sjeff
152219820Sjeffstatic inline void
153219820Sjeffdestroy_workqueue(struct workqueue_struct *wq)
154219820Sjeff{
155219820Sjeff	taskqueue_free(wq->taskqueue);
156219820Sjeff	kfree(wq);
157219820Sjeff}
158219820Sjeff
159219820Sjeff#define	flush_workqueue(wq)	flush_taskqueue((wq)->taskqueue)
160219820Sjeff
161219820Sjeffstatic inline void
162219820Sjeff_flush_fn(void *context, int pending)
163219820Sjeff{
164219820Sjeff}
165219820Sjeff
166219820Sjeffstatic inline void
167219820Sjeffflush_taskqueue(struct taskqueue *tq)
168219820Sjeff{
169219820Sjeff	struct task flushtask;
170219820Sjeff
171221055Sjeff	PHOLD(curproc);
172219820Sjeff	TASK_INIT(&flushtask, 0, _flush_fn, NULL);
173219820Sjeff	taskqueue_enqueue(tq, &flushtask);
174219820Sjeff	taskqueue_drain(tq, &flushtask);
175221055Sjeff	PRELE(curproc);
176219820Sjeff}
177219820Sjeff
178219820Sjeffstatic inline int
179219820Sjeffcancel_work_sync(struct work_struct *work)
180219820Sjeff{
181219820Sjeff	if (work->taskqueue &&
182219820Sjeff	    taskqueue_cancel(work->taskqueue, &work->work_task, NULL))
183219820Sjeff		taskqueue_drain(work->taskqueue, &work->work_task);
184219820Sjeff	return 0;
185219820Sjeff}
186219820Sjeff
187219820Sjeff/*
188219820Sjeff * This may leave work running on another CPU as it does on Linux.
189219820Sjeff */
190219820Sjeffstatic inline int
191219820Sjeffcancel_delayed_work(struct delayed_work *work)
192219820Sjeff{
193219820Sjeff
194219820Sjeff	callout_stop(&work->timer);
195250374Sdelphij	if (work->work.taskqueue)
196250374Sdelphij		return (taskqueue_cancel(work->work.taskqueue,
197250374Sdelphij		    &work->work.work_task, NULL) == 0);
198219820Sjeff	return 0;
199219820Sjeff}
200219820Sjeff
201255932Salfredstatic inline int
202255932Salfredcancel_delayed_work_sync(struct delayed_work *work)
203255932Salfred{
204255932Salfred
205255932Salfred        callout_drain(&work->timer);
206255932Salfred        if (work->work.taskqueue &&
207255932Salfred            taskqueue_cancel(work->work.taskqueue, &work->work.work_task, NULL))
208255932Salfred                taskqueue_drain(work->work.taskqueue, &work->work.work_task);
209255932Salfred        return 0;
210255932Salfred}
211255932Salfred
212219820Sjeff#endif	/* _LINUX_WORKQUEUE_H_ */
213