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
83219820Sjeff#define	INIT_DELAYED_WORK_DEFERRABLE	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
124219820Sjeffstatic inline struct workqueue_struct *
125219820Sjeff_create_workqueue_common(char *name, int cpus)
126219820Sjeff{
127219820Sjeff	struct workqueue_struct *wq;
128219820Sjeff
129219820Sjeff	wq = kmalloc(sizeof(*wq), M_WAITOK);
130219820Sjeff	wq->taskqueue = taskqueue_create((name), M_WAITOK,
131219820Sjeff	    taskqueue_thread_enqueue,  &wq->taskqueue);
132252555Snp	taskqueue_start_threads(&wq->taskqueue, cpus, PWAIT, "%s", name);
133219820Sjeff
134219820Sjeff	return (wq);
135219820Sjeff}
136219820Sjeff
137219820Sjeff
138219820Sjeff#define	create_singlethread_workqueue(name)				\
139219820Sjeff	_create_workqueue_common(name, 1)
140219820Sjeff
141219820Sjeff#define	create_workqueue(name)						\
142219820Sjeff	_create_workqueue_common(name, MAXCPU)
143219820Sjeff
144219820Sjeffstatic inline void
145219820Sjeffdestroy_workqueue(struct workqueue_struct *wq)
146219820Sjeff{
147219820Sjeff	taskqueue_free(wq->taskqueue);
148219820Sjeff	kfree(wq);
149219820Sjeff}
150219820Sjeff
151219820Sjeff#define	flush_workqueue(wq)	flush_taskqueue((wq)->taskqueue)
152219820Sjeff
153219820Sjeffstatic inline void
154219820Sjeff_flush_fn(void *context, int pending)
155219820Sjeff{
156219820Sjeff}
157219820Sjeff
158219820Sjeffstatic inline void
159219820Sjeffflush_taskqueue(struct taskqueue *tq)
160219820Sjeff{
161219820Sjeff	struct task flushtask;
162219820Sjeff
163221055Sjeff	PHOLD(curproc);
164219820Sjeff	TASK_INIT(&flushtask, 0, _flush_fn, NULL);
165219820Sjeff	taskqueue_enqueue(tq, &flushtask);
166219820Sjeff	taskqueue_drain(tq, &flushtask);
167221055Sjeff	PRELE(curproc);
168219820Sjeff}
169219820Sjeff
170219820Sjeffstatic inline int
171219820Sjeffcancel_work_sync(struct work_struct *work)
172219820Sjeff{
173219820Sjeff	if (work->taskqueue &&
174219820Sjeff	    taskqueue_cancel(work->taskqueue, &work->work_task, NULL))
175219820Sjeff		taskqueue_drain(work->taskqueue, &work->work_task);
176219820Sjeff	return 0;
177219820Sjeff}
178219820Sjeff
179219820Sjeff/*
180219820Sjeff * This may leave work running on another CPU as it does on Linux.
181219820Sjeff */
182219820Sjeffstatic inline int
183219820Sjeffcancel_delayed_work(struct delayed_work *work)
184219820Sjeff{
185219820Sjeff
186219820Sjeff	callout_stop(&work->timer);
187250893Sdelphij	if (work->work.taskqueue)
188250893Sdelphij		return (taskqueue_cancel(work->work.taskqueue,
189250893Sdelphij		    &work->work.work_task, NULL) == 0);
190219820Sjeff	return 0;
191219820Sjeff}
192219820Sjeff
193219820Sjeff#endif	/* _LINUX_WORKQUEUE_H_ */
194