xref: /titanic_44/usr/src/uts/common/fs/zev/zev.c (revision 41aa6ebd93e2fff9852b1fc8361292e2c67b4410)
1a18c35b9SAndreas Jaekel #include <sys/modctl.h>
2a18c35b9SAndreas Jaekel #include <sys/ddi.h>
3a18c35b9SAndreas Jaekel #include <sys/sunddi.h>
4a18c35b9SAndreas Jaekel #include <sys/conf.h>
5a18c35b9SAndreas Jaekel #include <sys/devops.h>
6a18c35b9SAndreas Jaekel #include <sys/stat.h>
7a18c35b9SAndreas Jaekel #include <sys/fs/zev.h>
8a18c35b9SAndreas Jaekel #include <sys/zev_callbacks.h>
9205ed6bfSAndreas Jaekel #include <sys/zev_checksums.h>
1035d4e8ddSAndreas Jaekel #include <sys/zfs_znode.h>
1135d4e8ddSAndreas Jaekel #include <sys/time.h>
1235d4e8ddSAndreas Jaekel #include <sys/sa.h>
1335d4e8ddSAndreas Jaekel #include <sys/zap.h>
14548c8b6eSAndreas Jaekel #include <sys/time.h>
1589a40d3dSAndreas Jaekel #include <sys/fs/dv_node.h>
16a18c35b9SAndreas Jaekel 
17add9520fSAndreas Jaekel #define	OFFSETOF(s, m)		((size_t)(&(((s *)0)->m)))
18add9520fSAndreas Jaekel 
191fbd5e10SAndreas Jaekel #define XSTRING(x)	STRING(x)
20e3455c18SAndreas Jaekel #define STRING(x)	#x
21e3455c18SAndreas Jaekel 
22add9520fSAndreas Jaekel #define ZEV_DEFAULT_QUEUE_NAME		"beaver"
23add9520fSAndreas Jaekel #define ZEV_CONTROL_DEVICE_MINOR	0
249fef2cddSAndreas Jaekel #define ZEV_TMPQUEUE_DEVICE_MINOR	1
259fef2cddSAndreas Jaekel #define ZEV_MINOR_MIN			(ZEV_TMPQUEUE_DEVICE_MINOR + 1)
26add9520fSAndreas Jaekel #define ZEV_MINOR_MAX			(ZEV_MINOR_MIN + ZEV_MAX_QUEUES - 1)
27add9520fSAndreas Jaekel 
28add9520fSAndreas Jaekel typedef struct zev_queue {
29add9520fSAndreas Jaekel 	char			zq_name[ZEV_MAX_QUEUE_NAME_LEN+1];
30add9520fSAndreas Jaekel 	minor_t			zq_minor_number;
31add9520fSAndreas Jaekel 	dev_info_t		*zq_dip;
32add9520fSAndreas Jaekel 	struct pollhead		zq_pollhead;
33add9520fSAndreas Jaekel 	uint64_t		zq_bytes_read;
34add9520fSAndreas Jaekel 	uint64_t		zq_events_read;
35add9520fSAndreas Jaekel 	uint64_t		zq_bytes_discarded;
36add9520fSAndreas Jaekel 	uint64_t		zq_events_discarded;
37add9520fSAndreas Jaekel 	uint64_t		zq_bytes_total;
38add9520fSAndreas Jaekel 	uint64_t		zq_events_total;
39add9520fSAndreas Jaekel 	uint64_t		zq_wakeup_threshold;
40add9520fSAndreas Jaekel 	uint16_t		zq_flags;
41add9520fSAndreas Jaekel 	uint16_t		zq_need_wakeup;
42add9520fSAndreas Jaekel 	/* protected by zev_mutex */
43add9520fSAndreas Jaekel 	int			zq_refcnt;
44add9520fSAndreas Jaekel 	uint64_t		zq_queue_len;
45add9520fSAndreas Jaekel 	uint64_t		zq_queue_messages;
46add9520fSAndreas Jaekel 	uint64_t		zq_max_queue_len;
47add9520fSAndreas Jaekel 	zev_msg_t		*zq_oldest;
48add9520fSAndreas Jaekel 	boolean_t		zq_busy;
49add9520fSAndreas Jaekel 	boolean_t		zq_to_be_removed;
50add9520fSAndreas Jaekel 	zev_statistics_t	zq_statistics;
516a6a51eeSAndreas Jaekel 	kcondvar_t		zq_condvar;
52add9520fSAndreas Jaekel } zev_queue_t;
53a18c35b9SAndreas Jaekel 
54a18c35b9SAndreas Jaekel static void		*statep;
55a18c35b9SAndreas Jaekel struct pollhead		zev_pollhead;
56a18c35b9SAndreas Jaekel 
57a18c35b9SAndreas Jaekel kmutex_t		zev_mutex;
58a18c35b9SAndreas Jaekel kcondvar_t		zev_condvar;
596a6a51eeSAndreas Jaekel kmutex_t		zev_queue_msg_mutex;
60a18c35b9SAndreas Jaekel krwlock_t		zev_pool_list_rwlock;
61a18c35b9SAndreas Jaekel static zev_statistics_t	zev_statistics;
62add9520fSAndreas Jaekel static boolean_t	zev_attached;
63888fea18SAndreas Jaekel static kmutex_t		zev_mark_id_mutex;
64888fea18SAndreas Jaekel static uint64_t		zev_mark_id = 0;
65a18c35b9SAndreas Jaekel 
66add9520fSAndreas Jaekel static uint64_t		zev_msg_sequence_number = 0;
676a6a51eeSAndreas Jaekel static zev_queue_t	*zev_queues[ZEV_MAX_QUEUES];
686a6a51eeSAndreas Jaekel static int		zev_queue_cnt = 0;
699720aba3SAndreas Jaekel static int		zev_have_blocking_queues = 1;
709fef2cddSAndreas Jaekel static int		zev_tmpqueue_num = 0;
71add9520fSAndreas Jaekel 
72add9520fSAndreas Jaekel uint64_t	zev_memory_allocated = 0;
73add9520fSAndreas Jaekel uint64_t	zev_memory_freed = 0;
74add9520fSAndreas Jaekel 
75a18c35b9SAndreas Jaekel /*
76a18c35b9SAndreas Jaekel  * The longest potential message is from zev_zfs_mount() and
77a18c35b9SAndreas Jaekel  * contains the mountpoint, which might be close to MAXPATHLEN bytes long.
78a18c35b9SAndreas Jaekel  *
79a18c35b9SAndreas Jaekel  * Another candidate is zev_znode_rename_cb() and contains three inode
80a18c35b9SAndreas Jaekel  * numbers and two filenames of up to MAXNAMELEN bytes each.
81a18c35b9SAndreas Jaekel  */
82a18c35b9SAndreas Jaekel #define ZEV_MAX_MESSAGE_LEN	4096
83a18c35b9SAndreas Jaekel 
84aafc540fSAndreas Jaekel static zev_msg_t *zev_queue_head = NULL;
85aafc540fSAndreas Jaekel static zev_msg_t *zev_queue_tail = NULL;
86aafc540fSAndreas Jaekel static uint64_t zev_queue_len = 0;
87aafc540fSAndreas Jaekel 
88a18c35b9SAndreas Jaekel 
89a18c35b9SAndreas Jaekel typedef struct zev_pool_list_entry {
90a18c35b9SAndreas Jaekel 	struct zev_pool_list_entry	*next;
91a18c35b9SAndreas Jaekel 	char				name[MAXPATHLEN];
92a18c35b9SAndreas Jaekel } zev_pool_list_entry_t;
93a18c35b9SAndreas Jaekel 
94a18c35b9SAndreas Jaekel static zev_pool_list_entry_t *zev_muted_pools_head = NULL;
95a18c35b9SAndreas Jaekel 
96add9520fSAndreas Jaekel static volatile int zev_wakeup_thread_run = 1;
97add9520fSAndreas Jaekel static kthread_t *zev_poll_wakeup_thread = NULL;
98add9520fSAndreas Jaekel 
99205ed6bfSAndreas Jaekel void *
zev_alloc(ssize_t sz)100205ed6bfSAndreas Jaekel zev_alloc(ssize_t sz)
101205ed6bfSAndreas Jaekel {
102205ed6bfSAndreas Jaekel 	ZEV_MEM_ADD(sz);
103205ed6bfSAndreas Jaekel 	return kmem_alloc(sz, KM_SLEEP);
104205ed6bfSAndreas Jaekel }
105205ed6bfSAndreas Jaekel 
106205ed6bfSAndreas Jaekel void *
zev_zalloc(ssize_t sz)107205ed6bfSAndreas Jaekel zev_zalloc(ssize_t sz)
108205ed6bfSAndreas Jaekel {
109205ed6bfSAndreas Jaekel 	ZEV_MEM_ADD(sz);
110205ed6bfSAndreas Jaekel 	return kmem_zalloc(sz, KM_SLEEP);
111205ed6bfSAndreas Jaekel }
112205ed6bfSAndreas Jaekel 
113205ed6bfSAndreas Jaekel void
zev_free(void * ptr,ssize_t sz)114205ed6bfSAndreas Jaekel zev_free(void *ptr, ssize_t sz)
115205ed6bfSAndreas Jaekel {
116205ed6bfSAndreas Jaekel 	ZEV_MEM_SUB(sz);						\
117205ed6bfSAndreas Jaekel 	kmem_free(ptr, sz);
118205ed6bfSAndreas Jaekel }
119205ed6bfSAndreas Jaekel 
1209720aba3SAndreas Jaekel /* must be called with zev_mutex held */
1219720aba3SAndreas Jaekel static void
zev_update_blockflag(void)1229720aba3SAndreas Jaekel zev_update_blockflag(void)
1239720aba3SAndreas Jaekel {
1249720aba3SAndreas Jaekel 	zev_queue_t *q;
1259720aba3SAndreas Jaekel 	int had_blocking_queues;
1269720aba3SAndreas Jaekel 	int i;
1279720aba3SAndreas Jaekel 
1289720aba3SAndreas Jaekel 	had_blocking_queues = zev_have_blocking_queues;
1299720aba3SAndreas Jaekel 
1309720aba3SAndreas Jaekel 	/* do we still have blocking queues? */
1319720aba3SAndreas Jaekel 	zev_have_blocking_queues = 0;
1329720aba3SAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
1339720aba3SAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
1349720aba3SAndreas Jaekel 		if (!q)
1359720aba3SAndreas Jaekel 			continue;
1369720aba3SAndreas Jaekel 		if (q->zq_flags & ZEV_FL_BLOCK_WHILE_QUEUE_FULL) {
1379720aba3SAndreas Jaekel 			zev_have_blocking_queues = 1;
1389720aba3SAndreas Jaekel 			break;
1399720aba3SAndreas Jaekel 		}
1409720aba3SAndreas Jaekel 	}
1419720aba3SAndreas Jaekel 	/* no blocking queues */
1429720aba3SAndreas Jaekel 	if (had_blocking_queues)
1439720aba3SAndreas Jaekel 		cv_broadcast(&zev_condvar);
1449720aba3SAndreas Jaekel }
1459720aba3SAndreas Jaekel 
146add9520fSAndreas Jaekel int
zev_queue_cmp(const void * a,const void * b)147add9520fSAndreas Jaekel zev_queue_cmp(const void *a, const void *b)
148add9520fSAndreas Jaekel {
149add9520fSAndreas Jaekel 	const zev_queue_t *qa = a;
150add9520fSAndreas Jaekel 	const zev_queue_t *qb = b;
151add9520fSAndreas Jaekel 	if (qa->zq_minor_number > qb->zq_minor_number)
152add9520fSAndreas Jaekel 		return 1;
153add9520fSAndreas Jaekel 	if (qa->zq_minor_number < qb->zq_minor_number)
154add9520fSAndreas Jaekel 		return -1;
155add9520fSAndreas Jaekel 	return 0;
156add9520fSAndreas Jaekel }
157add9520fSAndreas Jaekel 
158add9520fSAndreas Jaekel /* must be called with zev_mutex held */
159add9520fSAndreas Jaekel void
zev_queue_trim(void)160add9520fSAndreas Jaekel zev_queue_trim(void)
161add9520fSAndreas Jaekel {
162add9520fSAndreas Jaekel 	zev_msg_t *m;
163add9520fSAndreas Jaekel 	uint64_t oldest_message;
164add9520fSAndreas Jaekel 	zev_queue_t *q;
1656a6a51eeSAndreas Jaekel 	int i;
166add9520fSAndreas Jaekel 
167add9520fSAndreas Jaekel 	if (!zev_queue_tail)
168add9520fSAndreas Jaekel 		return;
169add9520fSAndreas Jaekel 
170add9520fSAndreas Jaekel 	oldest_message = zev_queue_tail->seq + 1;  /* does not exist, yet. */
1716a6a51eeSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
1726a6a51eeSAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
1736a6a51eeSAndreas Jaekel 		if (q == NULL)
1746a6a51eeSAndreas Jaekel 			continue;
175add9520fSAndreas Jaekel 		if (!q->zq_oldest)
176add9520fSAndreas Jaekel 			continue;
177add9520fSAndreas Jaekel 		if (oldest_message > q->zq_oldest->seq)
178add9520fSAndreas Jaekel 			oldest_message = q->zq_oldest->seq;
179add9520fSAndreas Jaekel 	}
180add9520fSAndreas Jaekel 
181add9520fSAndreas Jaekel 	/* remove msgs between oldest_message and zev_queue_head */
182add9520fSAndreas Jaekel 	while(zev_queue_head && (oldest_message > zev_queue_head->seq)) {
183add9520fSAndreas Jaekel 		m = zev_queue_head;
184add9520fSAndreas Jaekel 		zev_queue_head = m->next;
185add9520fSAndreas Jaekel 		if (zev_queue_head == NULL) {
186add9520fSAndreas Jaekel 			zev_queue_tail = NULL;
187add9520fSAndreas Jaekel 		} else {
188add9520fSAndreas Jaekel 			zev_queue_head->prev = NULL;
189add9520fSAndreas Jaekel 		}
190add9520fSAndreas Jaekel 		if (m->read == 0) {
191add9520fSAndreas Jaekel 			zev_statistics.zev_bytes_discarded += m->size;
192add9520fSAndreas Jaekel 			zev_statistics.zev_cnt_discarded_events++;
193add9520fSAndreas Jaekel 		}
194add9520fSAndreas Jaekel 		zev_statistics.zev_queue_len -= m->size;
195add9520fSAndreas Jaekel 		zev_queue_len--;
196205ed6bfSAndreas Jaekel 		zev_free(m, sizeof(*m) + m->size);
197add9520fSAndreas Jaekel 	}
198add9520fSAndreas Jaekel }
199add9520fSAndreas Jaekel 
200add9520fSAndreas Jaekel /* must be called with zev_mutex held */
201add9520fSAndreas Jaekel static void
zev_queue_hold(zev_queue_t * q)202add9520fSAndreas Jaekel zev_queue_hold(zev_queue_t *q)
203add9520fSAndreas Jaekel {
204add9520fSAndreas Jaekel 	q->zq_refcnt++;
205add9520fSAndreas Jaekel }
206add9520fSAndreas Jaekel 
207add9520fSAndreas Jaekel /* must be called with zev_mutex held */
208add9520fSAndreas Jaekel static void
zev_queue_release(zev_queue_t * q)209add9520fSAndreas Jaekel zev_queue_release(zev_queue_t *q)
210add9520fSAndreas Jaekel {
211add9520fSAndreas Jaekel 	q->zq_refcnt--;
212add9520fSAndreas Jaekel 	if (q->zq_refcnt > 0)
213add9520fSAndreas Jaekel 		return;
214add9520fSAndreas Jaekel 
215add9520fSAndreas Jaekel 	ASSERT(q->zq_busy == B_FALSE);
216add9520fSAndreas Jaekel 
217add9520fSAndreas Jaekel 	/* persistent queues will not be removed */
218add9520fSAndreas Jaekel 	if ((q->zq_flags & ZEV_FL_PERSISTENT) != 0)
219add9520fSAndreas Jaekel 		return;
220add9520fSAndreas Jaekel 
221add9520fSAndreas Jaekel 	/* remove queue from queue list */
2226a6a51eeSAndreas Jaekel 	zev_queues[q->zq_minor_number - ZEV_MINOR_MIN] = NULL;
223add9520fSAndreas Jaekel 
224add9520fSAndreas Jaekel 	/* discard messages that no queue references anymore */
225add9520fSAndreas Jaekel 	zev_queue_trim();
226add9520fSAndreas Jaekel 
2276a6a51eeSAndreas Jaekel 	cv_destroy(&q->zq_condvar);
228add9520fSAndreas Jaekel 	ddi_remove_minor_node(q->zq_dip, q->zq_name);
229cfdb9b63SAndreas Jaekel 	devfs_clean(ddi_root_node() ? ddi_root_node() : q->zq_dip,
230cfdb9b63SAndreas Jaekel 	            NULL, DV_CLEAN_FORCE);
231add9520fSAndreas Jaekel 	ddi_soft_state_free(statep, q->zq_minor_number);
232add9520fSAndreas Jaekel 	ZEV_MEM_SUB(sizeof(zev_queue_t));
2336a6a51eeSAndreas Jaekel 	zev_queue_cnt--;
2349720aba3SAndreas Jaekel 	zev_update_blockflag();
235add9520fSAndreas Jaekel }
236add9520fSAndreas Jaekel 
237add9520fSAndreas Jaekel int
zev_queue_new(zev_queue_t ** queue,dev_info_t * dip,char * name,uint64_t max_queue_len,uint16_t flags)238add9520fSAndreas Jaekel zev_queue_new(zev_queue_t **queue,
239add9520fSAndreas Jaekel               dev_info_t *dip,
240add9520fSAndreas Jaekel               char *name,
241add9520fSAndreas Jaekel               uint64_t max_queue_len,
242add9520fSAndreas Jaekel               uint16_t flags)
243add9520fSAndreas Jaekel {
244add9520fSAndreas Jaekel 	zev_queue_t *q;
245add9520fSAndreas Jaekel 	zev_queue_t *tmp;
246add9520fSAndreas Jaekel 	zev_msg_t *msg;
247add9520fSAndreas Jaekel 	int name_exists = 0;
248add9520fSAndreas Jaekel 	minor_t minor;
249add9520fSAndreas Jaekel 	char *p;
2506a6a51eeSAndreas Jaekel 	int i;
251add9520fSAndreas Jaekel 
252add9520fSAndreas Jaekel 	if (max_queue_len > ZEV_MAX_QUEUE_LEN)
253add9520fSAndreas Jaekel 		return EINVAL;
254add9520fSAndreas Jaekel 	if (max_queue_len == 0)
255add9520fSAndreas Jaekel 		max_queue_len = ZEV_MAX_QUEUE_LEN;
256add9520fSAndreas Jaekel 	if (!strcmp(name, ZEV_CONTROL_DEVICE_NAME))
257add9520fSAndreas Jaekel 		return EINVAL;
258add9520fSAndreas Jaekel 	for (p = name; *p; p++) {
259add9520fSAndreas Jaekel 		if (*p >= 'a' && *p <= 'z')
260add9520fSAndreas Jaekel 			continue;
261add9520fSAndreas Jaekel 		if (*p >= '0' && *p <= '9')
262add9520fSAndreas Jaekel 			continue;
263add9520fSAndreas Jaekel 		if (*p == '.')
264add9520fSAndreas Jaekel 			continue;
265add9520fSAndreas Jaekel 		return EINVAL;
266add9520fSAndreas Jaekel 	}
267add9520fSAndreas Jaekel 
268add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
269add9520fSAndreas Jaekel 
270add9520fSAndreas Jaekel 	/* find free minor number.*/
271add9520fSAndreas Jaekel 	/* if this were a frequent operation we'd have a free-minor list */
2726a6a51eeSAndreas Jaekel 	for (minor = ZEV_MINOR_MIN; minor <= ZEV_MINOR_MAX; minor++) {
2736a6a51eeSAndreas Jaekel 		tmp = zev_queues[minor - ZEV_MINOR_MIN];
2746a6a51eeSAndreas Jaekel 		if (tmp == NULL)
275add9520fSAndreas Jaekel 			break;
276add9520fSAndreas Jaekel 	}
2776a6a51eeSAndreas Jaekel 	if (tmp) {
278add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
279add9520fSAndreas Jaekel 		return ENOSPC;
280add9520fSAndreas Jaekel 	}
281add9520fSAndreas Jaekel 
282add9520fSAndreas Jaekel 	if (ddi_soft_state_zalloc(statep, minor) != DDI_SUCCESS) {
283add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
284add9520fSAndreas Jaekel 		return ENOSPC;
285add9520fSAndreas Jaekel 	}
286add9520fSAndreas Jaekel 	ZEV_MEM_ADD(sizeof(zev_queue_t));
287add9520fSAndreas Jaekel 
288add9520fSAndreas Jaekel 	q = ddi_get_soft_state(statep, minor);
289add9520fSAndreas Jaekel 	memset(q, 0, sizeof(*q));
290add9520fSAndreas Jaekel 	strncpy(q->zq_name, name, ZEV_MAX_QUEUE_NAME_LEN);
291add9520fSAndreas Jaekel 	q->zq_name[ZEV_MAX_QUEUE_NAME_LEN] = '\0';
292add9520fSAndreas Jaekel 	q->zq_max_queue_len = max_queue_len;
2936a6a51eeSAndreas Jaekel 	q->zq_wakeup_threshold = ZEV_DEFAULT_POLL_WAKEUP_QUEUE_LEN;
294add9520fSAndreas Jaekel 	q->zq_flags = flags;
295add9520fSAndreas Jaekel 	q->zq_refcnt = 1;
296add9520fSAndreas Jaekel 	q->zq_dip = dip;
297add9520fSAndreas Jaekel 	q->zq_minor_number = minor;
2986a6a51eeSAndreas Jaekel 	cv_init(&q->zq_condvar, NULL, CV_DRIVER, NULL);
299add9520fSAndreas Jaekel 
300add9520fSAndreas Jaekel 	/* insert into queue list */
3016a6a51eeSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
302add9520fSAndreas Jaekel 		/* if this were a frequent operation we'd have a name tree */
3036a6a51eeSAndreas Jaekel 		if (zev_queues[i - ZEV_MINOR_MIN] == NULL)
3046a6a51eeSAndreas Jaekel 			continue;
3056a6a51eeSAndreas Jaekel 		if (!strcmp(q->zq_name, zev_queues[i-ZEV_MINOR_MIN]->zq_name)) {
306add9520fSAndreas Jaekel 			name_exists = 1;
307add9520fSAndreas Jaekel 			break;
308add9520fSAndreas Jaekel 		}
309add9520fSAndreas Jaekel 	}
310add9520fSAndreas Jaekel 	if (name_exists) {
311add9520fSAndreas Jaekel 		ddi_soft_state_free(statep, minor);
312add9520fSAndreas Jaekel 		ZEV_MEM_SUB(sizeof(zev_queue_t));
313add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
314add9520fSAndreas Jaekel 		return EEXIST;
315add9520fSAndreas Jaekel 	}
3166a6a51eeSAndreas Jaekel 	zev_queues[minor - ZEV_MINOR_MIN] = q;
3176a6a51eeSAndreas Jaekel 	zev_queue_cnt++;
318add9520fSAndreas Jaekel 
319add9520fSAndreas Jaekel 	/* calculate current queue len and find head and tail */
320c99a1a25SAndreas Jaekel 	if (!(q->zq_flags & ZEV_FL_INITIALLY_EMPTY)) {
321add9520fSAndreas Jaekel 		q->zq_oldest = zev_queue_tail;
322add9520fSAndreas Jaekel 		msg = zev_queue_tail;
323c99a1a25SAndreas Jaekel 		while ((msg) && (q->zq_queue_len < q->zq_max_queue_len)) {
324add9520fSAndreas Jaekel 			q->zq_queue_len += msg->size;
325add9520fSAndreas Jaekel 			q->zq_queue_messages++;
326add9520fSAndreas Jaekel 			q->zq_oldest = msg;
327add9520fSAndreas Jaekel 			msg = msg->prev;
328add9520fSAndreas Jaekel 		}
329c99a1a25SAndreas Jaekel 	}
330add9520fSAndreas Jaekel 
3319f911cfeSAndreas Jaekel 	zev_update_blockflag();
3329f911cfeSAndreas Jaekel 
333add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
334add9520fSAndreas Jaekel 
335add9520fSAndreas Jaekel 	if (ddi_create_minor_node(dip, name,
336add9520fSAndreas Jaekel 	    S_IFCHR, minor, DDI_PSEUDO, 0) == DDI_FAILURE) {
337add9520fSAndreas Jaekel 		mutex_enter(&zev_mutex);
3386a6a51eeSAndreas Jaekel 		zev_queues[minor - ZEV_MINOR_MIN] = NULL;
3396a6a51eeSAndreas Jaekel 		zev_queue_cnt--;
340add9520fSAndreas Jaekel 		ddi_soft_state_free(statep, minor);
341add9520fSAndreas Jaekel 		ZEV_MEM_SUB(sizeof(zev_queue_t));
3429f911cfeSAndreas Jaekel 		zev_update_blockflag();
343add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
344add9520fSAndreas Jaekel 		return EFAULT;
345add9520fSAndreas Jaekel 	}
346add9520fSAndreas Jaekel 
347add9520fSAndreas Jaekel 	*queue = q;
348add9520fSAndreas Jaekel 	return 0;
349add9520fSAndreas Jaekel }
350add9520fSAndreas Jaekel 
3518657eef9SAndreas Jaekel /*
3528657eef9SAndreas Jaekel  * poll() wakeup thread.  Used to check periodically whether we have
3538657eef9SAndreas Jaekel  * bytes left in the queue that have not yet been made into a
3548657eef9SAndreas Jaekel  * pollwakeup() call.  This is meant to insure a maximum waiting
3558657eef9SAndreas Jaekel  * time until an event is presented as a poll wakeup, while at
3568657eef9SAndreas Jaekel  * the same time not making every single event into a poll wakeup
3578657eef9SAndreas Jaekel  * of it's own.
3588657eef9SAndreas Jaekel  */
3598657eef9SAndreas Jaekel 
3608657eef9SAndreas Jaekel static void
zev_poll_wakeup(boolean_t flush_all)3616a6a51eeSAndreas Jaekel zev_poll_wakeup(boolean_t flush_all)
3628657eef9SAndreas Jaekel {
363add9520fSAndreas Jaekel 	zev_queue_t *q;
3646a6a51eeSAndreas Jaekel 	int i;
365add9520fSAndreas Jaekel 
3666a6a51eeSAndreas Jaekel 	/*
3676a6a51eeSAndreas Jaekel 	 * This loop works with hold() and release() because
3686a6a51eeSAndreas Jaekel 	 * pollwakeup() requires us to release our locks before calling it.
3696a6a51eeSAndreas Jaekel 	 *
3706a6a51eeSAndreas Jaekel 	 * from pollwakeup(9F):
3716a6a51eeSAndreas Jaekel 	 *
3726a6a51eeSAndreas Jaekel 	 *   "Driver defined locks should not be held across calls
3736a6a51eeSAndreas Jaekel 	 *    to this function."
3746a6a51eeSAndreas Jaekel 	 */
375add9520fSAndreas Jaekel 
376add9520fSAndreas Jaekel 	/* wake up threads for each individual queue */
3778657eef9SAndreas Jaekel 	mutex_enter(&zev_mutex);
3786a6a51eeSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
3796a6a51eeSAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
3806a6a51eeSAndreas Jaekel 		if (q == NULL)
3816a6a51eeSAndreas Jaekel 			continue;
3826a6a51eeSAndreas Jaekel 		if (!q->zq_busy)
3836a6a51eeSAndreas Jaekel 			continue;
3846a6a51eeSAndreas Jaekel 		if (!q->zq_queue_len)
3856a6a51eeSAndreas Jaekel 			continue;
3866a6a51eeSAndreas Jaekel 		if ((flush_all) ||
3876a6a51eeSAndreas Jaekel 		    (q->zq_queue_len > q->zq_wakeup_threshold)) {
388add9520fSAndreas Jaekel 			zev_queue_hold(q);
3896a6a51eeSAndreas Jaekel 			mutex_exit(&zev_mutex);
3906a6a51eeSAndreas Jaekel 			pollwakeup(&q->zq_pollhead, POLLIN);
3916a6a51eeSAndreas Jaekel 			mutex_enter(&zev_mutex);
3926a6a51eeSAndreas Jaekel 			zev_queue_release(q);
393add9520fSAndreas Jaekel 		}
394add9520fSAndreas Jaekel 	}
3958657eef9SAndreas Jaekel 	mutex_exit(&zev_mutex);
396add9520fSAndreas Jaekel }
3976a6a51eeSAndreas Jaekel 
3986a6a51eeSAndreas Jaekel static void
zev_poll_wakeup_thread_main(void)3996a6a51eeSAndreas Jaekel zev_poll_wakeup_thread_main(void)
4006a6a51eeSAndreas Jaekel {
4016a6a51eeSAndreas Jaekel 	while (zev_wakeup_thread_run) {
4026a6a51eeSAndreas Jaekel 		delay(drv_usectohz(100 * 1000)); /* sleep 100ms */
4036a6a51eeSAndreas Jaekel 
4046a6a51eeSAndreas Jaekel 		zev_poll_wakeup(B_TRUE);
4058657eef9SAndreas Jaekel 	}
4068657eef9SAndreas Jaekel 	thread_exit();
4078657eef9SAndreas Jaekel }
4088657eef9SAndreas Jaekel 
409a18c35b9SAndreas Jaekel static int
zev_ioc_mute_pool(char * poolname)410a18c35b9SAndreas Jaekel zev_ioc_mute_pool(char *poolname)
411a18c35b9SAndreas Jaekel {
412a18c35b9SAndreas Jaekel 	zev_pool_list_entry_t *pe;
413a18c35b9SAndreas Jaekel 	rw_enter(&zev_pool_list_rwlock, RW_WRITER);
414a18c35b9SAndreas Jaekel 	/* pool already muted? */
415a18c35b9SAndreas Jaekel 	for (pe=zev_muted_pools_head; pe; pe=pe->next) {
416a18c35b9SAndreas Jaekel 		if (!strcmp(pe->name, poolname)) {
417a18c35b9SAndreas Jaekel 			rw_exit(&zev_pool_list_rwlock);
418a18c35b9SAndreas Jaekel 			return EEXIST;
419a18c35b9SAndreas Jaekel 		}
420a18c35b9SAndreas Jaekel 	}
421205ed6bfSAndreas Jaekel 	pe = zev_zalloc(sizeof(*pe));
422a18c35b9SAndreas Jaekel 	if (!pe) {
423a18c35b9SAndreas Jaekel 		rw_exit(&zev_pool_list_rwlock);
424a18c35b9SAndreas Jaekel 		return ENOMEM;
425a18c35b9SAndreas Jaekel 	}
426548c8b6eSAndreas Jaekel 	(void) strncpy(pe->name, poolname, sizeof(pe->name));
427a18c35b9SAndreas Jaekel 	pe->next = zev_muted_pools_head;
428a18c35b9SAndreas Jaekel 	zev_muted_pools_head = pe;
429a18c35b9SAndreas Jaekel 	rw_exit(&zev_pool_list_rwlock);
430a18c35b9SAndreas Jaekel 	return (0);
431a18c35b9SAndreas Jaekel }
432a18c35b9SAndreas Jaekel 
433a18c35b9SAndreas Jaekel static int
zev_ioc_unmute_pool(char * poolname)434a18c35b9SAndreas Jaekel zev_ioc_unmute_pool(char *poolname)
435a18c35b9SAndreas Jaekel {
436a18c35b9SAndreas Jaekel 	zev_pool_list_entry_t *pe, *peprev;
4376a6a51eeSAndreas Jaekel 
438a18c35b9SAndreas Jaekel 	rw_enter(&zev_pool_list_rwlock, RW_WRITER);
439a18c35b9SAndreas Jaekel 	/* pool muted? */
440a18c35b9SAndreas Jaekel 	peprev = NULL;
441a18c35b9SAndreas Jaekel 	for (pe=zev_muted_pools_head; pe; pe=pe->next) {
4426a6a51eeSAndreas Jaekel 		if (!strcmp(pe->name, poolname))
4436a6a51eeSAndreas Jaekel 			break;
444a18c35b9SAndreas Jaekel 		peprev = pe;
445a18c35b9SAndreas Jaekel 	}
4466a6a51eeSAndreas Jaekel 	if (pe) {
447a18c35b9SAndreas Jaekel 		rw_exit(&zev_pool_list_rwlock);
448a18c35b9SAndreas Jaekel 		return ENOENT;
4496a6a51eeSAndreas Jaekel 	}
4506a6a51eeSAndreas Jaekel 
451a18c35b9SAndreas Jaekel 	if (peprev != NULL) {
452a18c35b9SAndreas Jaekel 		peprev->next = pe->next;
453a18c35b9SAndreas Jaekel 	} else {
454a18c35b9SAndreas Jaekel 		zev_muted_pools_head = pe->next;
455a18c35b9SAndreas Jaekel 	}
456205ed6bfSAndreas Jaekel 	zev_free(pe, sizeof(*pe));
457a18c35b9SAndreas Jaekel 	rw_exit(&zev_pool_list_rwlock);
458a18c35b9SAndreas Jaekel 	return (0);
459a18c35b9SAndreas Jaekel }
460a18c35b9SAndreas Jaekel 
461a18c35b9SAndreas Jaekel int
zev_skip_pool(objset_t * os)462a18c35b9SAndreas Jaekel zev_skip_pool(objset_t *os)
463a18c35b9SAndreas Jaekel {
464a18c35b9SAndreas Jaekel 	zev_pool_list_entry_t *pe;
465a18c35b9SAndreas Jaekel 	dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool;
466a18c35b9SAndreas Jaekel 	rw_enter(&zev_pool_list_rwlock, RW_READER);
467a18c35b9SAndreas Jaekel 	for (pe=zev_muted_pools_head; pe; pe=pe->next) {
468a18c35b9SAndreas Jaekel 		if (!strcmp(pe->name, dp->dp_spa->spa_name)) {
469a18c35b9SAndreas Jaekel 			rw_exit(&zev_pool_list_rwlock);
470a18c35b9SAndreas Jaekel 			return 1;
471a18c35b9SAndreas Jaekel 		}
472a18c35b9SAndreas Jaekel 	}
473a18c35b9SAndreas Jaekel 	rw_exit(&zev_pool_list_rwlock);
474a18c35b9SAndreas Jaekel 	return 0;
475a18c35b9SAndreas Jaekel }
476a18c35b9SAndreas Jaekel 
4773655089bSAndreas Jaekel int
zev_skip_fs(zfsvfs_t * fs)4783655089bSAndreas Jaekel zev_skip_fs(zfsvfs_t *fs)
4793655089bSAndreas Jaekel {
4803655089bSAndreas Jaekel 	dsl_dir_t *d = fs->z_os->os_dsl_dataset->ds_dir;
4813655089bSAndreas Jaekel 	dsl_dir_t *prev = NULL;
4823655089bSAndreas Jaekel 
4833655089bSAndreas Jaekel 	while (d && d != prev) {
4843655089bSAndreas Jaekel 		if (strstr(d->dd_myname, "_root"))
4853655089bSAndreas Jaekel 			return 0;
4863655089bSAndreas Jaekel 		prev = d;
4873655089bSAndreas Jaekel 		d = d->dd_parent;
4883655089bSAndreas Jaekel 	}
4893655089bSAndreas Jaekel 	return 1;
4903655089bSAndreas Jaekel }
4913655089bSAndreas Jaekel 
492add9520fSAndreas Jaekel static void
zev_update_statistics(int op,zev_statistics_t * stat)493add9520fSAndreas Jaekel zev_update_statistics(int op, zev_statistics_t *stat)
494add9520fSAndreas Jaekel {
495add9520fSAndreas Jaekel 	switch (op) {
496add9520fSAndreas Jaekel 	case ZEV_OP_ERROR:
497add9520fSAndreas Jaekel 		stat->zev_cnt_errors++;
498add9520fSAndreas Jaekel 		break;
499add9520fSAndreas Jaekel 	case ZEV_OP_MARK:
500add9520fSAndreas Jaekel 		stat->zev_cnt_marks++;
501add9520fSAndreas Jaekel 		break;
502add9520fSAndreas Jaekel 	case ZEV_OP_ZFS_MOUNT:
503add9520fSAndreas Jaekel 		stat->zev_cnt_zfs_mount++;
504add9520fSAndreas Jaekel 		break;
505add9520fSAndreas Jaekel 	case ZEV_OP_ZFS_UMOUNT:
506add9520fSAndreas Jaekel 		stat->zev_cnt_zfs_umount++;
507add9520fSAndreas Jaekel 		break;
508add9520fSAndreas Jaekel 	case ZEV_OP_ZVOL_WRITE:
509add9520fSAndreas Jaekel 		stat->zev_cnt_zvol_write++;
510add9520fSAndreas Jaekel 		break;
511add9520fSAndreas Jaekel 	case ZEV_OP_ZVOL_TRUNCATE:
512add9520fSAndreas Jaekel 		stat->zev_cnt_zvol_truncate++;
513add9520fSAndreas Jaekel 		break;
514add9520fSAndreas Jaekel 	case ZEV_OP_ZNODE_CLOSE_AFTER_UPDATE:
515add9520fSAndreas Jaekel 		stat->zev_cnt_znode_close_after_update++;
516add9520fSAndreas Jaekel 		break;
517add9520fSAndreas Jaekel 	case ZEV_OP_ZNODE_CREATE:
518add9520fSAndreas Jaekel 		stat->zev_cnt_znode_create++;
519add9520fSAndreas Jaekel 		break;
520add9520fSAndreas Jaekel 	case ZEV_OP_ZNODE_REMOVE:
521add9520fSAndreas Jaekel 		stat->zev_cnt_znode_remove++;
522add9520fSAndreas Jaekel 		break;
523add9520fSAndreas Jaekel 	case ZEV_OP_ZNODE_LINK:
524add9520fSAndreas Jaekel 		stat->zev_cnt_znode_link++;
525add9520fSAndreas Jaekel 		break;
526add9520fSAndreas Jaekel 	case ZEV_OP_ZNODE_SYMLINK:
527add9520fSAndreas Jaekel 		stat->zev_cnt_znode_symlink++;
528add9520fSAndreas Jaekel 		break;
529add9520fSAndreas Jaekel 	case ZEV_OP_ZNODE_RENAME:
530add9520fSAndreas Jaekel 		stat->zev_cnt_znode_rename++;
531add9520fSAndreas Jaekel 		break;
532add9520fSAndreas Jaekel 	case ZEV_OP_ZNODE_WRITE:
533add9520fSAndreas Jaekel 		stat->zev_cnt_znode_write++;
534add9520fSAndreas Jaekel 		break;
535add9520fSAndreas Jaekel 	case ZEV_OP_ZNODE_TRUNCATE:
536add9520fSAndreas Jaekel 		stat->zev_cnt_znode_truncate++;
537add9520fSAndreas Jaekel 		break;
538add9520fSAndreas Jaekel 	case ZEV_OP_ZNODE_SETATTR:
539add9520fSAndreas Jaekel 		stat->zev_cnt_znode_setattr++;
540add9520fSAndreas Jaekel 		break;
541add9520fSAndreas Jaekel 	case ZEV_OP_ZNODE_ACL:
542add9520fSAndreas Jaekel 		stat->zev_cnt_znode_acl++;
543add9520fSAndreas Jaekel 		break;
544add9520fSAndreas Jaekel 	}
545add9520fSAndreas Jaekel }
546add9520fSAndreas Jaekel 
547a18c35b9SAndreas Jaekel void
zev_queue_message(int op,zev_msg_t * msg)548f2dd45e5SAndreas Jaekel zev_queue_message(int op, zev_msg_t *msg)
549a18c35b9SAndreas Jaekel {
550add9520fSAndreas Jaekel 	zev_queue_t *q;
551fec460f8SAndreas Jaekel 	int wakeup = 0;
552add9520fSAndreas Jaekel 	zev_msg_t *m;
5536a6a51eeSAndreas Jaekel 	int i;
554a18c35b9SAndreas Jaekel 
555aafc540fSAndreas Jaekel 	msg->next = NULL;
556add9520fSAndreas Jaekel 	msg->prev = NULL;
557add9520fSAndreas Jaekel 	msg->read = 0;
558a18c35b9SAndreas Jaekel 
559a18c35b9SAndreas Jaekel 	if (op < ZEV_OP_MIN || op > ZEV_OP_MAX) {
560aafc540fSAndreas Jaekel 		zev_queue_error(op, "unknown op id encountered: %d", op);
561205ed6bfSAndreas Jaekel 		zev_free(msg, sizeof(*msg) + msg->size);
562aafc540fSAndreas Jaekel 		return;
563a18c35b9SAndreas Jaekel 	}
564a18c35b9SAndreas Jaekel 
5656a6a51eeSAndreas Jaekel 	/*
5666a6a51eeSAndreas Jaekel 	 * This mutex protects us agains race conditions when several
5676a6a51eeSAndreas Jaekel 	 * threads want to queue a message and one or more queues are
5686a6a51eeSAndreas Jaekel 	 * full:  we release zev_mutex to wait for the queues to become
5696a6a51eeSAndreas Jaekel 	 * less-than-full, but we don't know in which order the waiting
5706a6a51eeSAndreas Jaekel 	 * threads will be awoken.  If it's not the same order in which
5716a6a51eeSAndreas Jaekel 	 * they went to sleep we might mark different messages as "newest"
5726a6a51eeSAndreas Jaekel 	 * in different queues, and so we might have dupes or even
5736a6a51eeSAndreas Jaekel 	 * skip messages.
5746a6a51eeSAndreas Jaekel 	 */
5756a6a51eeSAndreas Jaekel 	mutex_enter(&zev_queue_msg_mutex);
5766a6a51eeSAndreas Jaekel 
577a18c35b9SAndreas Jaekel 	mutex_enter(&zev_mutex);
578add9520fSAndreas Jaekel 
579add9520fSAndreas Jaekel 	/*
580add9520fSAndreas Jaekel 	 * When the module is loaded, the default behavior ist to
581add9520fSAndreas Jaekel 	 * put all events into a queue and block if the queue is full.
582add9520fSAndreas Jaekel 	 * This is done even before the pseudo device is attached.
583add9520fSAndreas Jaekel 	 * This way, no events are lost.
584add9520fSAndreas Jaekel 	 *
585add9520fSAndreas Jaekel 	 * To discard events entirely the "beaver" queue,
586add9520fSAndreas Jaekel 	 * which never discards anything, has to be removed.
587add9520fSAndreas Jaekel 	 */
588add9520fSAndreas Jaekel 
5896a6a51eeSAndreas Jaekel 	if (zev_queue_cnt == 0) {
590add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
5916a6a51eeSAndreas Jaekel 		mutex_exit(&zev_queue_msg_mutex);
592add9520fSAndreas Jaekel 		return;
593add9520fSAndreas Jaekel 	}
594add9520fSAndreas Jaekel 
595add9520fSAndreas Jaekel 	/* put message into global queue */
596add9520fSAndreas Jaekel 	msg->seq = zev_msg_sequence_number++;
5979720aba3SAndreas Jaekel 
5989720aba3SAndreas Jaekel 	/* do we need to make room? */
5994e175854SAndreas Jaekel again:
600a18c35b9SAndreas Jaekel 	while (zev_statistics.zev_max_queue_len &&
6019720aba3SAndreas Jaekel 	    zev_statistics.zev_queue_len > zev_statistics.zev_max_queue_len) {
6029720aba3SAndreas Jaekel 
6039720aba3SAndreas Jaekel 		if (zev_have_blocking_queues) {
6044e175854SAndreas Jaekel 			/* so we have blocking queues.  are they full? */
6054e175854SAndreas Jaekel 			for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
6064e175854SAndreas Jaekel 				q = zev_queues[i - ZEV_MINOR_MIN];
6074e175854SAndreas Jaekel 				if (!q)
6089720aba3SAndreas Jaekel 					continue;
6094e175854SAndreas Jaekel 				if ((q->zq_flags &
6104e175854SAndreas Jaekel 				     ZEV_FL_BLOCK_WHILE_QUEUE_FULL) == 0)
6114e175854SAndreas Jaekel 					continue;
6124e175854SAndreas Jaekel 				if (q->zq_queue_len &&
6134e175854SAndreas Jaekel 				    q->zq_queue_len > q->zq_max_queue_len) {
6144e175854SAndreas Jaekel 					/* block until queue's been shrunk. */
6154e175854SAndreas Jaekel 					cv_wait(&zev_condvar, &zev_mutex);
6164e175854SAndreas Jaekel 					goto again;
6174e175854SAndreas Jaekel 				}
6184e175854SAndreas Jaekel 			}
6199720aba3SAndreas Jaekel 		}
6209720aba3SAndreas Jaekel 
6219720aba3SAndreas Jaekel 		/* discard events until this message fits into all queues */
6229720aba3SAndreas Jaekel 
6239720aba3SAndreas Jaekel 		for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
6249720aba3SAndreas Jaekel 			q = zev_queues[i - ZEV_MINOR_MIN];
6259720aba3SAndreas Jaekel 			if (!q)
6269720aba3SAndreas Jaekel 				continue;
6279720aba3SAndreas Jaekel 			/* discard msgs until queue is small enough */
6289720aba3SAndreas Jaekel 			while (q->zq_queue_len &&
6299720aba3SAndreas Jaekel 			       q->zq_queue_len > q->zq_max_queue_len) {
6309720aba3SAndreas Jaekel 				m = q->zq_oldest;
6319720aba3SAndreas Jaekel 				if (m == NULL)
6329720aba3SAndreas Jaekel 					break;
6339720aba3SAndreas Jaekel 				q->zq_events_discarded++;
6349720aba3SAndreas Jaekel 				q->zq_bytes_discarded += m->size;
6359720aba3SAndreas Jaekel 				q->zq_oldest = m->next;
6369720aba3SAndreas Jaekel 				q->zq_queue_len -= m->size;
6379720aba3SAndreas Jaekel 				q->zq_queue_messages--;
6389720aba3SAndreas Jaekel 			}
6399720aba3SAndreas Jaekel 		}
6409720aba3SAndreas Jaekel 
6419720aba3SAndreas Jaekel 		zev_queue_trim();
6429720aba3SAndreas Jaekel 		ASSERT(zev_statistics.zev_queue_len == 0 ||
6439720aba3SAndreas Jaekel 		       zev_statistics.zev_queue_len <=
6449720aba3SAndreas Jaekel 				zev_statistics.zev_max_queue_len);
645a18c35b9SAndreas Jaekel 	}
646a18c35b9SAndreas Jaekel 
647aafc540fSAndreas Jaekel 	if (zev_queue_tail == NULL) {
648aafc540fSAndreas Jaekel 		zev_queue_head = zev_queue_tail = msg;
649aafc540fSAndreas Jaekel 	} else {
650aafc540fSAndreas Jaekel 		zev_queue_tail->next = msg;
651add9520fSAndreas Jaekel 		msg->prev = zev_queue_tail;
652aafc540fSAndreas Jaekel 		zev_queue_tail = msg;
653a18c35b9SAndreas Jaekel 	}
654aafc540fSAndreas Jaekel 	zev_queue_len++;
655a18c35b9SAndreas Jaekel 	zev_statistics.zev_cnt_total_events++;
656aafc540fSAndreas Jaekel 	zev_statistics.zev_queue_len += msg->size;
657add9520fSAndreas Jaekel 
658add9520fSAndreas Jaekel 	/* update per-device queues */
6596a6a51eeSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
6606a6a51eeSAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
6616a6a51eeSAndreas Jaekel 		if (!q)
6626a6a51eeSAndreas Jaekel 			continue;
6636a6a51eeSAndreas Jaekel 
664add9520fSAndreas Jaekel 		zev_queue_hold(q);
665add9520fSAndreas Jaekel 
666add9520fSAndreas Jaekel 		/* make sure queue has enough room */
6676a6a51eeSAndreas Jaekel 		while (q->zq_max_queue_len &&
6686a6a51eeSAndreas Jaekel 		       q->zq_queue_len > q->zq_max_queue_len) {
6696a6a51eeSAndreas Jaekel 
670add9520fSAndreas Jaekel 			if (q->zq_flags & ZEV_FL_BLOCK_WHILE_QUEUE_FULL) {
671add9520fSAndreas Jaekel 				/* block until queue has been shrunk. */
672add9520fSAndreas Jaekel 				cv_wait(&zev_condvar, &zev_mutex);
673add9520fSAndreas Jaekel 			} else {
674add9520fSAndreas Jaekel 				/* discard msgs until queue is small enough */
675add9520fSAndreas Jaekel 				while (q->zq_queue_len > q->zq_max_queue_len) {
676add9520fSAndreas Jaekel 					m = q->zq_oldest;
677add9520fSAndreas Jaekel 					if (m == NULL)
678aafc540fSAndreas Jaekel 						break;
679add9520fSAndreas Jaekel 					q->zq_events_discarded++;
680add9520fSAndreas Jaekel 					q->zq_bytes_discarded += m->size;
681add9520fSAndreas Jaekel 					q->zq_oldest = m->next;
682add9520fSAndreas Jaekel 					q->zq_queue_len -= m->size;
683add9520fSAndreas Jaekel 					q->zq_queue_messages--;
684a18c35b9SAndreas Jaekel 				}
685add9520fSAndreas Jaekel 			}
686add9520fSAndreas Jaekel 		}
687add9520fSAndreas Jaekel 
688add9520fSAndreas Jaekel 		/* register new message at the end of the queue */
689add9520fSAndreas Jaekel 		q->zq_queue_len += msg->size;
690add9520fSAndreas Jaekel 		q->zq_queue_messages++;
6916a6a51eeSAndreas Jaekel 		q->zq_bytes_total += msg->size;
692add9520fSAndreas Jaekel 		q->zq_events_total++;
693add9520fSAndreas Jaekel 		if (q->zq_oldest == NULL)
694add9520fSAndreas Jaekel 			q->zq_oldest = msg;
695add9520fSAndreas Jaekel 
696add9520fSAndreas Jaekel 		zev_update_statistics(op, &q->zq_statistics);
697add9520fSAndreas Jaekel 
6986a6a51eeSAndreas Jaekel 		if (q->zq_queue_len > q->zq_wakeup_threshold)
699add9520fSAndreas Jaekel 			wakeup = 1;
7006a6a51eeSAndreas Jaekel 		if (q->zq_queue_len == msg->size)  /* queue was empty */
7016a6a51eeSAndreas Jaekel 			cv_broadcast(&q->zq_condvar);
702add9520fSAndreas Jaekel 
703add9520fSAndreas Jaekel 		zev_queue_release(q);
704add9520fSAndreas Jaekel 	}
705add9520fSAndreas Jaekel 
706add9520fSAndreas Jaekel 	zev_queue_trim();
707add9520fSAndreas Jaekel 
708add9520fSAndreas Jaekel 	zev_update_statistics(op, &zev_statistics);
709a18c35b9SAndreas Jaekel 	mutex_exit(&zev_mutex);
7106a6a51eeSAndreas Jaekel 	mutex_exit(&zev_queue_msg_mutex);
711a18c35b9SAndreas Jaekel 
712add9520fSAndreas Jaekel 	/* one or more queues need a pollwakeup() */
7136a6a51eeSAndreas Jaekel 	if (op == ZEV_OP_MARK) {
7146a6a51eeSAndreas Jaekel 		zev_poll_wakeup(B_TRUE);
7156a6a51eeSAndreas Jaekel 	} else if (wakeup) {
7166a6a51eeSAndreas Jaekel 		zev_poll_wakeup(B_FALSE);
717add9520fSAndreas Jaekel 	}
718aafc540fSAndreas Jaekel 
719aafc540fSAndreas Jaekel 	return;
720aafc540fSAndreas Jaekel }
721aafc540fSAndreas Jaekel 
722aafc540fSAndreas Jaekel void
zev_queue_error(int op,char * fmt,...)723aafc540fSAndreas Jaekel zev_queue_error(int op, char *fmt, ...)
724aafc540fSAndreas Jaekel {
725aafc540fSAndreas Jaekel 	char buf[ZEV_MAX_MESSAGE_LEN];
726aafc540fSAndreas Jaekel 	va_list ap;
727aafc540fSAndreas Jaekel 	int len;
728f2dd45e5SAndreas Jaekel 	zev_msg_t *msg = NULL;
729f2dd45e5SAndreas Jaekel 	zev_error_t *rec;
730f2dd45e5SAndreas Jaekel 	int msg_size;
731aafc540fSAndreas Jaekel 
732aafc540fSAndreas Jaekel 	va_start(ap, fmt);
733aafc540fSAndreas Jaekel 	len = vsnprintf(buf, sizeof(buf), fmt, ap);
734aafc540fSAndreas Jaekel 	va_end(ap);
735f2dd45e5SAndreas Jaekel 	if (len >= sizeof(buf)) {
736f2dd45e5SAndreas Jaekel 		cmn_err(CE_WARN, "zev: can't report error - "
737f2dd45e5SAndreas Jaekel 		        "dropping event entirely.");
738aafc540fSAndreas Jaekel 		return;
739f2dd45e5SAndreas Jaekel 	}
740f2dd45e5SAndreas Jaekel 
741f2dd45e5SAndreas Jaekel 	msg_size = sizeof(*rec) + len + 1;
742205ed6bfSAndreas Jaekel 	msg = zev_alloc(sizeof(*msg) + msg_size);
743f2dd45e5SAndreas Jaekel 	msg->size = msg_size;
744f2dd45e5SAndreas Jaekel 	rec = (zev_error_t *)(msg + 1);
745e2e7ab14SAndreas Jaekel 	rec->record_len = msg_size;
746f2dd45e5SAndreas Jaekel 	rec->op = ZEV_OP_ERROR;
747f2dd45e5SAndreas Jaekel 	rec->op_time = ddi_get_time();
748149d0affSAndreas Jaekel 	rec->guid = 0;
749f2dd45e5SAndreas Jaekel 	rec->failed_op = op;
750f2dd45e5SAndreas Jaekel 	rec->errstr_len = len;
751548c8b6eSAndreas Jaekel 	(void) memcpy(ZEV_ERRSTR(rec), buf, len + 1);
752f2dd45e5SAndreas Jaekel 
753f2dd45e5SAndreas Jaekel 	zev_queue_message(ZEV_OP_ERROR, msg);
754f2dd45e5SAndreas Jaekel 	return;
755a18c35b9SAndreas Jaekel }
756a18c35b9SAndreas Jaekel 
7576a6a51eeSAndreas Jaekel static int
zev_find_queue(zev_queue_t ** out,zev_queue_t * req_q,zev_queue_name_t * name)7586a6a51eeSAndreas Jaekel zev_find_queue(zev_queue_t **out, zev_queue_t *req_q, zev_queue_name_t *name)
7596a6a51eeSAndreas Jaekel {
7606a6a51eeSAndreas Jaekel 	char namebuf[ZEV_MAX_QUEUE_NAME_LEN+1];
7616a6a51eeSAndreas Jaekel 	zev_queue_t *q;
7626a6a51eeSAndreas Jaekel 	int i;
7636a6a51eeSAndreas Jaekel 
7646a6a51eeSAndreas Jaekel 	*out = NULL;
7656a6a51eeSAndreas Jaekel 
7666a6a51eeSAndreas Jaekel 	if (name->zev_namelen == 0) {
7676a6a51eeSAndreas Jaekel 		if (req_q->zq_minor_number == ZEV_CONTROL_DEVICE_MINOR)
7686a6a51eeSAndreas Jaekel 			return EINVAL;
76963d4104fSAndreas Jaekel 		mutex_enter(&zev_mutex);
7706a6a51eeSAndreas Jaekel 		zev_queue_hold(req_q);
77163d4104fSAndreas Jaekel 		mutex_exit(&zev_mutex);
7726a6a51eeSAndreas Jaekel 		*out = req_q;
7736a6a51eeSAndreas Jaekel 		return 0;
7746a6a51eeSAndreas Jaekel 	}
7756a6a51eeSAndreas Jaekel 
7766a6a51eeSAndreas Jaekel 	if (name->zev_namelen > ZEV_MAX_QUEUE_NAME_LEN)
7776a6a51eeSAndreas Jaekel 		return EINVAL;
7786a6a51eeSAndreas Jaekel 	strncpy(namebuf, name->zev_name, name->zev_namelen);
7796a6a51eeSAndreas Jaekel 	namebuf[name->zev_namelen] = '\0';
7806a6a51eeSAndreas Jaekel 
7816a6a51eeSAndreas Jaekel 	mutex_enter(&zev_mutex);
7826a6a51eeSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
7836a6a51eeSAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
7846a6a51eeSAndreas Jaekel 		if (!q)
7856a6a51eeSAndreas Jaekel 			continue;
7866a6a51eeSAndreas Jaekel 		if (!strcmp(q->zq_name, namebuf)) {
7876a6a51eeSAndreas Jaekel 			zev_queue_hold(q);
7886a6a51eeSAndreas Jaekel 			mutex_exit(&zev_mutex);
7896a6a51eeSAndreas Jaekel 			*out = q;
7906a6a51eeSAndreas Jaekel 			return 0;
7916a6a51eeSAndreas Jaekel 		}
7926a6a51eeSAndreas Jaekel 	}
7936a6a51eeSAndreas Jaekel 	mutex_exit(&zev_mutex);
7946a6a51eeSAndreas Jaekel 	return ENOENT;
7956a6a51eeSAndreas Jaekel }
79635d4e8ddSAndreas Jaekel 
797add9520fSAndreas Jaekel static int
zev_ioc_get_queue_statistics(zev_queue_t * req_q,intptr_t arg,int mode)798add9520fSAndreas Jaekel zev_ioc_get_queue_statistics(zev_queue_t *req_q, intptr_t arg, int mode)
799add9520fSAndreas Jaekel {
800add9520fSAndreas Jaekel 	zev_ioctl_get_queue_statistics_t gs;
801add9520fSAndreas Jaekel 	zev_queue_t *q;
8026a6a51eeSAndreas Jaekel 	int ret;
803add9520fSAndreas Jaekel 
804add9520fSAndreas Jaekel 	if (ddi_copyin((void *)arg, &gs, sizeof(gs), mode) != 0)
80535d4e8ddSAndreas Jaekel 		return EFAULT;
806add9520fSAndreas Jaekel 
8076a6a51eeSAndreas Jaekel 	ret = zev_find_queue(&q, req_q, &gs.zev_queue_name);
8086a6a51eeSAndreas Jaekel 	if (ret)
8096a6a51eeSAndreas Jaekel 		return ret;
810add9520fSAndreas Jaekel 
811add9520fSAndreas Jaekel 	/* ddi_copyout() can take a long time.  Better make
812add9520fSAndreas Jaekel 	   a copy to be able to release the mutex faster. */
813add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
814add9520fSAndreas Jaekel 	memcpy(&gs.zev_statistics, &q->zq_statistics,sizeof(gs.zev_statistics));
815add9520fSAndreas Jaekel 	gs.zev_statistics.zev_queue_len = q->zq_queue_len;
816add9520fSAndreas Jaekel 	gs.zev_statistics.zev_bytes_read = q->zq_bytes_read;
817add9520fSAndreas Jaekel 	gs.zev_statistics.zev_bytes_discarded = q->zq_bytes_discarded;
818add9520fSAndreas Jaekel 	gs.zev_statistics.zev_max_queue_len = q->zq_max_queue_len;
819add9520fSAndreas Jaekel 	gs.zev_statistics.zev_cnt_discarded_events = q->zq_events_discarded;
820add9520fSAndreas Jaekel 	gs.zev_statistics.zev_cnt_total_events = q->zq_events_total;
821add9520fSAndreas Jaekel 	zev_queue_release(q);
822add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
823add9520fSAndreas Jaekel 
824add9520fSAndreas Jaekel 	if (ddi_copyout(&gs, (void *)arg, sizeof(gs), mode) != 0)
825add9520fSAndreas Jaekel 		return EFAULT;
826add9520fSAndreas Jaekel 	return 0;
82735d4e8ddSAndreas Jaekel }
828add9520fSAndreas Jaekel 
829add9520fSAndreas Jaekel static int
zev_ioc_set_queue_properties(zev_queue_t * req_q,intptr_t arg,int mode)830add9520fSAndreas Jaekel zev_ioc_set_queue_properties(zev_queue_t *req_q, intptr_t arg, int mode)
831add9520fSAndreas Jaekel {
832add9520fSAndreas Jaekel 	zev_ioctl_set_queue_properties_t qp;
833add9520fSAndreas Jaekel 	zev_queue_t *q;
834add9520fSAndreas Jaekel 	uint64_t old_max;
835add9520fSAndreas Jaekel 	uint64_t old_flags;
8366a6a51eeSAndreas Jaekel 	int ret;
837add9520fSAndreas Jaekel 
838add9520fSAndreas Jaekel 	if (ddi_copyin((void *)arg, &qp, sizeof(qp), mode) != 0)
839add9520fSAndreas Jaekel 		return EFAULT;
840add9520fSAndreas Jaekel 	if (qp.zev_max_queue_len > ZEV_MAX_QUEUE_LEN)
841add9520fSAndreas Jaekel 		return EINVAL;
8426a6a51eeSAndreas Jaekel 	if (qp.zev_poll_wakeup_threshold > ZEV_MAX_POLL_WAKEUP_QUEUE_LEN)
843add9520fSAndreas Jaekel 		return EINVAL;
844add9520fSAndreas Jaekel 
8456a6a51eeSAndreas Jaekel 	ret = zev_find_queue(&q, req_q, &qp.zev_queue_name);
8466a6a51eeSAndreas Jaekel 	if (ret)
8476a6a51eeSAndreas Jaekel 		return ret;
848add9520fSAndreas Jaekel 
849add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
850add9520fSAndreas Jaekel 
851add9520fSAndreas Jaekel 	/*
852add9520fSAndreas Jaekel 	 * Note: if the PERSISTENT flag is cleared, and the queue is not busy,
853add9520fSAndreas Jaekel 	 * the queue should be removed by zev_queue_release() in zev_ioctl().
854add9520fSAndreas Jaekel 	 */
855add9520fSAndreas Jaekel 	old_flags = qp.zev_flags;
856add9520fSAndreas Jaekel 	q->zq_flags = qp.zev_flags;
857add9520fSAndreas Jaekel 	if ((old_flags & ZEV_FL_BLOCK_WHILE_QUEUE_FULL) &&
858add9520fSAndreas Jaekel 	   (!(qp.zev_flags & ZEV_FL_BLOCK_WHILE_QUEUE_FULL))) {
859add9520fSAndreas Jaekel 		/* queue is no longer blocking - wake blocked threads */
860add9520fSAndreas Jaekel 		cv_broadcast(&zev_condvar);
86135d4e8ddSAndreas Jaekel 	}
862add9520fSAndreas Jaekel 
8639720aba3SAndreas Jaekel 	zev_update_blockflag();
8649720aba3SAndreas Jaekel 
865add9520fSAndreas Jaekel 	old_max = q->zq_max_queue_len;
866add9520fSAndreas Jaekel 	q->zq_max_queue_len = qp.zev_max_queue_len;
867add9520fSAndreas Jaekel 	if (q->zq_max_queue_len < old_max)
868add9520fSAndreas Jaekel 		zev_queue_trim();
869add9520fSAndreas Jaekel 	if (q->zq_max_queue_len > old_max)
870add9520fSAndreas Jaekel 		cv_broadcast(&zev_condvar);	/* threads may be waiting */
871add9520fSAndreas Jaekel 
872add9520fSAndreas Jaekel 	if ((qp.zev_poll_wakeup_threshold < q->zq_wakeup_threshold) &&
873add9520fSAndreas Jaekel 	    (qp.zev_poll_wakeup_threshold <= q->zq_queue_len))
874add9520fSAndreas Jaekel 		pollwakeup(&q->zq_pollhead, POLLIN);
875add9520fSAndreas Jaekel 	q->zq_wakeup_threshold = qp.zev_poll_wakeup_threshold;
876add9520fSAndreas Jaekel 
877add9520fSAndreas Jaekel 	zev_queue_release(q);
878add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
879add9520fSAndreas Jaekel 	return 0;
88035d4e8ddSAndreas Jaekel }
881add9520fSAndreas Jaekel 
882add9520fSAndreas Jaekel static int
zev_ioc_get_queue_properties(zev_queue_t * req_q,intptr_t arg,int mode)883add9520fSAndreas Jaekel zev_ioc_get_queue_properties(zev_queue_t *req_q, intptr_t arg, int mode)
884add9520fSAndreas Jaekel {
885add9520fSAndreas Jaekel 	zev_ioctl_get_queue_properties_t qp;
886add9520fSAndreas Jaekel 	zev_queue_t *q;
8876a6a51eeSAndreas Jaekel 	int ret;
888add9520fSAndreas Jaekel 
889add9520fSAndreas Jaekel 	if (ddi_copyin((void *)arg, &qp, sizeof(qp), mode) != 0)
890add9520fSAndreas Jaekel 		return EFAULT;
891add9520fSAndreas Jaekel 
8926a6a51eeSAndreas Jaekel 	ret = zev_find_queue(&q, req_q, &qp.zev_queue_name);
8936a6a51eeSAndreas Jaekel 	if (ret)
8946a6a51eeSAndreas Jaekel 		return ret;
895add9520fSAndreas Jaekel 
896add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
897add9520fSAndreas Jaekel 	qp.zev_max_queue_len = q->zq_max_queue_len;
898add9520fSAndreas Jaekel 	qp.zev_flags = q->zq_flags;
899add9520fSAndreas Jaekel 	qp.zev_poll_wakeup_threshold = q->zq_wakeup_threshold;
900add9520fSAndreas Jaekel 	zev_queue_release(q);
901add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
902add9520fSAndreas Jaekel 
903add9520fSAndreas Jaekel 	if (ddi_copyout(&qp, (void *)arg, sizeof(qp), mode) != 0)
904add9520fSAndreas Jaekel 		return EFAULT;
905add9520fSAndreas Jaekel 	return 0;
906add9520fSAndreas Jaekel }
907add9520fSAndreas Jaekel 
908add9520fSAndreas Jaekel static int
zev_ioc_add_queue(zev_queue_t * req_q,intptr_t arg,int mode)909add9520fSAndreas Jaekel zev_ioc_add_queue(zev_queue_t *req_q, intptr_t arg, int mode)
910add9520fSAndreas Jaekel {
911add9520fSAndreas Jaekel 	zev_ioctl_add_queue_t aq;
912add9520fSAndreas Jaekel 	zev_queue_t *new_q;
913add9520fSAndreas Jaekel 	char name[ZEV_MAX_QUEUE_NAME_LEN+1];
914add9520fSAndreas Jaekel 
915add9520fSAndreas Jaekel 	if (ddi_copyin((void *)arg, &aq, sizeof(aq), mode) != 0)
916add9520fSAndreas Jaekel 		return EFAULT;
917add9520fSAndreas Jaekel 
918add9520fSAndreas Jaekel 	if (aq.zev_namelen > ZEV_MAX_QUEUE_NAME_LEN)
919add9520fSAndreas Jaekel 		return EINVAL;
920add9520fSAndreas Jaekel 	strncpy(name, aq.zev_name, aq.zev_namelen);
921add9520fSAndreas Jaekel 	name[aq.zev_namelen] = '\0';
9229fef2cddSAndreas Jaekel 	if (!strncmp(name, ZEV_TMPQUEUE_DEVICE_NAME,
9239fef2cddSAndreas Jaekel 	             strlen(ZEV_TMPQUEUE_DEVICE_NAME)))
9249fef2cddSAndreas Jaekel 		return EINVAL;
925add9520fSAndreas Jaekel 
926add9520fSAndreas Jaekel 	return zev_queue_new(&new_q, req_q->zq_dip, name,
927add9520fSAndreas Jaekel 	                     aq.zev_max_queue_len, aq.zev_flags);
928add9520fSAndreas Jaekel }
929add9520fSAndreas Jaekel 
930add9520fSAndreas Jaekel static int
zev_ioc_remove_queue(zev_queue_t * req_q,intptr_t arg,int mode)931add9520fSAndreas Jaekel zev_ioc_remove_queue(zev_queue_t *req_q, intptr_t arg, int mode)
932add9520fSAndreas Jaekel {
933add9520fSAndreas Jaekel 	zev_ioctl_remove_queue_t rq;
934add9520fSAndreas Jaekel 	zev_queue_t *q;
935add9520fSAndreas Jaekel 	char name[ZEV_MAX_QUEUE_NAME_LEN+1];
9366a6a51eeSAndreas Jaekel 	int found = 0;
9376a6a51eeSAndreas Jaekel 	int i;
938add9520fSAndreas Jaekel 
939add9520fSAndreas Jaekel 	if (ddi_copyin((void *)arg, &rq, sizeof(rq), mode) != 0)
940add9520fSAndreas Jaekel 		return EFAULT;
941add9520fSAndreas Jaekel 
9426a6a51eeSAndreas Jaekel 	if (rq.zev_queue_name.zev_namelen > ZEV_MAX_QUEUE_NAME_LEN)
943add9520fSAndreas Jaekel 		return EINVAL;
9446a6a51eeSAndreas Jaekel 	strncpy(name, rq.zev_queue_name.zev_name,
9456a6a51eeSAndreas Jaekel 	        rq.zev_queue_name.zev_namelen);
9466a6a51eeSAndreas Jaekel 	name[rq.zev_queue_name.zev_namelen] = '\0';
947add9520fSAndreas Jaekel 
948add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
9496a6a51eeSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
9506a6a51eeSAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
9516a6a51eeSAndreas Jaekel 		if (!q)
9526a6a51eeSAndreas Jaekel 			continue;
9536a6a51eeSAndreas Jaekel 		if (!strcmp(q->zq_name, name)) {
9546a6a51eeSAndreas Jaekel 			found = 1;
9556a6a51eeSAndreas Jaekel 			break;
956add9520fSAndreas Jaekel 		}
9576a6a51eeSAndreas Jaekel 	}
9586a6a51eeSAndreas Jaekel 	if (!found) {
959add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
960add9520fSAndreas Jaekel 		return ENOENT;
9616a6a51eeSAndreas Jaekel 	}
9626a6a51eeSAndreas Jaekel 
963add9520fSAndreas Jaekel 	if (q->zq_busy) {
964add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
965add9520fSAndreas Jaekel 		return EBUSY;
966add9520fSAndreas Jaekel 	}
967add9520fSAndreas Jaekel 	/*
968add9520fSAndreas Jaekel 	 * clear flags, so that persistent queues are removed aswell
969add9520fSAndreas Jaekel 	 * and the queue becomes non-blocking.
970add9520fSAndreas Jaekel 	 */
971add9520fSAndreas Jaekel 	q->zq_flags = 0;
972add9520fSAndreas Jaekel 	if (q->zq_to_be_removed == B_FALSE) {
973add9520fSAndreas Jaekel 		q->zq_to_be_removed = B_TRUE;
974add9520fSAndreas Jaekel 		zev_queue_release(q);
975add9520fSAndreas Jaekel 	}
976add9520fSAndreas Jaekel 	/* some threads might be waiting for this queue to become writable */
977add9520fSAndreas Jaekel 	cv_broadcast(&zev_condvar);
978add9520fSAndreas Jaekel 
979add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
980add9520fSAndreas Jaekel 	return 0;
981add9520fSAndreas Jaekel }
982add9520fSAndreas Jaekel 
983add9520fSAndreas Jaekel static int
zev_ioc_get_debug_info(zev_queue_t * req_q,intptr_t arg,int mode)984add9520fSAndreas Jaekel zev_ioc_get_debug_info(zev_queue_t *req_q, intptr_t arg, int mode)
985add9520fSAndreas Jaekel {
986add9520fSAndreas Jaekel 	zev_ioctl_debug_info_t di;
987add9520fSAndreas Jaekel 	uint64_t mem_allocated = atomic_add_64_nv(&zev_memory_allocated, 0);
988add9520fSAndreas Jaekel 	uint64_t mem_freed     = atomic_add_64_nv(&zev_memory_freed, 0);
989add9520fSAndreas Jaekel 
990205ed6bfSAndreas Jaekel 	zev_chksum_stats(&di.zev_chksum_cache_size,
991205ed6bfSAndreas Jaekel 	                 &di.zev_chksum_cache_hits,
992205ed6bfSAndreas Jaekel 	                 &di.zev_chksum_cache_misses);
993add9520fSAndreas Jaekel 	di.zev_memory_allocated = mem_allocated - mem_freed;
994add9520fSAndreas Jaekel 	if (ddi_copyout(&di, (void *)arg, sizeof(di), mode) != 0)
995add9520fSAndreas Jaekel 		return EFAULT;
996add9520fSAndreas Jaekel 	return 0;
997add9520fSAndreas Jaekel }
998add9520fSAndreas Jaekel 
999add9520fSAndreas Jaekel static int
zev_ioc_get_queue_list(zev_queue_t * req_q,intptr_t arg,int mode)1000add9520fSAndreas Jaekel zev_ioc_get_queue_list(zev_queue_t *req_q, intptr_t arg, int mode)
1001add9520fSAndreas Jaekel {
1002add9520fSAndreas Jaekel 	zev_ioctl_get_queue_list_t gql;
1003add9520fSAndreas Jaekel 	zev_queue_t *q;
1004add9520fSAndreas Jaekel 	int i = 0;
10056a6a51eeSAndreas Jaekel 	int count = 0;
1006add9520fSAndreas Jaekel 
1007add9520fSAndreas Jaekel 	memset(&gql, 0, sizeof(gql));
1008add9520fSAndreas Jaekel 
1009add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
10106a6a51eeSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
10116a6a51eeSAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
10126a6a51eeSAndreas Jaekel 		if (!q)
10136a6a51eeSAndreas Jaekel 			continue;
10146a6a51eeSAndreas Jaekel 		strncpy(gql.zev_queue_name[count].zev_name,
1015add9520fSAndreas Jaekel 		    q->zq_name, ZEV_MAX_QUEUE_NAME_LEN);
10166a6a51eeSAndreas Jaekel 		gql.zev_queue_name[count].zev_namelen = strlen(q->zq_name);
10176a6a51eeSAndreas Jaekel 		count++;
1018add9520fSAndreas Jaekel 	}
10196a6a51eeSAndreas Jaekel 	gql.zev_n_queues = count;
1020add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
1021add9520fSAndreas Jaekel 
1022add9520fSAndreas Jaekel 	if (ddi_copyout(&gql, (void *)arg, sizeof(gql), mode) != 0)
1023add9520fSAndreas Jaekel 		return EFAULT;
1024add9520fSAndreas Jaekel 	return 0;
102535d4e8ddSAndreas Jaekel }
102635d4e8ddSAndreas Jaekel 
1027932509f9SAndreas Jaekel static int
zev_ioc_set_max_queue_len(zev_queue_t * req_q,intptr_t arg,int mode)1028932509f9SAndreas Jaekel zev_ioc_set_max_queue_len(zev_queue_t *req_q, intptr_t arg, int mode)
1029932509f9SAndreas Jaekel {
1030932509f9SAndreas Jaekel 	uint64_t len;
1031932509f9SAndreas Jaekel 	int i;
1032932509f9SAndreas Jaekel 	zev_queue_t *q;
1033932509f9SAndreas Jaekel 
1034932509f9SAndreas Jaekel 	if (ddi_copyin((void *)arg, &len, sizeof(len), mode) != 0) {
1035932509f9SAndreas Jaekel 		return EFAULT;
1036932509f9SAndreas Jaekel 	}
1037932509f9SAndreas Jaekel 	if (len > ZEV_MAX_QUEUE_LEN) {
1038932509f9SAndreas Jaekel 		return EINVAL;
1039932509f9SAndreas Jaekel 	}
1040932509f9SAndreas Jaekel 	mutex_enter(&zev_mutex);
1041932509f9SAndreas Jaekel 	zev_statistics.zev_max_queue_len = len;
1042932509f9SAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
1043932509f9SAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
1044932509f9SAndreas Jaekel 		if (!q)
1045932509f9SAndreas Jaekel 			continue;
1046932509f9SAndreas Jaekel 		if (q->zq_max_queue_len <=
1047932509f9SAndreas Jaekel 		    zev_statistics.zev_max_queue_len)
1048932509f9SAndreas Jaekel 			continue;
1049932509f9SAndreas Jaekel 		q->zq_max_queue_len = zev_statistics.zev_max_queue_len;
1050932509f9SAndreas Jaekel 	}
1051932509f9SAndreas Jaekel 	cv_broadcast(&zev_condvar);
1052932509f9SAndreas Jaekel 	mutex_exit(&zev_mutex);
1053932509f9SAndreas Jaekel 	return 0;
1054932509f9SAndreas Jaekel }
1055932509f9SAndreas Jaekel 
1056e3455c18SAndreas Jaekel static int
zev_ioc_get_zev_version(intptr_t arg,int mode)1057e3455c18SAndreas Jaekel zev_ioc_get_zev_version(intptr_t arg, int mode)
1058e3455c18SAndreas Jaekel {
1059e3455c18SAndreas Jaekel 	zev_ioctl_get_zev_version vi;
1060e3455c18SAndreas Jaekel 	vi.zev_major_version = ZEV_MAJOR_VERSION;
1061e3455c18SAndreas Jaekel 	vi.zev_minor_version = ZEV_MINOR_VERSION;
1062e3455c18SAndreas Jaekel 	if (ddi_copyout(&vi, (void *)arg, sizeof(vi), mode) != 0)
1063e3455c18SAndreas Jaekel 		return EFAULT;
1064e3455c18SAndreas Jaekel 	return 0;
1065e3455c18SAndreas Jaekel }
1066e3455c18SAndreas Jaekel 
1067548c8b6eSAndreas Jaekel /* ARGSUSED */
1068a18c35b9SAndreas Jaekel static int
zev_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)1069a18c35b9SAndreas Jaekel zev_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
1070a18c35b9SAndreas Jaekel {
1071a18c35b9SAndreas Jaekel 	zev_statistics_t zs;
1072a18c35b9SAndreas Jaekel 	zev_ioctl_poolarg_t pa;
1073888fea18SAndreas Jaekel 	zev_ioctl_mark_t mark;
1074888fea18SAndreas Jaekel 	zev_mark_t *rec;
1075888fea18SAndreas Jaekel 	int msg_size;
1076888fea18SAndreas Jaekel 	zev_msg_t *msg;
1077888fea18SAndreas Jaekel 	uint64_t mark_id;
1078add9520fSAndreas Jaekel 	minor_t minor;
1079add9520fSAndreas Jaekel 	zev_queue_t *req_q;
1080add9520fSAndreas Jaekel 	int ret = 0;
1081a18c35b9SAndreas Jaekel 
1082add9520fSAndreas Jaekel 	minor = getminor(dev);
1083add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
1084add9520fSAndreas Jaekel 	if ((req_q = ddi_get_soft_state(statep, minor)) == NULL) {
1085add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1086a18c35b9SAndreas Jaekel 		return (ENXIO);
1087add9520fSAndreas Jaekel 	}
1088add9520fSAndreas Jaekel 	zev_queue_hold(req_q);
1089add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
1090808a670aSAndreas Jaekel 	/*
1091808a670aSAndreas Jaekel 	 * all structures passed between kernel and userspace
1092808a670aSAndreas Jaekel 	 * are now compatible between 64 and 32 bit.  Model
1093add9520fSAndreas Jaekel 	 * conversion can be ignored.
1094808a670aSAndreas Jaekel 	 */
1095a18c35b9SAndreas Jaekel 	switch (cmd) {
1096add9520fSAndreas Jaekel 	case ZEV_IOC_GET_GLOBAL_STATISTICS:
1097a18c35b9SAndreas Jaekel 		/* ddi_copyout() can take a long time.  Better make
1098a18c35b9SAndreas Jaekel 		   a copy to be able to release the mutex faster. */
1099a18c35b9SAndreas Jaekel 		mutex_enter(&zev_mutex);
1100548c8b6eSAndreas Jaekel 		(void) memcpy(&zs, &zev_statistics, sizeof(zs));
1101a18c35b9SAndreas Jaekel 		mutex_exit(&zev_mutex);
1102a18c35b9SAndreas Jaekel 		if (ddi_copyout(&zs, (void *)arg, sizeof(zs), mode) != 0)
1103add9520fSAndreas Jaekel 			ret = EFAULT;
1104add9520fSAndreas Jaekel 		break;
1105add9520fSAndreas Jaekel 	case ZEV_IOC_GET_QUEUE_STATISTICS:
1106add9520fSAndreas Jaekel 		ret = zev_ioc_get_queue_statistics(req_q, arg, mode);
1107a18c35b9SAndreas Jaekel 		break;
1108a18c35b9SAndreas Jaekel 	case ZEV_IOC_MUTE_POOL:
1109a18c35b9SAndreas Jaekel 	case ZEV_IOC_UNMUTE_POOL:
1110add9520fSAndreas Jaekel 		if (ddi_copyin((void *)arg, &pa, sizeof(pa), mode) != 0) {
1111add9520fSAndreas Jaekel 			ret = EFAULT;
1112add9520fSAndreas Jaekel 			break;
1113add9520fSAndreas Jaekel 		}
1114add9520fSAndreas Jaekel 		if (pa.zev_poolname_len >=MAXPATHLEN) {
1115add9520fSAndreas Jaekel 			ret = EINVAL;
1116add9520fSAndreas Jaekel 			break;
1117add9520fSAndreas Jaekel 		}
1118a18c35b9SAndreas Jaekel 		pa.zev_poolname[pa.zev_poolname_len] = '\0';
1119a18c35b9SAndreas Jaekel 		if (cmd == ZEV_IOC_MUTE_POOL) {
1120add9520fSAndreas Jaekel 			ret = zev_ioc_mute_pool(pa.zev_poolname);
1121a18c35b9SAndreas Jaekel 		} else {
1122add9520fSAndreas Jaekel 			ret = zev_ioc_unmute_pool(pa.zev_poolname);
1123a18c35b9SAndreas Jaekel 		}
1124add9520fSAndreas Jaekel 		break;
1125a18c35b9SAndreas Jaekel 	case ZEV_IOC_SET_MAX_QUEUE_LEN:
1126932509f9SAndreas Jaekel 		ret = zev_ioc_set_max_queue_len(req_q, arg, mode);
1127a18c35b9SAndreas Jaekel 		break;
1128add9520fSAndreas Jaekel 	case ZEV_IOC_GET_QUEUE_PROPERTIES:
1129add9520fSAndreas Jaekel 		ret = zev_ioc_get_queue_properties(req_q, arg, mode);
1130add9520fSAndreas Jaekel 		break;
1131add9520fSAndreas Jaekel 	case ZEV_IOC_SET_QUEUE_PROPERTIES:
1132add9520fSAndreas Jaekel 		ret = zev_ioc_set_queue_properties(req_q, arg, mode);
1133fec460f8SAndreas Jaekel 		break;
1134888fea18SAndreas Jaekel 	case ZEV_IOC_MARK:
1135add9520fSAndreas Jaekel 		if (ddi_copyin((void *)arg, &mark, sizeof(mark), mode) != 0) {
1136add9520fSAndreas Jaekel 			ret = EFAULT;
1137add9520fSAndreas Jaekel 			break;
1138add9520fSAndreas Jaekel 		}
1139888fea18SAndreas Jaekel 		/* prepare message */
1140888fea18SAndreas Jaekel 		msg_size = sizeof(*rec) + mark.zev_payload_len + 1;
1141205ed6bfSAndreas Jaekel 		msg = zev_alloc(sizeof(*msg) + msg_size);
1142888fea18SAndreas Jaekel 		msg->size = msg_size;
1143888fea18SAndreas Jaekel 		rec = (zev_mark_t *)(msg + 1);
1144888fea18SAndreas Jaekel 		rec->record_len = msg_size;
1145888fea18SAndreas Jaekel 		rec->op = ZEV_OP_MARK;
1146888fea18SAndreas Jaekel 		rec->op_time = ddi_get_time();
1147888fea18SAndreas Jaekel 		rec->guid = mark.zev_guid;
1148888fea18SAndreas Jaekel 		rec->payload_len = mark.zev_payload_len;
1149888fea18SAndreas Jaekel 		/* get payload */
1150888fea18SAndreas Jaekel 		if (ddi_copyin(((char *)arg) + sizeof(mark),
1151888fea18SAndreas Jaekel 		               ZEV_PAYLOAD(rec),
1152888fea18SAndreas Jaekel 		               mark.zev_payload_len, mode) != 0) {
1153205ed6bfSAndreas Jaekel 			zev_free(msg, msg_size);
1154add9520fSAndreas Jaekel 			ret = EFAULT;
1155add9520fSAndreas Jaekel 			break;
1156888fea18SAndreas Jaekel 		}
1157888fea18SAndreas Jaekel 		*(ZEV_PAYLOAD(rec) + mark.zev_payload_len) = '\0';
1158888fea18SAndreas Jaekel 		/* get mark id and queue message */
1159888fea18SAndreas Jaekel 		mutex_enter(&zev_mark_id_mutex);
1160888fea18SAndreas Jaekel 		mark_id = zev_mark_id++;
1161888fea18SAndreas Jaekel 		mutex_exit(&zev_mark_id_mutex);
1162888fea18SAndreas Jaekel 		rec->mark_id = mark_id;
1163888fea18SAndreas Jaekel 		zev_queue_message(ZEV_OP_MARK, msg);
1164888fea18SAndreas Jaekel 		/* report mark id to userland, ignore errors */
1165888fea18SAndreas Jaekel 		mark.zev_mark_id = mark_id;
1166888fea18SAndreas Jaekel 		ddi_copyout(&mark, (void *)arg, sizeof(mark), mode);
1167888fea18SAndreas Jaekel 		break;
1168add9520fSAndreas Jaekel 	case ZEV_IOC_ADD_QUEUE:
1169add9520fSAndreas Jaekel 		if (minor != ZEV_CONTROL_DEVICE_MINOR) {
1170add9520fSAndreas Jaekel 			ret = EACCES;
1171add9520fSAndreas Jaekel 			break;
1172add9520fSAndreas Jaekel 		}
1173add9520fSAndreas Jaekel 		ret = zev_ioc_add_queue(req_q, arg, mode);
1174add9520fSAndreas Jaekel 		break;
1175add9520fSAndreas Jaekel 	case ZEV_IOC_REMOVE_QUEUE:
1176add9520fSAndreas Jaekel 		if (minor != ZEV_CONTROL_DEVICE_MINOR) {
1177add9520fSAndreas Jaekel 			ret = EACCES;
1178add9520fSAndreas Jaekel 			break;
1179add9520fSAndreas Jaekel 		}
1180add9520fSAndreas Jaekel 		ret = zev_ioc_remove_queue(req_q, arg, mode);
1181add9520fSAndreas Jaekel 		break;
1182add9520fSAndreas Jaekel 	case ZEV_IOC_GET_DEBUG_INFO:
1183add9520fSAndreas Jaekel 		ret = zev_ioc_get_debug_info(req_q, arg, mode);
1184add9520fSAndreas Jaekel 		break;
1185add9520fSAndreas Jaekel 	case ZEV_IOC_GET_QUEUE_LIST:
1186add9520fSAndreas Jaekel 		ret = zev_ioc_get_queue_list(req_q, arg, mode);
1187add9520fSAndreas Jaekel 		break;
118842110aacSAndreas Jaekel 	case ZEV_IOC_GET_FILE_SIGNATURES:
118942110aacSAndreas Jaekel 		ret = zev_ioc_get_signatures(arg, mode);
119042110aacSAndreas Jaekel 		break;
1191e3455c18SAndreas Jaekel 	case ZEV_IOC_GET_ZEV_VERSION:
1192e3455c18SAndreas Jaekel 		ret = zev_ioc_get_zev_version(arg, mode);
1193e3455c18SAndreas Jaekel 		break;
1194a18c35b9SAndreas Jaekel 	default:
1195a18c35b9SAndreas Jaekel 		/* generic "ioctl unknown" error */
1196add9520fSAndreas Jaekel 		ret = ENOTTY;
1197a18c35b9SAndreas Jaekel 	}
1198add9520fSAndreas Jaekel 
1199add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
1200add9520fSAndreas Jaekel 	zev_queue_release(req_q);
1201add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
120242110aacSAndreas Jaekel 	if (ret)
1203*41aa6ebdSSimon Klinkert 		return(SET_ERROR(ret));
1204add9520fSAndreas Jaekel 	return (ret);
1205a18c35b9SAndreas Jaekel }
1206a18c35b9SAndreas Jaekel 
1207a18c35b9SAndreas Jaekel static int
zev_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)1208a18c35b9SAndreas Jaekel zev_chpoll(dev_t dev, short events, int anyyet,
1209a18c35b9SAndreas Jaekel     short *reventsp, struct pollhead **phpp)
1210a18c35b9SAndreas Jaekel {
1211add9520fSAndreas Jaekel 	int minor;
1212a18c35b9SAndreas Jaekel 	short revent = 0;
1213add9520fSAndreas Jaekel 	zev_queue_t *q;
1214a18c35b9SAndreas Jaekel 
1215add9520fSAndreas Jaekel 	/* use minor-specific queue context and it's pollhead */
1216add9520fSAndreas Jaekel 	minor = getminor(dev);
1217add9520fSAndreas Jaekel 	if (minor == ZEV_CONTROL_DEVICE_MINOR)
1218add9520fSAndreas Jaekel 		return (EINVAL);
1219add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
1220add9520fSAndreas Jaekel 	if ((q = ddi_get_soft_state(statep, minor)) == NULL) {
1221add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1222a18c35b9SAndreas Jaekel 		return (ENXIO);
1223add9520fSAndreas Jaekel 	}
1224a18c35b9SAndreas Jaekel 	revent = 0;
1225a18c35b9SAndreas Jaekel 	if ((events & POLLIN)) {
1226add9520fSAndreas Jaekel 		if (q->zq_oldest)
1227a18c35b9SAndreas Jaekel 			revent |= POLLIN;
1228a18c35b9SAndreas Jaekel 	}
1229a18c35b9SAndreas Jaekel 	if (revent == 0) {
1230a18c35b9SAndreas Jaekel 		if (!anyyet) {
1231add9520fSAndreas Jaekel 			*phpp = &q->zq_pollhead;
1232a18c35b9SAndreas Jaekel 		}
1233a18c35b9SAndreas Jaekel 	}
1234a18c35b9SAndreas Jaekel 	*reventsp = revent;
1235add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
1236a18c35b9SAndreas Jaekel 	return (0);
1237a18c35b9SAndreas Jaekel }
1238a18c35b9SAndreas Jaekel 
1239548c8b6eSAndreas Jaekel /* ARGSUSED */
1240a18c35b9SAndreas Jaekel static int
zev_read(dev_t dev,struct uio * uio_p,cred_t * crep_p)1241a18c35b9SAndreas Jaekel zev_read(dev_t dev, struct uio *uio_p, cred_t *crep_p)
1242a18c35b9SAndreas Jaekel {
1243add9520fSAndreas Jaekel 	minor_t minor;
1244a18c35b9SAndreas Jaekel 	offset_t off;
1245a18c35b9SAndreas Jaekel 	int ret = 0;
1246aafc540fSAndreas Jaekel 	zev_msg_t *msg;
1247aafc540fSAndreas Jaekel 	char *data;
1248add9520fSAndreas Jaekel 	zev_queue_t *q;
1249a18c35b9SAndreas Jaekel 
1250add9520fSAndreas Jaekel 	minor = getminor(dev);
1251add9520fSAndreas Jaekel 	if (minor == ZEV_CONTROL_DEVICE_MINOR)
1252add9520fSAndreas Jaekel 		return (EINVAL);
1253add9520fSAndreas Jaekel 
1254a18c35b9SAndreas Jaekel 	mutex_enter(&zev_mutex);
1255add9520fSAndreas Jaekel 	q = ddi_get_soft_state(statep, minor);
1256add9520fSAndreas Jaekel 	if (q == NULL) {
1257add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1258add9520fSAndreas Jaekel 		return (ENXIO);
1259add9520fSAndreas Jaekel 	}
1260add9520fSAndreas Jaekel 	off = uio_p->uio_loffset;
1261add9520fSAndreas Jaekel 	msg = q->zq_oldest;
12626a6a51eeSAndreas Jaekel 	while (msg == NULL) {
12636a6a51eeSAndreas Jaekel 		if (!ddi_can_receive_sig()) {
12646a6a51eeSAndreas Jaekel 			/*
12656a6a51eeSAndreas Jaekel 			 * read() shouldn't block because this thread
12666a6a51eeSAndreas Jaekel 			 * can't receive signals. (e.g., it might be
12676a6a51eeSAndreas Jaekel 			 * torn down by exit() right now.)
12686a6a51eeSAndreas Jaekel 			 */
1269aafc540fSAndreas Jaekel 			mutex_exit(&zev_mutex);
1270aafc540fSAndreas Jaekel 			return 0;
1271a18c35b9SAndreas Jaekel 		}
12726a6a51eeSAndreas Jaekel 		if (cv_wait_sig(&q->zq_condvar, &zev_mutex) == 0) {
12736a6a51eeSAndreas Jaekel 			/* signal received. */
12746a6a51eeSAndreas Jaekel 			mutex_exit(&zev_mutex);
12756a6a51eeSAndreas Jaekel 			return EINTR;
12766a6a51eeSAndreas Jaekel 		}
12776a6a51eeSAndreas Jaekel 		msg = q->zq_oldest;
12786a6a51eeSAndreas Jaekel 	}
1279aafc540fSAndreas Jaekel 	if (msg->size > uio_p->uio_resid) {
1280aafc540fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1281aafc540fSAndreas Jaekel 		return E2BIG;
1282a18c35b9SAndreas Jaekel 	}
1283d979f56cSAndreas Jaekel 	while (msg && uio_p->uio_resid >= msg->size) {
1284aafc540fSAndreas Jaekel 		data = (char *)(msg + 1);
1285aafc540fSAndreas Jaekel 		ret = uiomove(data, msg->size, UIO_READ, uio_p);
1286aafc540fSAndreas Jaekel 		if (ret != 0) {
1287a18c35b9SAndreas Jaekel 			mutex_exit(&zev_mutex);
1288d979f56cSAndreas Jaekel 			cmn_err(CE_WARN, "zev: uiomove failed; messages lost");
1289a18c35b9SAndreas Jaekel 			uio_p->uio_loffset = off;
1290a18c35b9SAndreas Jaekel 			return (ret);
1291a18c35b9SAndreas Jaekel 		}
1292add9520fSAndreas Jaekel 		q->zq_oldest = msg->next;
1293add9520fSAndreas Jaekel 		q->zq_bytes_read += msg->size;
1294add9520fSAndreas Jaekel 		q->zq_queue_len -= msg->size;
1295add9520fSAndreas Jaekel 		q->zq_queue_messages--;
1296add9520fSAndreas Jaekel 		msg->read++;
1297add9520fSAndreas Jaekel 		msg = q->zq_oldest;
1298d979f56cSAndreas Jaekel 	}
129902e8461dSAndreas Jaekel 	zev_queue_trim();
1300aafc540fSAndreas Jaekel 	cv_broadcast(&zev_condvar);
1301aafc540fSAndreas Jaekel 	mutex_exit(&zev_mutex);
1302aafc540fSAndreas Jaekel 	uio_p->uio_loffset = off;
1303aafc540fSAndreas Jaekel 	return 0;
1304aafc540fSAndreas Jaekel }
1305a18c35b9SAndreas Jaekel 
1306548c8b6eSAndreas Jaekel /* ARGSUSED */
1307a18c35b9SAndreas Jaekel static int
zev_close(dev_t dev,int flag,int otyp,cred_t * crepd)1308a18c35b9SAndreas Jaekel zev_close(dev_t dev, int flag, int otyp, cred_t *crepd)
1309a18c35b9SAndreas Jaekel {
1310add9520fSAndreas Jaekel 	zev_queue_t *q;
1311add9520fSAndreas Jaekel 	int minor;
1312a18c35b9SAndreas Jaekel 
1313add9520fSAndreas Jaekel 	minor = getminor(dev);
1314a18c35b9SAndreas Jaekel 	if (otyp != OTYP_CHR)
1315a18c35b9SAndreas Jaekel 		return (EINVAL);
1316add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
1317add9520fSAndreas Jaekel 	if ((q = ddi_get_soft_state(statep, minor)) == NULL) {
1318add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1319add9520fSAndreas Jaekel 		return (ENXIO);
1320add9520fSAndreas Jaekel 	}
1321add9520fSAndreas Jaekel 	if (q->zq_busy != B_TRUE) {
1322add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1323a18c35b9SAndreas Jaekel 		return (EINVAL);
1324a18c35b9SAndreas Jaekel 	}
1325add9520fSAndreas Jaekel 	q->zq_busy = B_FALSE;
1326add9520fSAndreas Jaekel 	if ((q->zq_flags & ZEV_FL_PERSISTENT) == 0)
1327add9520fSAndreas Jaekel 		zev_queue_release(q);
1328add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
1329a18c35b9SAndreas Jaekel 	return (0);
1330a18c35b9SAndreas Jaekel }
1331a18c35b9SAndreas Jaekel 
1332548c8b6eSAndreas Jaekel /* ARGSUSED */
1333a18c35b9SAndreas Jaekel static int
zev_open(dev_t * devp,int flag,int otyp,cred_t * credp)1334a18c35b9SAndreas Jaekel zev_open(dev_t *devp, int flag, int otyp, cred_t *credp)
1335a18c35b9SAndreas Jaekel {
1336add9520fSAndreas Jaekel 	zev_queue_t *q;
1337add9520fSAndreas Jaekel 	minor_t minor;
13389fef2cddSAndreas Jaekel 	char zq_name[ZEV_MAX_QUEUE_NAME_LEN];
13399fef2cddSAndreas Jaekel 	int ret;
1340a18c35b9SAndreas Jaekel 
1341add9520fSAndreas Jaekel 	minor = getminor(*devp);
1342a18c35b9SAndreas Jaekel 	if (otyp != OTYP_CHR)
1343a18c35b9SAndreas Jaekel 		return (EINVAL);
1344a18c35b9SAndreas Jaekel 	if (drv_priv(credp) != 0)
1345a18c35b9SAndreas Jaekel 		return (EPERM);
13469fef2cddSAndreas Jaekel 	if (minor == ZEV_TMPQUEUE_DEVICE_MINOR) {
13479fef2cddSAndreas Jaekel 		/* get control queue soft state to have dip */
13489fef2cddSAndreas Jaekel 		if ((q = ddi_get_soft_state(statep,
13499fef2cddSAndreas Jaekel 		                            ZEV_CONTROL_DEVICE_MINOR)) == NULL){
13509fef2cddSAndreas Jaekel 			mutex_exit(&zev_mutex);
13519fef2cddSAndreas Jaekel 			return (ENXIO);
13529fef2cddSAndreas Jaekel 		}
13539fef2cddSAndreas Jaekel 
13549fef2cddSAndreas Jaekel 		/* create new temporary queue and return it. */
13559fef2cddSAndreas Jaekel 
13569fef2cddSAndreas Jaekel 		snprintf(zq_name, sizeof(zq_name),
13579fef2cddSAndreas Jaekel 		         ZEV_TMPQUEUE_DEVICE_NAME ".%d", zev_tmpqueue_num++);
13589fef2cddSAndreas Jaekel 
13596ac2e832SAndreas Jaekel 		ret = zev_queue_new(&q, q->zq_dip, zq_name, 0,
13606ac2e832SAndreas Jaekel 		                    ZEV_FL_INITIALLY_EMPTY);
13619fef2cddSAndreas Jaekel 		if (ret) {
13629fef2cddSAndreas Jaekel 			return ret;
13639fef2cddSAndreas Jaekel 		}
13649fef2cddSAndreas Jaekel 
13659fef2cddSAndreas Jaekel 		q->zq_busy = B_TRUE;
13669fef2cddSAndreas Jaekel 		*devp = makedevice(getmajor(*devp), q->zq_minor_number);
13679fef2cddSAndreas Jaekel 		return 0;
13689fef2cddSAndreas Jaekel 	}
1369add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
1370add9520fSAndreas Jaekel 	if ((q = ddi_get_soft_state(statep, minor)) == NULL) {
1371add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1372add9520fSAndreas Jaekel 		return (ENXIO);
1373add9520fSAndreas Jaekel 	}
1374add9520fSAndreas Jaekel 	if (minor == ZEV_CONTROL_DEVICE_MINOR) {
1375add9520fSAndreas Jaekel 		/* control device may be used in parallel */
1376add9520fSAndreas Jaekel 		q->zq_busy = B_TRUE;
1377add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1378add9520fSAndreas Jaekel 		return 0;
1379add9520fSAndreas Jaekel 	}
1380add9520fSAndreas Jaekel 	if (q->zq_busy == B_TRUE) {
1381add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1382a18c35b9SAndreas Jaekel 		return (EBUSY);
1383a18c35b9SAndreas Jaekel 	}
1384add9520fSAndreas Jaekel 	q->zq_busy = B_TRUE;	/* can only be opened exclusively */
1385add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
1386a18c35b9SAndreas Jaekel 	return (0);
1387a18c35b9SAndreas Jaekel }
1388a18c35b9SAndreas Jaekel 
1389a18c35b9SAndreas Jaekel static struct cb_ops zev_cb_ops = {
1390a18c35b9SAndreas Jaekel 	zev_open,		/* open */
1391a18c35b9SAndreas Jaekel 	zev_close,		/* close */
1392a18c35b9SAndreas Jaekel 	nodev,			/* strategy */
1393a18c35b9SAndreas Jaekel 	nodev,			/* print */
1394a18c35b9SAndreas Jaekel 	nodev,			/* dump */
1395a18c35b9SAndreas Jaekel 	zev_read,		/* read */
1396a18c35b9SAndreas Jaekel 	nodev,			/* write */
1397a18c35b9SAndreas Jaekel 	zev_ioctl,		/* ioctl */
1398a18c35b9SAndreas Jaekel 	nodev,			/* devmap */
1399a18c35b9SAndreas Jaekel 	nodev,			/* mmap */
1400a18c35b9SAndreas Jaekel 	nodev,			/* segmap */
1401a18c35b9SAndreas Jaekel 	zev_chpoll,		/* chpoll */
1402a18c35b9SAndreas Jaekel 	ddi_prop_op,		/* prop_op */
1403a18c35b9SAndreas Jaekel 	NULL,			/* streamtab */
1404a18c35b9SAndreas Jaekel 	D_MP | D_64BIT,		/* cb_flag */
1405a18c35b9SAndreas Jaekel 	CB_REV,			/* cb_rev */
1406a18c35b9SAndreas Jaekel 	nodev,			/* aread */
1407a18c35b9SAndreas Jaekel 	nodev,			/* awrite */
1408a18c35b9SAndreas Jaekel };
1409a18c35b9SAndreas Jaekel 
1410a18c35b9SAndreas Jaekel static void
zev_free_instance(dev_info_t * dip)1411a18c35b9SAndreas Jaekel zev_free_instance(dev_info_t *dip)
1412a18c35b9SAndreas Jaekel {
1413a18c35b9SAndreas Jaekel 	int instance;
1414add9520fSAndreas Jaekel 	zev_queue_t *q;
14156a6a51eeSAndreas Jaekel 	int i;
1416add9520fSAndreas Jaekel 
1417a18c35b9SAndreas Jaekel 	instance = ddi_get_instance(dip);
1418add9520fSAndreas Jaekel 	if (instance != 0) {
1419add9520fSAndreas Jaekel 		cmn_err(CE_WARN, "zev: tried to free instance != 0 (%d)",
1420add9520fSAndreas Jaekel 		        instance);
1421add9520fSAndreas Jaekel 		return;
1422a18c35b9SAndreas Jaekel 	}
1423add9520fSAndreas Jaekel 
1424add9520fSAndreas Jaekel 	ddi_remove_minor_node(dip, NULL);
1425cfdb9b63SAndreas Jaekel 	devfs_clean(ddi_root_node() ? ddi_root_node() : dip,
1426cfdb9b63SAndreas Jaekel 	            NULL, DV_CLEAN_FORCE);
1427add9520fSAndreas Jaekel 
1428add9520fSAndreas Jaekel 	/* stop pollwakeup thread */
1429add9520fSAndreas Jaekel 	zev_wakeup_thread_run = 0;
1430add9520fSAndreas Jaekel 	if (zev_poll_wakeup_thread != NULL) {
1431add9520fSAndreas Jaekel 		thread_join(zev_poll_wakeup_thread->t_did);
1432add9520fSAndreas Jaekel 		zev_poll_wakeup_thread = NULL;
1433add9520fSAndreas Jaekel 	}
1434add9520fSAndreas Jaekel 
1435add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
1436add9520fSAndreas Jaekel 
1437add9520fSAndreas Jaekel 	/* remove "ctrl" dummy queue */
1438add9520fSAndreas Jaekel 	q = ddi_get_soft_state(statep, ZEV_CONTROL_DEVICE_MINOR);
1439add9520fSAndreas Jaekel 	if (q) {
1440add9520fSAndreas Jaekel 		ddi_soft_state_free(statep, ZEV_CONTROL_DEVICE_MINOR);
1441add9520fSAndreas Jaekel 		ZEV_MEM_SUB(sizeof(zev_queue_t));
1442add9520fSAndreas Jaekel 	}
1443add9520fSAndreas Jaekel 
1444add9520fSAndreas Jaekel 	/* remove all other queues */
14456a6a51eeSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
14466a6a51eeSAndreas Jaekel 		q = zev_queues[i- ZEV_MINOR_MIN];
14476a6a51eeSAndreas Jaekel 		if (!q)
14486a6a51eeSAndreas Jaekel 			continue;
1449add9520fSAndreas Jaekel 		ASSERT(q->zq_refcnt == 1);
1450add9520fSAndreas Jaekel 		zev_queue_release(q);
1451add9520fSAndreas Jaekel 	}
1452add9520fSAndreas Jaekel 	zev_queue_trim();
14536a6a51eeSAndreas Jaekel 	bzero(&zev_queues, sizeof(zev_queues));
1454add9520fSAndreas Jaekel 
1455add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
1456add9520fSAndreas Jaekel 
1457a18c35b9SAndreas Jaekel }
1458a18c35b9SAndreas Jaekel 
1459a18c35b9SAndreas Jaekel static int
zev_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)1460a18c35b9SAndreas Jaekel zev_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1461a18c35b9SAndreas Jaekel {
1462a18c35b9SAndreas Jaekel 	int instance;
1463add9520fSAndreas Jaekel 	zev_queue_t *q;
1464add9520fSAndreas Jaekel 
1465a18c35b9SAndreas Jaekel 	/* called once per instance with DDI_DETACH,
1466a18c35b9SAndreas Jaekel 	   may be called to suspend */
1467a18c35b9SAndreas Jaekel 	switch (cmd) {
1468a18c35b9SAndreas Jaekel 	case DDI_DETACH:
1469a18c35b9SAndreas Jaekel 		/* instance busy? */
1470a18c35b9SAndreas Jaekel 		instance = ddi_get_instance(dip);
1471add9520fSAndreas Jaekel 		if (instance != 0) {	/* hardcoded in zev.conf */
1472add9520fSAndreas Jaekel 			/* this module only supports one instance. */
14739a535601SAndreas Jaekel 			return (DDI_FAILURE);
1474a18c35b9SAndreas Jaekel 		}
1475add9520fSAndreas Jaekel 
1476add9520fSAndreas Jaekel 		mutex_enter(&zev_mutex);
1477add9520fSAndreas Jaekel 		if (!zev_attached) {
1478add9520fSAndreas Jaekel 			mutex_exit(&zev_mutex);
1479add9520fSAndreas Jaekel 			return (DDI_FAILURE);
1480add9520fSAndreas Jaekel 		}
1481add9520fSAndreas Jaekel 
1482add9520fSAndreas Jaekel 		/* check "ctrl" queue to see if t is busy */
1483add9520fSAndreas Jaekel 		q = ddi_get_soft_state(statep, ZEV_CONTROL_DEVICE_MINOR);
1484add9520fSAndreas Jaekel 		if (q == NULL) {
1485add9520fSAndreas Jaekel 			mutex_exit(&zev_mutex);
1486add9520fSAndreas Jaekel 			return (DDI_FAILURE);
1487add9520fSAndreas Jaekel 		}
1488add9520fSAndreas Jaekel 		if (q->zq_busy) {
1489add9520fSAndreas Jaekel 			mutex_exit(&zev_mutex);
1490add9520fSAndreas Jaekel 			return (DDI_FAILURE);
1491add9520fSAndreas Jaekel 		}
1492add9520fSAndreas Jaekel 		/* are there any queues? */
14936a6a51eeSAndreas Jaekel 		if (zev_queue_cnt > 0) {
1494add9520fSAndreas Jaekel 			mutex_exit(&zev_mutex);
1495add9520fSAndreas Jaekel 			return (DDI_FAILURE);
1496add9520fSAndreas Jaekel 		}
1497add9520fSAndreas Jaekel 
1498add9520fSAndreas Jaekel 		zev_attached = B_FALSE;
1499add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1500add9520fSAndreas Jaekel 
1501add9520fSAndreas Jaekel 		/* switch ZFS event callbacks back to default */
1502add9520fSAndreas Jaekel 		rw_enter(&rz_zev_rwlock, RW_WRITER);
1503add9520fSAndreas Jaekel 		rz_zev_callbacks = rz_zev_default_callbacks;
1504205ed6bfSAndreas Jaekel 		rz_zev_set_active(B_FALSE);
1505add9520fSAndreas Jaekel 		rw_exit(&rz_zev_rwlock);
1506add9520fSAndreas Jaekel 
1507add9520fSAndreas Jaekel 		/* no thread is inside of the callbacks anymore. */
1508add9520fSAndreas Jaekel 
1509a18c35b9SAndreas Jaekel 		/* free resources allocated for this instance */
1510a18c35b9SAndreas Jaekel 		zev_free_instance(dip);
1511205ed6bfSAndreas Jaekel 		zev_chksum_fini();
15126a6a51eeSAndreas Jaekel #if 0
1513add9520fSAndreas Jaekel 		cmn_err(CE_WARN, "zev: allocated memory at detach: %" PRIu64,
1514add9520fSAndreas Jaekel 			zev_memory_allocated - zev_memory_freed);
15156a6a51eeSAndreas Jaekel #endif
1516a18c35b9SAndreas Jaekel 		return (DDI_SUCCESS);
1517a18c35b9SAndreas Jaekel 	case DDI_SUSPEND:
1518a18c35b9SAndreas Jaekel 		/* kernel must not suspend zev devices while ZFS is running */
1519a18c35b9SAndreas Jaekel 		return (DDI_FAILURE);
1520a18c35b9SAndreas Jaekel 	default:
1521a18c35b9SAndreas Jaekel 		return (DDI_FAILURE);
1522a18c35b9SAndreas Jaekel 	}
1523a18c35b9SAndreas Jaekel }
1524a18c35b9SAndreas Jaekel 
1525a18c35b9SAndreas Jaekel static int
zev_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1526a18c35b9SAndreas Jaekel zev_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1527a18c35b9SAndreas Jaekel {
1528a18c35b9SAndreas Jaekel 	/* called once per instance with DDI_ATTACH,
1529a18c35b9SAndreas Jaekel 	   may be called to resume */
1530a18c35b9SAndreas Jaekel 	int instance;
1531add9520fSAndreas Jaekel 	int error;
1532add9520fSAndreas Jaekel 	zev_queue_t *q;
1533a18c35b9SAndreas Jaekel 	switch (cmd) {
1534a18c35b9SAndreas Jaekel 	case DDI_ATTACH:
1535add9520fSAndreas Jaekel 		/* create instance state */
1536a18c35b9SAndreas Jaekel 		instance = ddi_get_instance(dip);
1537add9520fSAndreas Jaekel 		if (instance != 0) {	/* hardcoded in zev.conf */
1538add9520fSAndreas Jaekel 			/* this module only supports one instance. */
1539a18c35b9SAndreas Jaekel 			return (DDI_FAILURE);
1540a18c35b9SAndreas Jaekel 		}
1541add9520fSAndreas Jaekel 
1542add9520fSAndreas Jaekel 		mutex_enter(&zev_mutex);
1543add9520fSAndreas Jaekel 		if (zev_attached) {
1544add9520fSAndreas Jaekel 			mutex_exit(&zev_mutex);
1545a18c35b9SAndreas Jaekel 			return (DDI_FAILURE);
1546a18c35b9SAndreas Jaekel 		}
1547add9520fSAndreas Jaekel 		if (ddi_soft_state_zalloc(statep, ZEV_CONTROL_DEVICE_MINOR) !=
1548add9520fSAndreas Jaekel 		    DDI_SUCCESS) {
1549add9520fSAndreas Jaekel 			mutex_exit(&zev_mutex);
1550add9520fSAndreas Jaekel 			return (DDI_FAILURE);
1551add9520fSAndreas Jaekel 		}
1552add9520fSAndreas Jaekel 		ZEV_MEM_ADD(sizeof(zev_queue_t));
1553add9520fSAndreas Jaekel 		zev_attached = B_TRUE;
1554add9520fSAndreas Jaekel 
1555add9520fSAndreas Jaekel 		/* init queue list */
15566a6a51eeSAndreas Jaekel 		bzero(&zev_queues, sizeof(zev_queues));
1557add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1558add9520fSAndreas Jaekel 
1559add9520fSAndreas Jaekel 		/* create a dummy queue for management of "ctrl" */
1560add9520fSAndreas Jaekel 
1561add9520fSAndreas Jaekel 		q = ddi_get_soft_state(statep, ZEV_CONTROL_DEVICE_MINOR);
1562add9520fSAndreas Jaekel 		q->zq_dip = dip;
1563add9520fSAndreas Jaekel 		q->zq_refcnt = 1;
1564add9520fSAndreas Jaekel 		q->zq_busy = B_FALSE;
1565add9520fSAndreas Jaekel 		q->zq_minor_number = ZEV_CONTROL_DEVICE_MINOR;
1566add9520fSAndreas Jaekel 		q->zq_flags = ZEV_FL_PERSISTENT;
1567add9520fSAndreas Jaekel 		strcpy(q->zq_name, ZEV_CONTROL_DEVICE_NAME);
1568add9520fSAndreas Jaekel 
1569add9520fSAndreas Jaekel 		/* create device node for "ctrl" */
1570add9520fSAndreas Jaekel 		if (ddi_create_minor_node(dip, ZEV_CONTROL_DEVICE_NAME,
1571add9520fSAndreas Jaekel 		    S_IFCHR, ZEV_CONTROL_DEVICE_MINOR,
1572add9520fSAndreas Jaekel 		    DDI_PSEUDO, 0) == DDI_FAILURE) {
1573add9520fSAndreas Jaekel 			goto fail;
1574add9520fSAndreas Jaekel 		}
1575add9520fSAndreas Jaekel 
1576add9520fSAndreas Jaekel 		/* note: intentionally not adding ctrl queue to queue list. */
1577add9520fSAndreas Jaekel 
15789fef2cddSAndreas Jaekel 		/* create device node for "tmpqueue" */
15799fef2cddSAndreas Jaekel 		if (ddi_create_minor_node(dip, ZEV_TMPQUEUE_DEVICE_NAME,
15809fef2cddSAndreas Jaekel 		    S_IFCHR, ZEV_TMPQUEUE_DEVICE_MINOR,
15819fef2cddSAndreas Jaekel 		    DDI_PSEUDO, 0) == DDI_FAILURE) {
15829fef2cddSAndreas Jaekel 			goto fail;
15839fef2cddSAndreas Jaekel 		}
15849fef2cddSAndreas Jaekel 
1585add9520fSAndreas Jaekel 		/* default queue */
1586add9520fSAndreas Jaekel 		error = zev_queue_new(&q, dip,
1587add9520fSAndreas Jaekel 				      ZEV_DEFAULT_QUEUE_NAME,
1588add9520fSAndreas Jaekel 				      ZEV_MAX_QUEUE_LEN,
1589add9520fSAndreas Jaekel 				      ZEV_FL_BLOCK_WHILE_QUEUE_FULL|
1590add9520fSAndreas Jaekel 		                      ZEV_FL_PERSISTENT);
1591add9520fSAndreas Jaekel 		if (error)
1592add9520fSAndreas Jaekel 			goto fail;
1593add9520fSAndreas Jaekel 
1594add9520fSAndreas Jaekel 		/* start pollwakeup thread */
1595add9520fSAndreas Jaekel 		zev_wakeup_thread_run = 1;
1596add9520fSAndreas Jaekel 		zev_poll_wakeup_thread = thread_create(NULL, 0,
1597add9520fSAndreas Jaekel 		    zev_poll_wakeup_thread_main, NULL, 0, &p0,
1598add9520fSAndreas Jaekel 		    TS_RUN, minclsyspri);
1599add9520fSAndreas Jaekel 
1600a18c35b9SAndreas Jaekel 		ddi_report_dev(dip);
1601add9520fSAndreas Jaekel 
1602205ed6bfSAndreas Jaekel 		zev_chksum_init();
1603205ed6bfSAndreas Jaekel 
1604add9520fSAndreas Jaekel 		/* switch ZFS event callbacks to zev module callbacks */
1605add9520fSAndreas Jaekel 		rw_enter(&rz_zev_rwlock, RW_WRITER);
1606add9520fSAndreas Jaekel 		rz_zev_callbacks = &zev_callbacks;
1607205ed6bfSAndreas Jaekel 		rz_zev_set_active(B_TRUE);
1608add9520fSAndreas Jaekel 		rw_exit(&rz_zev_rwlock);
1609add9520fSAndreas Jaekel 
1610a18c35b9SAndreas Jaekel 		return (DDI_SUCCESS);
1611a18c35b9SAndreas Jaekel 	case DDI_RESUME:
1612a18c35b9SAndreas Jaekel 		/* suspendeding zev devices should never happen */
1613a18c35b9SAndreas Jaekel 		return (DDI_SUCCESS);
1614a18c35b9SAndreas Jaekel 	default:
1615a18c35b9SAndreas Jaekel 		return (DDI_FAILURE);
1616a18c35b9SAndreas Jaekel 	}
1617add9520fSAndreas Jaekel fail:
1618add9520fSAndreas Jaekel 	cmn_err(CE_WARN, "zev: attach failed");
1619add9520fSAndreas Jaekel 	zev_free_instance(dip);
1620add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
1621add9520fSAndreas Jaekel 	zev_attached = B_FALSE;
1622add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
1623add9520fSAndreas Jaekel 	return (DDI_FAILURE);
1624a18c35b9SAndreas Jaekel }
1625a18c35b9SAndreas Jaekel 
1626548c8b6eSAndreas Jaekel /* ARGSUSED */
1627a18c35b9SAndreas Jaekel static int
zev_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** resultp)1628a18c35b9SAndreas Jaekel zev_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp)
1629a18c35b9SAndreas Jaekel {
1630add9520fSAndreas Jaekel 	minor_t minor;
1631add9520fSAndreas Jaekel 	zev_queue_t *q;
1632add9520fSAndreas Jaekel 
1633add9520fSAndreas Jaekel 	/* arg is dev_t */
1634add9520fSAndreas Jaekel 	minor = getminor((dev_t)arg);
1635add9520fSAndreas Jaekel 	mutex_enter(&zev_mutex);
1636add9520fSAndreas Jaekel 	q = ddi_get_soft_state(statep, minor);
1637add9520fSAndreas Jaekel 	if (q == NULL) {
1638add9520fSAndreas Jaekel 		*resultp = NULL;
1639add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1640add9520fSAndreas Jaekel 		return (DDI_FAILURE);
1641add9520fSAndreas Jaekel 	}
1642add9520fSAndreas Jaekel 
1643a18c35b9SAndreas Jaekel 	switch (infocmd) {
1644a18c35b9SAndreas Jaekel 	case DDI_INFO_DEVT2DEVINFO:
1645add9520fSAndreas Jaekel 		*resultp = q->zq_dip;
1646add9520fSAndreas Jaekel 		break;
1647a18c35b9SAndreas Jaekel 	case DDI_INFO_DEVT2INSTANCE:
1648add9520fSAndreas Jaekel 		*resultp = (void *)(uintptr_t)ddi_get_instance(q->zq_dip);
1649add9520fSAndreas Jaekel 		break;
1650add9520fSAndreas Jaekel 	default:
1651add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1652a18c35b9SAndreas Jaekel 		return (DDI_FAILURE);
1653a18c35b9SAndreas Jaekel 	}
1654add9520fSAndreas Jaekel 	mutex_exit(&zev_mutex);
1655add9520fSAndreas Jaekel 	return (DDI_SUCCESS);
1656a18c35b9SAndreas Jaekel }
1657a18c35b9SAndreas Jaekel 
1658a18c35b9SAndreas Jaekel static struct dev_ops zev_dev_ops = {
1659a18c35b9SAndreas Jaekel 	DEVO_REV,			/* driver build revision */
1660a18c35b9SAndreas Jaekel 	0,				/* driver reference count */
1661a18c35b9SAndreas Jaekel 	zev_getinfo,			/* getinfo */
1662a18c35b9SAndreas Jaekel 	nulldev,			/* identify (obsolete) */
1663a18c35b9SAndreas Jaekel 	nulldev,			/* probe (search for devices) */
1664a18c35b9SAndreas Jaekel 	zev_attach,			/* attach */
1665a18c35b9SAndreas Jaekel 	zev_detach,			/* detach */
1666a18c35b9SAndreas Jaekel 	nodev,				/* reset (obsolete, use quiesce) */
1667a18c35b9SAndreas Jaekel 	&zev_cb_ops,			/* character and block device ops */
1668a18c35b9SAndreas Jaekel 	NULL,				/* bus driver ops */
1669a18c35b9SAndreas Jaekel 	NULL,				/* power management, not needed */
1670a18c35b9SAndreas Jaekel 	ddi_quiesce_not_needed,		/* quiesce */
1671a18c35b9SAndreas Jaekel };
1672a18c35b9SAndreas Jaekel 
1673a18c35b9SAndreas Jaekel static struct modldrv zev_modldrv = {
1674a18c35b9SAndreas Jaekel 	&mod_driverops,			/* all loadable modules use this */
1675e3455c18SAndreas Jaekel 	"ZFS event provider, v"
16761fbd5e10SAndreas Jaekel 		XSTRING(ZEV_MAJOR_VERSION) "."
16771fbd5e10SAndreas Jaekel 		XSTRING(ZEV_MINOR_VERSION),
1678e3455c18SAndreas Jaekel 					/* driver name and version info */
1679a18c35b9SAndreas Jaekel 	&zev_dev_ops			/* ops method pointers */
1680a18c35b9SAndreas Jaekel };
1681a18c35b9SAndreas Jaekel 
1682a18c35b9SAndreas Jaekel static struct modlinkage zev_modlinkage = {
1683a18c35b9SAndreas Jaekel 	MODREV_1,	/* fixed value */
1684a18c35b9SAndreas Jaekel 	{
1685a18c35b9SAndreas Jaekel 		&zev_modldrv,	/* driver linkage structure */
1686a18c35b9SAndreas Jaekel 		NULL		/* list terminator */
1687a18c35b9SAndreas Jaekel 	}
1688a18c35b9SAndreas Jaekel };
1689a18c35b9SAndreas Jaekel 
1690a18c35b9SAndreas Jaekel int
_init(void)1691a18c35b9SAndreas Jaekel _init(void)
1692a18c35b9SAndreas Jaekel {
1693a18c35b9SAndreas Jaekel 	int error;
1694a18c35b9SAndreas Jaekel 
1695add9520fSAndreas Jaekel 	if ((error = ddi_soft_state_init(&statep, sizeof(zev_queue_t), 1)) != 0)
1696a18c35b9SAndreas Jaekel 		return (error);
1697add9520fSAndreas Jaekel 	zev_attached = B_FALSE;
1698add9520fSAndreas Jaekel 
1699add9520fSAndreas Jaekel 	zev_queue_head = NULL;
1700add9520fSAndreas Jaekel 	zev_queue_tail = NULL;
1701add9520fSAndreas Jaekel 	zev_queue_len = 0;
1702add9520fSAndreas Jaekel 	zev_muted_pools_head = NULL;
1703add9520fSAndreas Jaekel 	zev_memory_allocated = 0;
1704add9520fSAndreas Jaekel 	zev_memory_freed = 0;
17056a6a51eeSAndreas Jaekel 	zev_queue_cnt = 0;
17069720aba3SAndreas Jaekel 	zev_have_blocking_queues = 1;
1707a18c35b9SAndreas Jaekel 
1708a18c35b9SAndreas Jaekel 	mutex_init(&zev_mutex, NULL, MUTEX_DRIVER, NULL);
1709a18c35b9SAndreas Jaekel 	cv_init(&zev_condvar, NULL, CV_DRIVER, NULL);
1710a18c35b9SAndreas Jaekel 	rw_init(&zev_pool_list_rwlock, NULL, RW_DRIVER, NULL);
1711888fea18SAndreas Jaekel 	mutex_init(&zev_mark_id_mutex, NULL, MUTEX_DRIVER, NULL);
1712888fea18SAndreas Jaekel 	zev_mark_id = gethrtime();
17136a6a51eeSAndreas Jaekel 	mutex_init(&zev_queue_msg_mutex, NULL, MUTEX_DRIVER, NULL);
1714add9520fSAndreas Jaekel 	zev_msg_sequence_number = gethrtime();
1715a18c35b9SAndreas Jaekel 	bzero(&zev_statistics, sizeof(zev_statistics));
1716add9520fSAndreas Jaekel 	bzero(&zev_pollhead, sizeof(zev_pollhead));
17176a6a51eeSAndreas Jaekel 	bzero(&zev_queues, sizeof(zev_queues));
1718a18c35b9SAndreas Jaekel 	zev_statistics.zev_max_queue_len = ZEV_MAX_QUEUE_LEN;
1719a18c35b9SAndreas Jaekel 	if (zev_ioc_mute_pool("zg0")) {
1720a18c35b9SAndreas Jaekel 		cmn_err(CE_WARN, "zev: could not init mute list");
1721a18c35b9SAndreas Jaekel 		goto FAIL;
1722a18c35b9SAndreas Jaekel 	}
1723a18c35b9SAndreas Jaekel 
1724a18c35b9SAndreas Jaekel 	if ((error = mod_install(&zev_modlinkage)) != 0) {
1725a18c35b9SAndreas Jaekel 		cmn_err(CE_WARN, "zev: could not install module");
1726a18c35b9SAndreas Jaekel 		goto FAIL;
1727a18c35b9SAndreas Jaekel 	}
17281f47b6c0SAndreas Jaekel 
1729a18c35b9SAndreas Jaekel 	return (0);
1730a18c35b9SAndreas Jaekel FAIL:
1731a18c35b9SAndreas Jaekel 	/* free resources */
1732add9520fSAndreas Jaekel 	cmn_err(CE_WARN, "zev: _init failed");
1733a18c35b9SAndreas Jaekel 	mutex_destroy(&zev_mutex);
1734a18c35b9SAndreas Jaekel 	ddi_soft_state_fini(&statep);
1735a18c35b9SAndreas Jaekel 	return (error);
1736a18c35b9SAndreas Jaekel }
1737a18c35b9SAndreas Jaekel 
1738a18c35b9SAndreas Jaekel int
_info(struct modinfo * modinfop)1739a18c35b9SAndreas Jaekel _info(struct modinfo *modinfop)
1740a18c35b9SAndreas Jaekel {
1741a18c35b9SAndreas Jaekel 	return (mod_info(&zev_modlinkage, modinfop));
1742a18c35b9SAndreas Jaekel }
1743a18c35b9SAndreas Jaekel 
1744a18c35b9SAndreas Jaekel int
_fini(void)1745a18c35b9SAndreas Jaekel _fini(void)
1746a18c35b9SAndreas Jaekel {
1747a18c35b9SAndreas Jaekel 	int error = 0;
1748aafc540fSAndreas Jaekel 	zev_msg_t *msg;
1749a18c35b9SAndreas Jaekel 	zev_pool_list_entry_t *pe, *npe;
1750a18c35b9SAndreas Jaekel 
1751a18c35b9SAndreas Jaekel 	mutex_enter(&zev_mutex);
1752add9520fSAndreas Jaekel 	if (zev_attached == B_TRUE) {
1753a18c35b9SAndreas Jaekel 		mutex_exit(&zev_mutex);
1754a18c35b9SAndreas Jaekel 		return (SET_ERROR(EBUSY));
1755a18c35b9SAndreas Jaekel 	}
17566a6a51eeSAndreas Jaekel 	if (zev_queue_cnt != 0) {
1757add9520fSAndreas Jaekel 		/* should never happen */
1758add9520fSAndreas Jaekel 		mutex_exit(&zev_mutex);
1759add9520fSAndreas Jaekel 		return (SET_ERROR(EBUSY));
1760add9520fSAndreas Jaekel 	}
1761add9520fSAndreas Jaekel 
1762add9520fSAndreas Jaekel 	/*
1763add9520fSAndreas Jaekel 	 * avoid deadlock if event list is full: make sure threads currently
1764add9520fSAndreas Jaekel 	 * blocking on the event list can append their event and then release
1765add9520fSAndreas Jaekel 	 * rz_zev_rwlock.  Since there should be no queues left when we
1766add9520fSAndreas Jaekel 	 * reach this point we can simply empty the event list and then
1767add9520fSAndreas Jaekel 	 * wake everybody.
1768add9520fSAndreas Jaekel 	 */
1769add9520fSAndreas Jaekel 	while (zev_queue_head) {
1770add9520fSAndreas Jaekel 		msg = zev_queue_head;
1771add9520fSAndreas Jaekel 		zev_queue_head = msg->next;
1772205ed6bfSAndreas Jaekel 		zev_free(msg, sizeof(*msg) + msg->size);
1773add9520fSAndreas Jaekel 	}
1774add9520fSAndreas Jaekel 	cv_broadcast(&zev_condvar);
1775a18c35b9SAndreas Jaekel 	mutex_exit(&zev_mutex);
1776a18c35b9SAndreas Jaekel 
1777add9520fSAndreas Jaekel 	/* switch ZFS event callbacks back to default (again) */
17781f47b6c0SAndreas Jaekel 	rw_enter(&rz_zev_rwlock, RW_WRITER);
17791f47b6c0SAndreas Jaekel 	rz_zev_callbacks = rz_zev_default_callbacks;
1780205ed6bfSAndreas Jaekel 	rz_zev_set_active(B_FALSE);
17811f47b6c0SAndreas Jaekel 	rw_exit(&rz_zev_rwlock);
17821f47b6c0SAndreas Jaekel 
1783a18c35b9SAndreas Jaekel 	/* no thread is inside of the callbacks anymore.  Safe to remove. */
1784add9520fSAndreas Jaekel 
1785add9520fSAndreas Jaekel 	/* unload module callbacks */
1786a18c35b9SAndreas Jaekel 	if ((error = mod_remove(&zev_modlinkage)) != 0) {
1787a18c35b9SAndreas Jaekel 		cmn_err(CE_WARN, "mod_remove failed: %d", error);
1788a18c35b9SAndreas Jaekel 		return (error);
1789a18c35b9SAndreas Jaekel 	}
1790a18c35b9SAndreas Jaekel 
1791a18c35b9SAndreas Jaekel 	/* free resources */
1792a18c35b9SAndreas Jaekel 	mutex_enter(&zev_mutex);
1793aafc540fSAndreas Jaekel 	while (zev_queue_head) {
1794aafc540fSAndreas Jaekel 		msg = zev_queue_head;
1795aafc540fSAndreas Jaekel 		zev_queue_head = msg->next;
1796205ed6bfSAndreas Jaekel 		zev_free(msg, sizeof(*msg) + msg->size);
1797a18c35b9SAndreas Jaekel 	}
1798a18c35b9SAndreas Jaekel 	mutex_exit(&zev_mutex);
1799a18c35b9SAndreas Jaekel 	rw_enter(&zev_pool_list_rwlock, RW_WRITER);
1800a18c35b9SAndreas Jaekel 	pe = zev_muted_pools_head;
1801a18c35b9SAndreas Jaekel 	while (pe) {
1802a18c35b9SAndreas Jaekel 		npe = pe;
1803a18c35b9SAndreas Jaekel 		pe = pe->next;
1804205ed6bfSAndreas Jaekel 		zev_free(npe, sizeof(*npe));
1805a18c35b9SAndreas Jaekel 	}
1806a18c35b9SAndreas Jaekel 	rw_exit(&zev_pool_list_rwlock);
1807a18c35b9SAndreas Jaekel 	ddi_soft_state_fini(&statep);
1808a18c35b9SAndreas Jaekel 	rw_destroy(&zev_pool_list_rwlock);
1809a18c35b9SAndreas Jaekel 	cv_destroy(&zev_condvar);
1810a18c35b9SAndreas Jaekel 	mutex_destroy(&zev_mutex);
1811888fea18SAndreas Jaekel 	mutex_destroy(&zev_mark_id_mutex);
18126a6a51eeSAndreas Jaekel 	mutex_destroy(&zev_queue_msg_mutex);
1813a18c35b9SAndreas Jaekel 
1814a18c35b9SAndreas Jaekel 	return (0);
1815a18c35b9SAndreas Jaekel }
1816a18c35b9SAndreas Jaekel 
1817