xref: /titanic_52/usr/src/uts/common/fs/zev/zev.c (revision df8caf2d21b03a898b9330fb7beaa3093954f4ce)
12bb8e5e2SAndreas Jaekel #include <sys/modctl.h>
22bb8e5e2SAndreas Jaekel #include <sys/ddi.h>
32bb8e5e2SAndreas Jaekel #include <sys/sunddi.h>
42bb8e5e2SAndreas Jaekel #include <sys/conf.h>
52bb8e5e2SAndreas Jaekel #include <sys/devops.h>
62bb8e5e2SAndreas Jaekel #include <sys/stat.h>
72bb8e5e2SAndreas Jaekel #include <sys/fs/zev.h>
82bb8e5e2SAndreas Jaekel #include <sys/zev_callbacks.h>
95e286361SAndreas Jaekel #include <sys/zev_checksums.h>
10c035b1e8SAndreas Jaekel #include <sys/zfs_znode.h>
11c035b1e8SAndreas Jaekel #include <sys/time.h>
12c035b1e8SAndreas Jaekel #include <sys/sa.h>
13c035b1e8SAndreas Jaekel #include <sys/zap.h>
14aea44f72SAndreas Jaekel #include <sys/time.h>
15f5ef1346SAndreas Jaekel #include <sys/fs/dv_node.h>
162bb8e5e2SAndreas Jaekel 
17e9a5e479SAndreas Jaekel #define	OFFSETOF(s, m)		((size_t)(&(((s *)0)->m)))
18e9a5e479SAndreas Jaekel 
19dc1d6f1aSAndreas Jaekel #define XSTRING(x)	STRING(x)
20c62d8367SAndreas Jaekel #define STRING(x)	#x
21c62d8367SAndreas Jaekel 
22e9a5e479SAndreas Jaekel #define ZEV_DEFAULT_QUEUE_NAME		"beaver"
23e9a5e479SAndreas Jaekel #define ZEV_CONTROL_DEVICE_MINOR	0
246450d95eSAndreas Jaekel #define ZEV_TMPQUEUE_DEVICE_MINOR	1
256450d95eSAndreas Jaekel #define ZEV_MINOR_MIN			(ZEV_TMPQUEUE_DEVICE_MINOR + 1)
26e9a5e479SAndreas Jaekel #define ZEV_MINOR_MAX			(ZEV_MINOR_MIN + ZEV_MAX_QUEUES - 1)
27e9a5e479SAndreas Jaekel 
28e9a5e479SAndreas Jaekel typedef struct zev_queue {
29e9a5e479SAndreas Jaekel 	char			zq_name[ZEV_MAX_QUEUE_NAME_LEN+1];
30e9a5e479SAndreas Jaekel 	minor_t			zq_minor_number;
31e9a5e479SAndreas Jaekel 	dev_info_t		*zq_dip;
32e9a5e479SAndreas Jaekel 	struct pollhead		zq_pollhead;
33e9a5e479SAndreas Jaekel 	uint64_t		zq_bytes_read;
34e9a5e479SAndreas Jaekel 	uint64_t		zq_events_read;
35e9a5e479SAndreas Jaekel 	uint64_t		zq_bytes_discarded;
36e9a5e479SAndreas Jaekel 	uint64_t		zq_events_discarded;
37e9a5e479SAndreas Jaekel 	uint64_t		zq_bytes_total;
38e9a5e479SAndreas Jaekel 	uint64_t		zq_events_total;
39e9a5e479SAndreas Jaekel 	uint64_t		zq_wakeup_threshold;
40e9a5e479SAndreas Jaekel 	uint16_t		zq_flags;
41e9a5e479SAndreas Jaekel 	uint16_t		zq_need_wakeup;
42e9a5e479SAndreas Jaekel 	/* protected by zev_mutex */
43e9a5e479SAndreas Jaekel 	int			zq_refcnt;
44e9a5e479SAndreas Jaekel 	uint64_t		zq_queue_len;
45e9a5e479SAndreas Jaekel 	uint64_t		zq_queue_messages;
46e9a5e479SAndreas Jaekel 	uint64_t		zq_max_queue_len;
47e9a5e479SAndreas Jaekel 	zev_msg_t		*zq_oldest;
48e9a5e479SAndreas Jaekel 	boolean_t		zq_busy;
49e9a5e479SAndreas Jaekel 	boolean_t		zq_to_be_removed;
50e9a5e479SAndreas Jaekel 	zev_statistics_t	zq_statistics;
514ca7dd5eSAndreas Jaekel 	kcondvar_t		zq_condvar;
52e9a5e479SAndreas Jaekel } zev_queue_t;
532bb8e5e2SAndreas Jaekel 
542bb8e5e2SAndreas Jaekel static void		*statep;
552bb8e5e2SAndreas Jaekel struct pollhead		zev_pollhead;
562bb8e5e2SAndreas Jaekel 
572bb8e5e2SAndreas Jaekel kmutex_t		zev_mutex;
582bb8e5e2SAndreas Jaekel kcondvar_t		zev_condvar;
594ca7dd5eSAndreas Jaekel kmutex_t		zev_queue_msg_mutex;
602bb8e5e2SAndreas Jaekel krwlock_t		zev_pool_list_rwlock;
612bb8e5e2SAndreas Jaekel static zev_statistics_t	zev_statistics;
62e9a5e479SAndreas Jaekel static boolean_t	zev_attached;
6301c2c787SAndreas Jaekel static kmutex_t		zev_mark_id_mutex;
6401c2c787SAndreas Jaekel static uint64_t		zev_mark_id = 0;
652bb8e5e2SAndreas Jaekel 
66e9a5e479SAndreas Jaekel static uint64_t		zev_msg_sequence_number = 0;
674ca7dd5eSAndreas Jaekel static zev_queue_t	*zev_queues[ZEV_MAX_QUEUES];
684ca7dd5eSAndreas Jaekel static int		zev_queue_cnt = 0;
6907bff397SAndreas Jaekel static int		zev_have_blocking_queues = 1;
706450d95eSAndreas Jaekel static int		zev_tmpqueue_num = 0;
71e9a5e479SAndreas Jaekel 
72e9a5e479SAndreas Jaekel uint64_t	zev_memory_allocated = 0;
73e9a5e479SAndreas Jaekel uint64_t	zev_memory_freed = 0;
74e9a5e479SAndreas Jaekel 
752bb8e5e2SAndreas Jaekel /*
762bb8e5e2SAndreas Jaekel  * The longest potential message is from zev_zfs_mount() and
772bb8e5e2SAndreas Jaekel  * contains the mountpoint, which might be close to MAXPATHLEN bytes long.
782bb8e5e2SAndreas Jaekel  *
792bb8e5e2SAndreas Jaekel  * Another candidate is zev_znode_rename_cb() and contains three inode
802bb8e5e2SAndreas Jaekel  * numbers and two filenames of up to MAXNAMELEN bytes each.
812bb8e5e2SAndreas Jaekel  */
822bb8e5e2SAndreas Jaekel #define ZEV_MAX_MESSAGE_LEN	4096
832bb8e5e2SAndreas Jaekel 
849193e9c2SAndreas Jaekel static zev_msg_t *zev_queue_head = NULL;
859193e9c2SAndreas Jaekel static zev_msg_t *zev_queue_tail = NULL;
869193e9c2SAndreas Jaekel static uint64_t zev_queue_len = 0;
879193e9c2SAndreas Jaekel 
882bb8e5e2SAndreas Jaekel 
892bb8e5e2SAndreas Jaekel typedef struct zev_pool_list_entry {
902bb8e5e2SAndreas Jaekel 	struct zev_pool_list_entry	*next;
912bb8e5e2SAndreas Jaekel 	char				name[MAXPATHLEN];
922bb8e5e2SAndreas Jaekel } zev_pool_list_entry_t;
932bb8e5e2SAndreas Jaekel 
942bb8e5e2SAndreas Jaekel static zev_pool_list_entry_t *zev_muted_pools_head = NULL;
952bb8e5e2SAndreas Jaekel 
96e9a5e479SAndreas Jaekel static volatile int zev_wakeup_thread_run = 1;
97e9a5e479SAndreas Jaekel static kthread_t *zev_poll_wakeup_thread = NULL;
98e9a5e479SAndreas Jaekel 
995e286361SAndreas Jaekel void *
1005e286361SAndreas Jaekel zev_alloc(ssize_t sz)
1015e286361SAndreas Jaekel {
1025e286361SAndreas Jaekel 	ZEV_MEM_ADD(sz);
1035e286361SAndreas Jaekel 	return kmem_alloc(sz, KM_SLEEP);
1045e286361SAndreas Jaekel }
1055e286361SAndreas Jaekel 
1065e286361SAndreas Jaekel void *
1075e286361SAndreas Jaekel zev_zalloc(ssize_t sz)
1085e286361SAndreas Jaekel {
1095e286361SAndreas Jaekel 	ZEV_MEM_ADD(sz);
1105e286361SAndreas Jaekel 	return kmem_zalloc(sz, KM_SLEEP);
1115e286361SAndreas Jaekel }
1125e286361SAndreas Jaekel 
1135e286361SAndreas Jaekel void
1145e286361SAndreas Jaekel zev_free(void *ptr, ssize_t sz)
1155e286361SAndreas Jaekel {
1165e286361SAndreas Jaekel 	ZEV_MEM_SUB(sz);						\
1175e286361SAndreas Jaekel 	kmem_free(ptr, sz);
1185e286361SAndreas Jaekel }
1195e286361SAndreas Jaekel 
12007bff397SAndreas Jaekel /* must be called with zev_mutex held */
12107bff397SAndreas Jaekel static void
12207bff397SAndreas Jaekel zev_update_blockflag(void)
12307bff397SAndreas Jaekel {
12407bff397SAndreas Jaekel 	zev_queue_t *q;
12507bff397SAndreas Jaekel 	int had_blocking_queues;
12607bff397SAndreas Jaekel 	int i;
12707bff397SAndreas Jaekel 
12807bff397SAndreas Jaekel 	had_blocking_queues = zev_have_blocking_queues;
12907bff397SAndreas Jaekel 
13007bff397SAndreas Jaekel 	/* do we still have blocking queues? */
13107bff397SAndreas Jaekel 	zev_have_blocking_queues = 0;
13207bff397SAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
13307bff397SAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
13407bff397SAndreas Jaekel 		if (!q)
13507bff397SAndreas Jaekel 			continue;
13607bff397SAndreas Jaekel 		if (q->zq_flags & ZEV_FL_BLOCK_WHILE_QUEUE_FULL) {
13707bff397SAndreas Jaekel 			zev_have_blocking_queues = 1;
13807bff397SAndreas Jaekel 			break;
13907bff397SAndreas Jaekel 		}
14007bff397SAndreas Jaekel 	}
14107bff397SAndreas Jaekel 	/* no blocking queues */
14207bff397SAndreas Jaekel 	if (had_blocking_queues)
14307bff397SAndreas Jaekel 		cv_broadcast(&zev_condvar);
14407bff397SAndreas Jaekel }
14507bff397SAndreas Jaekel 
146e9a5e479SAndreas Jaekel int
147e9a5e479SAndreas Jaekel zev_queue_cmp(const void *a, const void *b)
148e9a5e479SAndreas Jaekel {
149e9a5e479SAndreas Jaekel 	const zev_queue_t *qa = a;
150e9a5e479SAndreas Jaekel 	const zev_queue_t *qb = b;
151e9a5e479SAndreas Jaekel 	if (qa->zq_minor_number > qb->zq_minor_number)
152e9a5e479SAndreas Jaekel 		return 1;
153e9a5e479SAndreas Jaekel 	if (qa->zq_minor_number < qb->zq_minor_number)
154e9a5e479SAndreas Jaekel 		return -1;
155e9a5e479SAndreas Jaekel 	return 0;
156e9a5e479SAndreas Jaekel }
157e9a5e479SAndreas Jaekel 
158e9a5e479SAndreas Jaekel /* must be called with zev_mutex held */
159e9a5e479SAndreas Jaekel void
160e9a5e479SAndreas Jaekel zev_queue_trim(void)
161e9a5e479SAndreas Jaekel {
162e9a5e479SAndreas Jaekel 	zev_msg_t *m;
163e9a5e479SAndreas Jaekel 	uint64_t oldest_message;
164e9a5e479SAndreas Jaekel 	zev_queue_t *q;
1654ca7dd5eSAndreas Jaekel 	int i;
166e9a5e479SAndreas Jaekel 
167e9a5e479SAndreas Jaekel 	if (!zev_queue_tail)
168e9a5e479SAndreas Jaekel 		return;
169e9a5e479SAndreas Jaekel 
170e9a5e479SAndreas Jaekel 	oldest_message = zev_queue_tail->seq + 1;  /* does not exist, yet. */
1714ca7dd5eSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
1724ca7dd5eSAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
1734ca7dd5eSAndreas Jaekel 		if (q == NULL)
1744ca7dd5eSAndreas Jaekel 			continue;
175e9a5e479SAndreas Jaekel 		if (!q->zq_oldest)
176e9a5e479SAndreas Jaekel 			continue;
177e9a5e479SAndreas Jaekel 		if (oldest_message > q->zq_oldest->seq)
178e9a5e479SAndreas Jaekel 			oldest_message = q->zq_oldest->seq;
179e9a5e479SAndreas Jaekel 	}
180e9a5e479SAndreas Jaekel 
181e9a5e479SAndreas Jaekel 	/* remove msgs between oldest_message and zev_queue_head */
182e9a5e479SAndreas Jaekel 	while(zev_queue_head && (oldest_message > zev_queue_head->seq)) {
183e9a5e479SAndreas Jaekel 		m = zev_queue_head;
184e9a5e479SAndreas Jaekel 		zev_queue_head = m->next;
185e9a5e479SAndreas Jaekel 		if (zev_queue_head == NULL) {
186e9a5e479SAndreas Jaekel 			zev_queue_tail = NULL;
187e9a5e479SAndreas Jaekel 		} else {
188e9a5e479SAndreas Jaekel 			zev_queue_head->prev = NULL;
189e9a5e479SAndreas Jaekel 		}
190e9a5e479SAndreas Jaekel 		if (m->read == 0) {
191e9a5e479SAndreas Jaekel 			zev_statistics.zev_bytes_discarded += m->size;
192e9a5e479SAndreas Jaekel 			zev_statistics.zev_cnt_discarded_events++;
193e9a5e479SAndreas Jaekel 		}
194e9a5e479SAndreas Jaekel 		zev_statistics.zev_queue_len -= m->size;
195e9a5e479SAndreas Jaekel 		zev_queue_len--;
1965e286361SAndreas Jaekel 		zev_free(m, sizeof(*m) + m->size);
197e9a5e479SAndreas Jaekel 	}
198e9a5e479SAndreas Jaekel }
199e9a5e479SAndreas Jaekel 
200e9a5e479SAndreas Jaekel /* must be called with zev_mutex held */
201e9a5e479SAndreas Jaekel static void
202e9a5e479SAndreas Jaekel zev_queue_hold(zev_queue_t *q)
203e9a5e479SAndreas Jaekel {
204e9a5e479SAndreas Jaekel 	q->zq_refcnt++;
205e9a5e479SAndreas Jaekel }
206e9a5e479SAndreas Jaekel 
207e9a5e479SAndreas Jaekel /* must be called with zev_mutex held */
208e9a5e479SAndreas Jaekel static void
209e9a5e479SAndreas Jaekel zev_queue_release(zev_queue_t *q)
210e9a5e479SAndreas Jaekel {
211e9a5e479SAndreas Jaekel 	q->zq_refcnt--;
212e9a5e479SAndreas Jaekel 	if (q->zq_refcnt > 0)
213e9a5e479SAndreas Jaekel 		return;
214e9a5e479SAndreas Jaekel 
215e9a5e479SAndreas Jaekel 	ASSERT(q->zq_busy == B_FALSE);
216e9a5e479SAndreas Jaekel 
217e9a5e479SAndreas Jaekel 	/* persistent queues will not be removed */
218e9a5e479SAndreas Jaekel 	if ((q->zq_flags & ZEV_FL_PERSISTENT) != 0)
219e9a5e479SAndreas Jaekel 		return;
220e9a5e479SAndreas Jaekel 
221e9a5e479SAndreas Jaekel 	/* remove queue from queue list */
2224ca7dd5eSAndreas Jaekel 	zev_queues[q->zq_minor_number - ZEV_MINOR_MIN] = NULL;
223e9a5e479SAndreas Jaekel 
224e9a5e479SAndreas Jaekel 	/* discard messages that no queue references anymore */
225e9a5e479SAndreas Jaekel 	zev_queue_trim();
226e9a5e479SAndreas Jaekel 
2274ca7dd5eSAndreas Jaekel 	cv_destroy(&q->zq_condvar);
228e9a5e479SAndreas Jaekel 	ddi_remove_minor_node(q->zq_dip, q->zq_name);
229d5b96be3SAndreas Jaekel 	devfs_clean(ddi_root_node() ? ddi_root_node() : q->zq_dip,
230d5b96be3SAndreas Jaekel 	            NULL, DV_CLEAN_FORCE);
231e9a5e479SAndreas Jaekel 	ddi_soft_state_free(statep, q->zq_minor_number);
232e9a5e479SAndreas Jaekel 	ZEV_MEM_SUB(sizeof(zev_queue_t));
2334ca7dd5eSAndreas Jaekel 	zev_queue_cnt--;
23407bff397SAndreas Jaekel 	zev_update_blockflag();
235e9a5e479SAndreas Jaekel }
236e9a5e479SAndreas Jaekel 
237e9a5e479SAndreas Jaekel int
238e9a5e479SAndreas Jaekel zev_queue_new(zev_queue_t **queue,
239e9a5e479SAndreas Jaekel               dev_info_t *dip,
240e9a5e479SAndreas Jaekel               char *name,
241e9a5e479SAndreas Jaekel               uint64_t max_queue_len,
242e9a5e479SAndreas Jaekel               uint16_t flags)
243e9a5e479SAndreas Jaekel {
244e9a5e479SAndreas Jaekel 	zev_queue_t *q;
245e9a5e479SAndreas Jaekel 	zev_queue_t *tmp;
246e9a5e479SAndreas Jaekel 	zev_msg_t *msg;
247e9a5e479SAndreas Jaekel 	int name_exists = 0;
248e9a5e479SAndreas Jaekel 	minor_t minor;
249e9a5e479SAndreas Jaekel 	char *p;
2504ca7dd5eSAndreas Jaekel 	int i;
251e9a5e479SAndreas Jaekel 
252e9a5e479SAndreas Jaekel 	if (max_queue_len > ZEV_MAX_QUEUE_LEN)
253e9a5e479SAndreas Jaekel 		return EINVAL;
254e9a5e479SAndreas Jaekel 	if (max_queue_len == 0)
255e9a5e479SAndreas Jaekel 		max_queue_len = ZEV_MAX_QUEUE_LEN;
256e9a5e479SAndreas Jaekel 	if (!strcmp(name, ZEV_CONTROL_DEVICE_NAME))
257e9a5e479SAndreas Jaekel 		return EINVAL;
258e9a5e479SAndreas Jaekel 	for (p = name; *p; p++) {
259e9a5e479SAndreas Jaekel 		if (*p >= 'a' && *p <= 'z')
260e9a5e479SAndreas Jaekel 			continue;
261e9a5e479SAndreas Jaekel 		if (*p >= '0' && *p <= '9')
262e9a5e479SAndreas Jaekel 			continue;
263e9a5e479SAndreas Jaekel 		if (*p == '.')
264e9a5e479SAndreas Jaekel 			continue;
265e9a5e479SAndreas Jaekel 		return EINVAL;
266e9a5e479SAndreas Jaekel 	}
267e9a5e479SAndreas Jaekel 
268e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
269e9a5e479SAndreas Jaekel 
270e9a5e479SAndreas Jaekel 	/* find free minor number.*/
271e9a5e479SAndreas Jaekel 	/* if this were a frequent operation we'd have a free-minor list */
2724ca7dd5eSAndreas Jaekel 	for (minor = ZEV_MINOR_MIN; minor <= ZEV_MINOR_MAX; minor++) {
2734ca7dd5eSAndreas Jaekel 		tmp = zev_queues[minor - ZEV_MINOR_MIN];
2744ca7dd5eSAndreas Jaekel 		if (tmp == NULL)
275e9a5e479SAndreas Jaekel 			break;
276e9a5e479SAndreas Jaekel 	}
2774ca7dd5eSAndreas Jaekel 	if (tmp) {
278e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
279e9a5e479SAndreas Jaekel 		return ENOSPC;
280e9a5e479SAndreas Jaekel 	}
281e9a5e479SAndreas Jaekel 
282e9a5e479SAndreas Jaekel 	if (ddi_soft_state_zalloc(statep, minor) != DDI_SUCCESS) {
283e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
284e9a5e479SAndreas Jaekel 		return ENOSPC;
285e9a5e479SAndreas Jaekel 	}
286e9a5e479SAndreas Jaekel 	ZEV_MEM_ADD(sizeof(zev_queue_t));
287e9a5e479SAndreas Jaekel 
288e9a5e479SAndreas Jaekel 	q = ddi_get_soft_state(statep, minor);
289e9a5e479SAndreas Jaekel 	memset(q, 0, sizeof(*q));
290e9a5e479SAndreas Jaekel 	strncpy(q->zq_name, name, ZEV_MAX_QUEUE_NAME_LEN);
291e9a5e479SAndreas Jaekel 	q->zq_name[ZEV_MAX_QUEUE_NAME_LEN] = '\0';
292e9a5e479SAndreas Jaekel 	q->zq_max_queue_len = max_queue_len;
2934ca7dd5eSAndreas Jaekel 	q->zq_wakeup_threshold = ZEV_DEFAULT_POLL_WAKEUP_QUEUE_LEN;
294e9a5e479SAndreas Jaekel 	q->zq_flags = flags;
295e9a5e479SAndreas Jaekel 	q->zq_refcnt = 1;
296e9a5e479SAndreas Jaekel 	q->zq_dip = dip;
297e9a5e479SAndreas Jaekel 	q->zq_minor_number = minor;
2984ca7dd5eSAndreas Jaekel 	cv_init(&q->zq_condvar, NULL, CV_DRIVER, NULL);
299e9a5e479SAndreas Jaekel 
300e9a5e479SAndreas Jaekel 	/* insert into queue list */
3014ca7dd5eSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
302e9a5e479SAndreas Jaekel 		/* if this were a frequent operation we'd have a name tree */
3034ca7dd5eSAndreas Jaekel 		if (zev_queues[i - ZEV_MINOR_MIN] == NULL)
3044ca7dd5eSAndreas Jaekel 			continue;
3054ca7dd5eSAndreas Jaekel 		if (!strcmp(q->zq_name, zev_queues[i-ZEV_MINOR_MIN]->zq_name)) {
306e9a5e479SAndreas Jaekel 			name_exists = 1;
307e9a5e479SAndreas Jaekel 			break;
308e9a5e479SAndreas Jaekel 		}
309e9a5e479SAndreas Jaekel 	}
310e9a5e479SAndreas Jaekel 	if (name_exists) {
311e9a5e479SAndreas Jaekel 		ddi_soft_state_free(statep, minor);
312e9a5e479SAndreas Jaekel 		ZEV_MEM_SUB(sizeof(zev_queue_t));
313e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
314e9a5e479SAndreas Jaekel 		return EEXIST;
315e9a5e479SAndreas Jaekel 	}
3164ca7dd5eSAndreas Jaekel 	zev_queues[minor - ZEV_MINOR_MIN] = q;
3174ca7dd5eSAndreas Jaekel 	zev_queue_cnt++;
318e9a5e479SAndreas Jaekel 
319e9a5e479SAndreas Jaekel 	/* calculate current queue len and find head and tail */
320b434d29cSAndreas Jaekel 	if (!(q->zq_flags & ZEV_FL_INITIALLY_EMPTY)) {
321e9a5e479SAndreas Jaekel 		q->zq_oldest = zev_queue_tail;
322e9a5e479SAndreas Jaekel 		msg = zev_queue_tail;
323b434d29cSAndreas Jaekel 		while ((msg) && (q->zq_queue_len < q->zq_max_queue_len)) {
324e9a5e479SAndreas Jaekel 			q->zq_queue_len += msg->size;
325e9a5e479SAndreas Jaekel 			q->zq_queue_messages++;
326e9a5e479SAndreas Jaekel 			q->zq_oldest = msg;
327e9a5e479SAndreas Jaekel 			msg = msg->prev;
328e9a5e479SAndreas Jaekel 		}
329b434d29cSAndreas Jaekel 	}
330e9a5e479SAndreas Jaekel 
331566cb09fSAndreas Jaekel 	zev_update_blockflag();
332566cb09fSAndreas Jaekel 
333e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
334e9a5e479SAndreas Jaekel 
335e9a5e479SAndreas Jaekel 	if (ddi_create_minor_node(dip, name,
336e9a5e479SAndreas Jaekel 	    S_IFCHR, minor, DDI_PSEUDO, 0) == DDI_FAILURE) {
337e9a5e479SAndreas Jaekel 		mutex_enter(&zev_mutex);
3384ca7dd5eSAndreas Jaekel 		zev_queues[minor - ZEV_MINOR_MIN] = NULL;
3394ca7dd5eSAndreas Jaekel 		zev_queue_cnt--;
340e9a5e479SAndreas Jaekel 		ddi_soft_state_free(statep, minor);
341e9a5e479SAndreas Jaekel 		ZEV_MEM_SUB(sizeof(zev_queue_t));
342566cb09fSAndreas Jaekel 		zev_update_blockflag();
343e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
344e9a5e479SAndreas Jaekel 		return EFAULT;
345e9a5e479SAndreas Jaekel 	}
346e9a5e479SAndreas Jaekel 
347e9a5e479SAndreas Jaekel 	*queue = q;
348e9a5e479SAndreas Jaekel 	return 0;
349e9a5e479SAndreas Jaekel }
350e9a5e479SAndreas Jaekel 
351a052d397SAndreas Jaekel /*
352a052d397SAndreas Jaekel  * poll() wakeup thread.  Used to check periodically whether we have
353a052d397SAndreas Jaekel  * bytes left in the queue that have not yet been made into a
354a052d397SAndreas Jaekel  * pollwakeup() call.  This is meant to insure a maximum waiting
355a052d397SAndreas Jaekel  * time until an event is presented as a poll wakeup, while at
356a052d397SAndreas Jaekel  * the same time not making every single event into a poll wakeup
357a052d397SAndreas Jaekel  * of it's own.
358a052d397SAndreas Jaekel  */
359a052d397SAndreas Jaekel 
360a052d397SAndreas Jaekel static void
3614ca7dd5eSAndreas Jaekel zev_poll_wakeup(boolean_t flush_all)
362a052d397SAndreas Jaekel {
363e9a5e479SAndreas Jaekel 	zev_queue_t *q;
3644ca7dd5eSAndreas Jaekel 	int i;
365e9a5e479SAndreas Jaekel 
3664ca7dd5eSAndreas Jaekel 	/*
3674ca7dd5eSAndreas Jaekel 	 * This loop works with hold() and release() because
3684ca7dd5eSAndreas Jaekel 	 * pollwakeup() requires us to release our locks before calling it.
3694ca7dd5eSAndreas Jaekel 	 *
3704ca7dd5eSAndreas Jaekel 	 * from pollwakeup(9F):
3714ca7dd5eSAndreas Jaekel 	 *
3724ca7dd5eSAndreas Jaekel 	 *   "Driver defined locks should not be held across calls
3734ca7dd5eSAndreas Jaekel 	 *    to this function."
3744ca7dd5eSAndreas Jaekel 	 */
375e9a5e479SAndreas Jaekel 
376e9a5e479SAndreas Jaekel 	/* wake up threads for each individual queue */
377a052d397SAndreas Jaekel 	mutex_enter(&zev_mutex);
3784ca7dd5eSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
3794ca7dd5eSAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
3804ca7dd5eSAndreas Jaekel 		if (q == NULL)
3814ca7dd5eSAndreas Jaekel 			continue;
3824ca7dd5eSAndreas Jaekel 		if (!q->zq_busy)
3834ca7dd5eSAndreas Jaekel 			continue;
3844ca7dd5eSAndreas Jaekel 		if (!q->zq_queue_len)
3854ca7dd5eSAndreas Jaekel 			continue;
3864ca7dd5eSAndreas Jaekel 		if ((flush_all) ||
3874ca7dd5eSAndreas Jaekel 		    (q->zq_queue_len > q->zq_wakeup_threshold)) {
388e9a5e479SAndreas Jaekel 			zev_queue_hold(q);
3894ca7dd5eSAndreas Jaekel 			mutex_exit(&zev_mutex);
3904ca7dd5eSAndreas Jaekel 			pollwakeup(&q->zq_pollhead, POLLIN);
3914ca7dd5eSAndreas Jaekel 			mutex_enter(&zev_mutex);
3924ca7dd5eSAndreas Jaekel 			zev_queue_release(q);
393e9a5e479SAndreas Jaekel 		}
394e9a5e479SAndreas Jaekel 	}
395a052d397SAndreas Jaekel 	mutex_exit(&zev_mutex);
396e9a5e479SAndreas Jaekel }
3974ca7dd5eSAndreas Jaekel 
3984ca7dd5eSAndreas Jaekel static void
3994ca7dd5eSAndreas Jaekel zev_poll_wakeup_thread_main(void)
4004ca7dd5eSAndreas Jaekel {
4014ca7dd5eSAndreas Jaekel 	while (zev_wakeup_thread_run) {
4024ca7dd5eSAndreas Jaekel 		delay(drv_usectohz(100 * 1000)); /* sleep 100ms */
4034ca7dd5eSAndreas Jaekel 
4044ca7dd5eSAndreas Jaekel 		zev_poll_wakeup(B_TRUE);
405a052d397SAndreas Jaekel 	}
406a052d397SAndreas Jaekel 	thread_exit();
407a052d397SAndreas Jaekel }
408a052d397SAndreas Jaekel 
4092bb8e5e2SAndreas Jaekel static int
4102bb8e5e2SAndreas Jaekel zev_ioc_mute_pool(char *poolname)
4112bb8e5e2SAndreas Jaekel {
4122bb8e5e2SAndreas Jaekel 	zev_pool_list_entry_t *pe;
4132bb8e5e2SAndreas Jaekel 	rw_enter(&zev_pool_list_rwlock, RW_WRITER);
4142bb8e5e2SAndreas Jaekel 	/* pool already muted? */
4152bb8e5e2SAndreas Jaekel 	for (pe=zev_muted_pools_head; pe; pe=pe->next) {
4162bb8e5e2SAndreas Jaekel 		if (!strcmp(pe->name, poolname)) {
4172bb8e5e2SAndreas Jaekel 			rw_exit(&zev_pool_list_rwlock);
4182bb8e5e2SAndreas Jaekel 			return EEXIST;
4192bb8e5e2SAndreas Jaekel 		}
4202bb8e5e2SAndreas Jaekel 	}
4215e286361SAndreas Jaekel 	pe = zev_zalloc(sizeof(*pe));
4222bb8e5e2SAndreas Jaekel 	if (!pe) {
4232bb8e5e2SAndreas Jaekel 		rw_exit(&zev_pool_list_rwlock);
4242bb8e5e2SAndreas Jaekel 		return ENOMEM;
4252bb8e5e2SAndreas Jaekel 	}
426aea44f72SAndreas Jaekel 	(void) strncpy(pe->name, poolname, sizeof(pe->name));
4272bb8e5e2SAndreas Jaekel 	pe->next = zev_muted_pools_head;
4282bb8e5e2SAndreas Jaekel 	zev_muted_pools_head = pe;
4292bb8e5e2SAndreas Jaekel 	rw_exit(&zev_pool_list_rwlock);
4302bb8e5e2SAndreas Jaekel 	return (0);
4312bb8e5e2SAndreas Jaekel }
4322bb8e5e2SAndreas Jaekel 
4332bb8e5e2SAndreas Jaekel static int
4342bb8e5e2SAndreas Jaekel zev_ioc_unmute_pool(char *poolname)
4352bb8e5e2SAndreas Jaekel {
4362bb8e5e2SAndreas Jaekel 	zev_pool_list_entry_t *pe, *peprev;
4374ca7dd5eSAndreas Jaekel 
4382bb8e5e2SAndreas Jaekel 	rw_enter(&zev_pool_list_rwlock, RW_WRITER);
4392bb8e5e2SAndreas Jaekel 	/* pool muted? */
4402bb8e5e2SAndreas Jaekel 	peprev = NULL;
4412bb8e5e2SAndreas Jaekel 	for (pe=zev_muted_pools_head; pe; pe=pe->next) {
4424ca7dd5eSAndreas Jaekel 		if (!strcmp(pe->name, poolname))
4434ca7dd5eSAndreas Jaekel 			break;
4442bb8e5e2SAndreas Jaekel 		peprev = pe;
4452bb8e5e2SAndreas Jaekel 	}
4464ca7dd5eSAndreas Jaekel 	if (pe) {
4472bb8e5e2SAndreas Jaekel 		rw_exit(&zev_pool_list_rwlock);
4482bb8e5e2SAndreas Jaekel 		return ENOENT;
4494ca7dd5eSAndreas Jaekel 	}
4504ca7dd5eSAndreas Jaekel 
4512bb8e5e2SAndreas Jaekel 	if (peprev != NULL) {
4522bb8e5e2SAndreas Jaekel 		peprev->next = pe->next;
4532bb8e5e2SAndreas Jaekel 	} else {
4542bb8e5e2SAndreas Jaekel 		zev_muted_pools_head = pe->next;
4552bb8e5e2SAndreas Jaekel 	}
4565e286361SAndreas Jaekel 	zev_free(pe, sizeof(*pe));
4572bb8e5e2SAndreas Jaekel 	rw_exit(&zev_pool_list_rwlock);
4582bb8e5e2SAndreas Jaekel 	return (0);
4592bb8e5e2SAndreas Jaekel }
4602bb8e5e2SAndreas Jaekel 
4612bb8e5e2SAndreas Jaekel int
4622bb8e5e2SAndreas Jaekel zev_skip_pool(objset_t *os)
4632bb8e5e2SAndreas Jaekel {
4642bb8e5e2SAndreas Jaekel 	zev_pool_list_entry_t *pe;
4652bb8e5e2SAndreas Jaekel 	dsl_pool_t *dp = os->os_dsl_dataset->ds_dir->dd_pool;
4662bb8e5e2SAndreas Jaekel 	rw_enter(&zev_pool_list_rwlock, RW_READER);
4672bb8e5e2SAndreas Jaekel 	for (pe=zev_muted_pools_head; pe; pe=pe->next) {
4682bb8e5e2SAndreas Jaekel 		if (!strcmp(pe->name, dp->dp_spa->spa_name)) {
4692bb8e5e2SAndreas Jaekel 			rw_exit(&zev_pool_list_rwlock);
4702bb8e5e2SAndreas Jaekel 			return 1;
4712bb8e5e2SAndreas Jaekel 		}
4722bb8e5e2SAndreas Jaekel 	}
4732bb8e5e2SAndreas Jaekel 	rw_exit(&zev_pool_list_rwlock);
4742bb8e5e2SAndreas Jaekel 	return 0;
4752bb8e5e2SAndreas Jaekel }
4762bb8e5e2SAndreas Jaekel 
477bdbd253bSAndreas Jaekel int
478bdbd253bSAndreas Jaekel zev_skip_fs(zfsvfs_t *fs)
479bdbd253bSAndreas Jaekel {
480bdbd253bSAndreas Jaekel 	dsl_dir_t *d = fs->z_os->os_dsl_dataset->ds_dir;
481bdbd253bSAndreas Jaekel 	dsl_dir_t *prev = NULL;
482bdbd253bSAndreas Jaekel 
483bdbd253bSAndreas Jaekel 	while (d && d != prev) {
484bdbd253bSAndreas Jaekel 		if (strstr(d->dd_myname, "_root"))
485bdbd253bSAndreas Jaekel 			return 0;
486bdbd253bSAndreas Jaekel 		prev = d;
487bdbd253bSAndreas Jaekel 		d = d->dd_parent;
488bdbd253bSAndreas Jaekel 	}
489bdbd253bSAndreas Jaekel 	return 1;
490bdbd253bSAndreas Jaekel }
491bdbd253bSAndreas Jaekel 
492e9a5e479SAndreas Jaekel static void
493e9a5e479SAndreas Jaekel zev_update_statistics(int op, zev_statistics_t *stat)
494e9a5e479SAndreas Jaekel {
495e9a5e479SAndreas Jaekel 	switch (op) {
496e9a5e479SAndreas Jaekel 	case ZEV_OP_ERROR:
497e9a5e479SAndreas Jaekel 		stat->zev_cnt_errors++;
498e9a5e479SAndreas Jaekel 		break;
499e9a5e479SAndreas Jaekel 	case ZEV_OP_MARK:
500e9a5e479SAndreas Jaekel 		stat->zev_cnt_marks++;
501e9a5e479SAndreas Jaekel 		break;
502e9a5e479SAndreas Jaekel 	case ZEV_OP_ZFS_MOUNT:
503e9a5e479SAndreas Jaekel 		stat->zev_cnt_zfs_mount++;
504e9a5e479SAndreas Jaekel 		break;
505e9a5e479SAndreas Jaekel 	case ZEV_OP_ZFS_UMOUNT:
506e9a5e479SAndreas Jaekel 		stat->zev_cnt_zfs_umount++;
507e9a5e479SAndreas Jaekel 		break;
508e9a5e479SAndreas Jaekel 	case ZEV_OP_ZVOL_WRITE:
509e9a5e479SAndreas Jaekel 		stat->zev_cnt_zvol_write++;
510e9a5e479SAndreas Jaekel 		break;
511e9a5e479SAndreas Jaekel 	case ZEV_OP_ZVOL_TRUNCATE:
512e9a5e479SAndreas Jaekel 		stat->zev_cnt_zvol_truncate++;
513e9a5e479SAndreas Jaekel 		break;
514e9a5e479SAndreas Jaekel 	case ZEV_OP_ZNODE_CLOSE_AFTER_UPDATE:
515e9a5e479SAndreas Jaekel 		stat->zev_cnt_znode_close_after_update++;
516e9a5e479SAndreas Jaekel 		break;
517e9a5e479SAndreas Jaekel 	case ZEV_OP_ZNODE_CREATE:
518e9a5e479SAndreas Jaekel 		stat->zev_cnt_znode_create++;
519e9a5e479SAndreas Jaekel 		break;
520e9a5e479SAndreas Jaekel 	case ZEV_OP_ZNODE_REMOVE:
521e9a5e479SAndreas Jaekel 		stat->zev_cnt_znode_remove++;
522e9a5e479SAndreas Jaekel 		break;
523e9a5e479SAndreas Jaekel 	case ZEV_OP_ZNODE_LINK:
524e9a5e479SAndreas Jaekel 		stat->zev_cnt_znode_link++;
525e9a5e479SAndreas Jaekel 		break;
526e9a5e479SAndreas Jaekel 	case ZEV_OP_ZNODE_SYMLINK:
527e9a5e479SAndreas Jaekel 		stat->zev_cnt_znode_symlink++;
528e9a5e479SAndreas Jaekel 		break;
529e9a5e479SAndreas Jaekel 	case ZEV_OP_ZNODE_RENAME:
530e9a5e479SAndreas Jaekel 		stat->zev_cnt_znode_rename++;
531e9a5e479SAndreas Jaekel 		break;
532e9a5e479SAndreas Jaekel 	case ZEV_OP_ZNODE_WRITE:
533e9a5e479SAndreas Jaekel 		stat->zev_cnt_znode_write++;
534e9a5e479SAndreas Jaekel 		break;
535e9a5e479SAndreas Jaekel 	case ZEV_OP_ZNODE_TRUNCATE:
536e9a5e479SAndreas Jaekel 		stat->zev_cnt_znode_truncate++;
537e9a5e479SAndreas Jaekel 		break;
538e9a5e479SAndreas Jaekel 	case ZEV_OP_ZNODE_SETATTR:
539e9a5e479SAndreas Jaekel 		stat->zev_cnt_znode_setattr++;
540e9a5e479SAndreas Jaekel 		break;
541e9a5e479SAndreas Jaekel 	case ZEV_OP_ZNODE_ACL:
542e9a5e479SAndreas Jaekel 		stat->zev_cnt_znode_acl++;
543e9a5e479SAndreas Jaekel 		break;
544e9a5e479SAndreas Jaekel 	}
545e9a5e479SAndreas Jaekel }
546e9a5e479SAndreas Jaekel 
5472bb8e5e2SAndreas Jaekel void
54863aba447SAndreas Jaekel zev_queue_message(int op, zev_msg_t *msg)
5492bb8e5e2SAndreas Jaekel {
550e9a5e479SAndreas Jaekel 	zev_queue_t *q;
551205a9bc9SAndreas Jaekel 	int wakeup = 0;
552e9a5e479SAndreas Jaekel 	zev_msg_t *m;
5534ca7dd5eSAndreas Jaekel 	int i;
5542bb8e5e2SAndreas Jaekel 
5559193e9c2SAndreas Jaekel 	msg->next = NULL;
556e9a5e479SAndreas Jaekel 	msg->prev = NULL;
557e9a5e479SAndreas Jaekel 	msg->read = 0;
5582bb8e5e2SAndreas Jaekel 
5592bb8e5e2SAndreas Jaekel 	if (op < ZEV_OP_MIN || op > ZEV_OP_MAX) {
5609193e9c2SAndreas Jaekel 		zev_queue_error(op, "unknown op id encountered: %d", op);
5615e286361SAndreas Jaekel 		zev_free(msg, sizeof(*msg) + msg->size);
5629193e9c2SAndreas Jaekel 		return;
5632bb8e5e2SAndreas Jaekel 	}
5642bb8e5e2SAndreas Jaekel 
5654ca7dd5eSAndreas Jaekel 	/*
5664ca7dd5eSAndreas Jaekel 	 * This mutex protects us agains race conditions when several
5674ca7dd5eSAndreas Jaekel 	 * threads want to queue a message and one or more queues are
5684ca7dd5eSAndreas Jaekel 	 * full:  we release zev_mutex to wait for the queues to become
5694ca7dd5eSAndreas Jaekel 	 * less-than-full, but we don't know in which order the waiting
5704ca7dd5eSAndreas Jaekel 	 * threads will be awoken.  If it's not the same order in which
5714ca7dd5eSAndreas Jaekel 	 * they went to sleep we might mark different messages as "newest"
5724ca7dd5eSAndreas Jaekel 	 * in different queues, and so we might have dupes or even
5734ca7dd5eSAndreas Jaekel 	 * skip messages.
5744ca7dd5eSAndreas Jaekel 	 */
5754ca7dd5eSAndreas Jaekel 	mutex_enter(&zev_queue_msg_mutex);
5764ca7dd5eSAndreas Jaekel 
5772bb8e5e2SAndreas Jaekel 	mutex_enter(&zev_mutex);
578e9a5e479SAndreas Jaekel 
579e9a5e479SAndreas Jaekel 	/*
580e9a5e479SAndreas Jaekel 	 * When the module is loaded, the default behavior ist to
581e9a5e479SAndreas Jaekel 	 * put all events into a queue and block if the queue is full.
582e9a5e479SAndreas Jaekel 	 * This is done even before the pseudo device is attached.
583e9a5e479SAndreas Jaekel 	 * This way, no events are lost.
584e9a5e479SAndreas Jaekel 	 *
585e9a5e479SAndreas Jaekel 	 * To discard events entirely the "beaver" queue,
586e9a5e479SAndreas Jaekel 	 * which never discards anything, has to be removed.
587e9a5e479SAndreas Jaekel 	 */
588e9a5e479SAndreas Jaekel 
5894ca7dd5eSAndreas Jaekel 	if (zev_queue_cnt == 0) {
590e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
5914ca7dd5eSAndreas Jaekel 		mutex_exit(&zev_queue_msg_mutex);
592e9a5e479SAndreas Jaekel 		return;
593e9a5e479SAndreas Jaekel 	}
594e9a5e479SAndreas Jaekel 
595e9a5e479SAndreas Jaekel 	/* put message into global queue */
596e9a5e479SAndreas Jaekel 	msg->seq = zev_msg_sequence_number++;
59707bff397SAndreas Jaekel 
59807bff397SAndreas Jaekel 	/* do we need to make room? */
599ec4d6322SAndreas Jaekel again:
6002bb8e5e2SAndreas Jaekel 	while (zev_statistics.zev_max_queue_len &&
60107bff397SAndreas Jaekel 	    zev_statistics.zev_queue_len > zev_statistics.zev_max_queue_len) {
60207bff397SAndreas Jaekel 
60307bff397SAndreas Jaekel 		if (zev_have_blocking_queues) {
604ec4d6322SAndreas Jaekel 			/* so we have blocking queues.  are they full? */
605ec4d6322SAndreas Jaekel 			for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
606ec4d6322SAndreas Jaekel 				q = zev_queues[i - ZEV_MINOR_MIN];
607ec4d6322SAndreas Jaekel 				if (!q)
60807bff397SAndreas Jaekel 					continue;
609ec4d6322SAndreas Jaekel 				if ((q->zq_flags &
610ec4d6322SAndreas Jaekel 				     ZEV_FL_BLOCK_WHILE_QUEUE_FULL) == 0)
611ec4d6322SAndreas Jaekel 					continue;
612ec4d6322SAndreas Jaekel 				if (q->zq_queue_len &&
613ec4d6322SAndreas Jaekel 				    q->zq_queue_len > q->zq_max_queue_len) {
614ec4d6322SAndreas Jaekel 					/* block until queue's been shrunk. */
615ec4d6322SAndreas Jaekel 					cv_wait(&zev_condvar, &zev_mutex);
616ec4d6322SAndreas Jaekel 					goto again;
617ec4d6322SAndreas Jaekel 				}
618ec4d6322SAndreas Jaekel 			}
61907bff397SAndreas Jaekel 		}
62007bff397SAndreas Jaekel 
62107bff397SAndreas Jaekel 		/* discard events until this message fits into all queues */
62207bff397SAndreas Jaekel 
62307bff397SAndreas Jaekel 		for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
62407bff397SAndreas Jaekel 			q = zev_queues[i - ZEV_MINOR_MIN];
62507bff397SAndreas Jaekel 			if (!q)
62607bff397SAndreas Jaekel 				continue;
62707bff397SAndreas Jaekel 			/* discard msgs until queue is small enough */
62807bff397SAndreas Jaekel 			while (q->zq_queue_len &&
62907bff397SAndreas Jaekel 			       q->zq_queue_len > q->zq_max_queue_len) {
63007bff397SAndreas Jaekel 				m = q->zq_oldest;
63107bff397SAndreas Jaekel 				if (m == NULL)
63207bff397SAndreas Jaekel 					break;
63307bff397SAndreas Jaekel 				q->zq_events_discarded++;
63407bff397SAndreas Jaekel 				q->zq_bytes_discarded += m->size;
63507bff397SAndreas Jaekel 				q->zq_oldest = m->next;
63607bff397SAndreas Jaekel 				q->zq_queue_len -= m->size;
63707bff397SAndreas Jaekel 				q->zq_queue_messages--;
63807bff397SAndreas Jaekel 			}
63907bff397SAndreas Jaekel 		}
64007bff397SAndreas Jaekel 
64107bff397SAndreas Jaekel 		zev_queue_trim();
64207bff397SAndreas Jaekel 		ASSERT(zev_statistics.zev_queue_len == 0 ||
64307bff397SAndreas Jaekel 		       zev_statistics.zev_queue_len <=
64407bff397SAndreas Jaekel 				zev_statistics.zev_max_queue_len);
6452bb8e5e2SAndreas Jaekel 	}
6462bb8e5e2SAndreas Jaekel 
6479193e9c2SAndreas Jaekel 	if (zev_queue_tail == NULL) {
6489193e9c2SAndreas Jaekel 		zev_queue_head = zev_queue_tail = msg;
6499193e9c2SAndreas Jaekel 	} else {
6509193e9c2SAndreas Jaekel 		zev_queue_tail->next = msg;
651e9a5e479SAndreas Jaekel 		msg->prev = zev_queue_tail;
6529193e9c2SAndreas Jaekel 		zev_queue_tail = msg;
6532bb8e5e2SAndreas Jaekel 	}
6549193e9c2SAndreas Jaekel 	zev_queue_len++;
6552bb8e5e2SAndreas Jaekel 	zev_statistics.zev_cnt_total_events++;
6569193e9c2SAndreas Jaekel 	zev_statistics.zev_queue_len += msg->size;
657e9a5e479SAndreas Jaekel 
658e9a5e479SAndreas Jaekel 	/* update per-device queues */
6594ca7dd5eSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
6604ca7dd5eSAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
6614ca7dd5eSAndreas Jaekel 		if (!q)
6624ca7dd5eSAndreas Jaekel 			continue;
6634ca7dd5eSAndreas Jaekel 
664e9a5e479SAndreas Jaekel 		zev_queue_hold(q);
665e9a5e479SAndreas Jaekel 
666e9a5e479SAndreas Jaekel 		/* make sure queue has enough room */
6674ca7dd5eSAndreas Jaekel 		while (q->zq_max_queue_len &&
6684ca7dd5eSAndreas Jaekel 		       q->zq_queue_len > q->zq_max_queue_len) {
6694ca7dd5eSAndreas Jaekel 
670e9a5e479SAndreas Jaekel 			if (q->zq_flags & ZEV_FL_BLOCK_WHILE_QUEUE_FULL) {
671e9a5e479SAndreas Jaekel 				/* block until queue has been shrunk. */
672e9a5e479SAndreas Jaekel 				cv_wait(&zev_condvar, &zev_mutex);
673e9a5e479SAndreas Jaekel 			} else {
674e9a5e479SAndreas Jaekel 				/* discard msgs until queue is small enough */
675e9a5e479SAndreas Jaekel 				while (q->zq_queue_len > q->zq_max_queue_len) {
676e9a5e479SAndreas Jaekel 					m = q->zq_oldest;
677e9a5e479SAndreas Jaekel 					if (m == NULL)
6789193e9c2SAndreas Jaekel 						break;
679e9a5e479SAndreas Jaekel 					q->zq_events_discarded++;
680e9a5e479SAndreas Jaekel 					q->zq_bytes_discarded += m->size;
681e9a5e479SAndreas Jaekel 					q->zq_oldest = m->next;
682e9a5e479SAndreas Jaekel 					q->zq_queue_len -= m->size;
683e9a5e479SAndreas Jaekel 					q->zq_queue_messages--;
6842bb8e5e2SAndreas Jaekel 				}
685e9a5e479SAndreas Jaekel 			}
686e9a5e479SAndreas Jaekel 		}
687e9a5e479SAndreas Jaekel 
688e9a5e479SAndreas Jaekel 		/* register new message at the end of the queue */
689e9a5e479SAndreas Jaekel 		q->zq_queue_len += msg->size;
690e9a5e479SAndreas Jaekel 		q->zq_queue_messages++;
6914ca7dd5eSAndreas Jaekel 		q->zq_bytes_total += msg->size;
692e9a5e479SAndreas Jaekel 		q->zq_events_total++;
693e9a5e479SAndreas Jaekel 		if (q->zq_oldest == NULL)
694e9a5e479SAndreas Jaekel 			q->zq_oldest = msg;
695e9a5e479SAndreas Jaekel 
696e9a5e479SAndreas Jaekel 		zev_update_statistics(op, &q->zq_statistics);
697e9a5e479SAndreas Jaekel 
6984ca7dd5eSAndreas Jaekel 		if (q->zq_queue_len > q->zq_wakeup_threshold)
699e9a5e479SAndreas Jaekel 			wakeup = 1;
7004ca7dd5eSAndreas Jaekel 		if (q->zq_queue_len == msg->size)  /* queue was empty */
7014ca7dd5eSAndreas Jaekel 			cv_broadcast(&q->zq_condvar);
702e9a5e479SAndreas Jaekel 
703e9a5e479SAndreas Jaekel 		zev_queue_release(q);
704e9a5e479SAndreas Jaekel 	}
705e9a5e479SAndreas Jaekel 
706e9a5e479SAndreas Jaekel 	zev_queue_trim();
707e9a5e479SAndreas Jaekel 
708e9a5e479SAndreas Jaekel 	zev_update_statistics(op, &zev_statistics);
7092bb8e5e2SAndreas Jaekel 	mutex_exit(&zev_mutex);
7104ca7dd5eSAndreas Jaekel 	mutex_exit(&zev_queue_msg_mutex);
7112bb8e5e2SAndreas Jaekel 
712e9a5e479SAndreas Jaekel 	/* one or more queues need a pollwakeup() */
7134ca7dd5eSAndreas Jaekel 	if (op == ZEV_OP_MARK) {
7144ca7dd5eSAndreas Jaekel 		zev_poll_wakeup(B_TRUE);
7154ca7dd5eSAndreas Jaekel 	} else if (wakeup) {
7164ca7dd5eSAndreas Jaekel 		zev_poll_wakeup(B_FALSE);
717e9a5e479SAndreas Jaekel 	}
7189193e9c2SAndreas Jaekel 
7199193e9c2SAndreas Jaekel 	return;
7209193e9c2SAndreas Jaekel }
7219193e9c2SAndreas Jaekel 
7229193e9c2SAndreas Jaekel void
7239193e9c2SAndreas Jaekel zev_queue_error(int op, char *fmt, ...)
7249193e9c2SAndreas Jaekel {
7259193e9c2SAndreas Jaekel 	char buf[ZEV_MAX_MESSAGE_LEN];
7269193e9c2SAndreas Jaekel 	va_list ap;
7279193e9c2SAndreas Jaekel 	int len;
72863aba447SAndreas Jaekel 	zev_msg_t *msg = NULL;
72963aba447SAndreas Jaekel 	zev_error_t *rec;
73063aba447SAndreas Jaekel 	int msg_size;
7319193e9c2SAndreas Jaekel 
7329193e9c2SAndreas Jaekel 	va_start(ap, fmt);
7339193e9c2SAndreas Jaekel 	len = vsnprintf(buf, sizeof(buf), fmt, ap);
7349193e9c2SAndreas Jaekel 	va_end(ap);
73563aba447SAndreas Jaekel 	if (len >= sizeof(buf)) {
73663aba447SAndreas Jaekel 		cmn_err(CE_WARN, "zev: can't report error - "
73763aba447SAndreas Jaekel 		        "dropping event entirely.");
7389193e9c2SAndreas Jaekel 		return;
73963aba447SAndreas Jaekel 	}
74063aba447SAndreas Jaekel 
74163aba447SAndreas Jaekel 	msg_size = sizeof(*rec) + len + 1;
7425e286361SAndreas Jaekel 	msg = zev_alloc(sizeof(*msg) + msg_size);
74363aba447SAndreas Jaekel 	msg->size = msg_size;
74463aba447SAndreas Jaekel 	rec = (zev_error_t *)(msg + 1);
74590440cf2SAndreas Jaekel 	rec->record_len = msg_size;
74663aba447SAndreas Jaekel 	rec->op = ZEV_OP_ERROR;
74763aba447SAndreas Jaekel 	rec->op_time = ddi_get_time();
748108668daSAndreas Jaekel 	rec->guid = 0;
74963aba447SAndreas Jaekel 	rec->failed_op = op;
75063aba447SAndreas Jaekel 	rec->errstr_len = len;
751aea44f72SAndreas Jaekel 	(void) memcpy(ZEV_ERRSTR(rec), buf, len + 1);
75263aba447SAndreas Jaekel 
75363aba447SAndreas Jaekel 	zev_queue_message(ZEV_OP_ERROR, msg);
75463aba447SAndreas Jaekel 	return;
7552bb8e5e2SAndreas Jaekel }
7562bb8e5e2SAndreas Jaekel 
7574ca7dd5eSAndreas Jaekel static int
7584ca7dd5eSAndreas Jaekel zev_find_queue(zev_queue_t **out, zev_queue_t *req_q, zev_queue_name_t *name)
7594ca7dd5eSAndreas Jaekel {
7604ca7dd5eSAndreas Jaekel 	char namebuf[ZEV_MAX_QUEUE_NAME_LEN+1];
7614ca7dd5eSAndreas Jaekel 	zev_queue_t *q;
7624ca7dd5eSAndreas Jaekel 	int i;
7634ca7dd5eSAndreas Jaekel 
7644ca7dd5eSAndreas Jaekel 	*out = NULL;
7654ca7dd5eSAndreas Jaekel 
7664ca7dd5eSAndreas Jaekel 	if (name->zev_namelen == 0) {
7674ca7dd5eSAndreas Jaekel 		if (req_q->zq_minor_number == ZEV_CONTROL_DEVICE_MINOR)
7684ca7dd5eSAndreas Jaekel 			return EINVAL;
7699ae8ebd1SAndreas Jaekel 		mutex_enter(&zev_mutex);
7704ca7dd5eSAndreas Jaekel 		zev_queue_hold(req_q);
7719ae8ebd1SAndreas Jaekel 		mutex_exit(&zev_mutex);
7724ca7dd5eSAndreas Jaekel 		*out = req_q;
7734ca7dd5eSAndreas Jaekel 		return 0;
7744ca7dd5eSAndreas Jaekel 	}
7754ca7dd5eSAndreas Jaekel 
7764ca7dd5eSAndreas Jaekel 	if (name->zev_namelen > ZEV_MAX_QUEUE_NAME_LEN)
7774ca7dd5eSAndreas Jaekel 		return EINVAL;
7784ca7dd5eSAndreas Jaekel 	strncpy(namebuf, name->zev_name, name->zev_namelen);
7794ca7dd5eSAndreas Jaekel 	namebuf[name->zev_namelen] = '\0';
7804ca7dd5eSAndreas Jaekel 
7814ca7dd5eSAndreas Jaekel 	mutex_enter(&zev_mutex);
7824ca7dd5eSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
7834ca7dd5eSAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
7844ca7dd5eSAndreas Jaekel 		if (!q)
7854ca7dd5eSAndreas Jaekel 			continue;
7864ca7dd5eSAndreas Jaekel 		if (!strcmp(q->zq_name, namebuf)) {
7874ca7dd5eSAndreas Jaekel 			zev_queue_hold(q);
7884ca7dd5eSAndreas Jaekel 			mutex_exit(&zev_mutex);
7894ca7dd5eSAndreas Jaekel 			*out = q;
7904ca7dd5eSAndreas Jaekel 			return 0;
7914ca7dd5eSAndreas Jaekel 		}
7924ca7dd5eSAndreas Jaekel 	}
7934ca7dd5eSAndreas Jaekel 	mutex_exit(&zev_mutex);
7944ca7dd5eSAndreas Jaekel 	return ENOENT;
7954ca7dd5eSAndreas Jaekel }
796c035b1e8SAndreas Jaekel 
797e9a5e479SAndreas Jaekel static int
798e9a5e479SAndreas Jaekel zev_ioc_get_queue_statistics(zev_queue_t *req_q, intptr_t arg, int mode)
799e9a5e479SAndreas Jaekel {
800e9a5e479SAndreas Jaekel 	zev_ioctl_get_queue_statistics_t gs;
801e9a5e479SAndreas Jaekel 	zev_queue_t *q;
8024ca7dd5eSAndreas Jaekel 	int ret;
803e9a5e479SAndreas Jaekel 
804e9a5e479SAndreas Jaekel 	if (ddi_copyin((void *)arg, &gs, sizeof(gs), mode) != 0)
805c035b1e8SAndreas Jaekel 		return EFAULT;
806e9a5e479SAndreas Jaekel 
8074ca7dd5eSAndreas Jaekel 	ret = zev_find_queue(&q, req_q, &gs.zev_queue_name);
8084ca7dd5eSAndreas Jaekel 	if (ret)
8094ca7dd5eSAndreas Jaekel 		return ret;
810e9a5e479SAndreas Jaekel 
811e9a5e479SAndreas Jaekel 	/* ddi_copyout() can take a long time.  Better make
812e9a5e479SAndreas Jaekel 	   a copy to be able to release the mutex faster. */
813e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
814e9a5e479SAndreas Jaekel 	memcpy(&gs.zev_statistics, &q->zq_statistics,sizeof(gs.zev_statistics));
815e9a5e479SAndreas Jaekel 	gs.zev_statistics.zev_queue_len = q->zq_queue_len;
816e9a5e479SAndreas Jaekel 	gs.zev_statistics.zev_bytes_read = q->zq_bytes_read;
817e9a5e479SAndreas Jaekel 	gs.zev_statistics.zev_bytes_discarded = q->zq_bytes_discarded;
818e9a5e479SAndreas Jaekel 	gs.zev_statistics.zev_max_queue_len = q->zq_max_queue_len;
819e9a5e479SAndreas Jaekel 	gs.zev_statistics.zev_cnt_discarded_events = q->zq_events_discarded;
820e9a5e479SAndreas Jaekel 	gs.zev_statistics.zev_cnt_total_events = q->zq_events_total;
821e9a5e479SAndreas Jaekel 	zev_queue_release(q);
822e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
823e9a5e479SAndreas Jaekel 
824e9a5e479SAndreas Jaekel 	if (ddi_copyout(&gs, (void *)arg, sizeof(gs), mode) != 0)
825e9a5e479SAndreas Jaekel 		return EFAULT;
826e9a5e479SAndreas Jaekel 	return 0;
827c035b1e8SAndreas Jaekel }
828e9a5e479SAndreas Jaekel 
829e9a5e479SAndreas Jaekel static int
830e9a5e479SAndreas Jaekel zev_ioc_set_queue_properties(zev_queue_t *req_q, intptr_t arg, int mode)
831e9a5e479SAndreas Jaekel {
832e9a5e479SAndreas Jaekel 	zev_ioctl_set_queue_properties_t qp;
833e9a5e479SAndreas Jaekel 	zev_queue_t *q;
834e9a5e479SAndreas Jaekel 	uint64_t old_max;
835e9a5e479SAndreas Jaekel 	uint64_t old_flags;
8364ca7dd5eSAndreas Jaekel 	int ret;
837e9a5e479SAndreas Jaekel 
838e9a5e479SAndreas Jaekel 	if (ddi_copyin((void *)arg, &qp, sizeof(qp), mode) != 0)
839e9a5e479SAndreas Jaekel 		return EFAULT;
840e9a5e479SAndreas Jaekel 	if (qp.zev_max_queue_len > ZEV_MAX_QUEUE_LEN)
841e9a5e479SAndreas Jaekel 		return EINVAL;
8424ca7dd5eSAndreas Jaekel 	if (qp.zev_poll_wakeup_threshold > ZEV_MAX_POLL_WAKEUP_QUEUE_LEN)
843e9a5e479SAndreas Jaekel 		return EINVAL;
844e9a5e479SAndreas Jaekel 
8454ca7dd5eSAndreas Jaekel 	ret = zev_find_queue(&q, req_q, &qp.zev_queue_name);
8464ca7dd5eSAndreas Jaekel 	if (ret)
8474ca7dd5eSAndreas Jaekel 		return ret;
848e9a5e479SAndreas Jaekel 
849e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
850e9a5e479SAndreas Jaekel 
851e9a5e479SAndreas Jaekel 	/*
852e9a5e479SAndreas Jaekel 	 * Note: if the PERSISTENT flag is cleared, and the queue is not busy,
853e9a5e479SAndreas Jaekel 	 * the queue should be removed by zev_queue_release() in zev_ioctl().
854e9a5e479SAndreas Jaekel 	 */
855e9a5e479SAndreas Jaekel 	old_flags = qp.zev_flags;
856e9a5e479SAndreas Jaekel 	q->zq_flags = qp.zev_flags;
857e9a5e479SAndreas Jaekel 	if ((old_flags & ZEV_FL_BLOCK_WHILE_QUEUE_FULL) &&
858e9a5e479SAndreas Jaekel 	   (!(qp.zev_flags & ZEV_FL_BLOCK_WHILE_QUEUE_FULL))) {
859e9a5e479SAndreas Jaekel 		/* queue is no longer blocking - wake blocked threads */
860e9a5e479SAndreas Jaekel 		cv_broadcast(&zev_condvar);
861c035b1e8SAndreas Jaekel 	}
862e9a5e479SAndreas Jaekel 
86307bff397SAndreas Jaekel 	zev_update_blockflag();
86407bff397SAndreas Jaekel 
865e9a5e479SAndreas Jaekel 	old_max = q->zq_max_queue_len;
866e9a5e479SAndreas Jaekel 	q->zq_max_queue_len = qp.zev_max_queue_len;
867e9a5e479SAndreas Jaekel 	if (q->zq_max_queue_len < old_max)
868e9a5e479SAndreas Jaekel 		zev_queue_trim();
869e9a5e479SAndreas Jaekel 	if (q->zq_max_queue_len > old_max)
870e9a5e479SAndreas Jaekel 		cv_broadcast(&zev_condvar);	/* threads may be waiting */
871e9a5e479SAndreas Jaekel 
872e9a5e479SAndreas Jaekel 	if ((qp.zev_poll_wakeup_threshold < q->zq_wakeup_threshold) &&
873e9a5e479SAndreas Jaekel 	    (qp.zev_poll_wakeup_threshold <= q->zq_queue_len))
874e9a5e479SAndreas Jaekel 		pollwakeup(&q->zq_pollhead, POLLIN);
875e9a5e479SAndreas Jaekel 	q->zq_wakeup_threshold = qp.zev_poll_wakeup_threshold;
876e9a5e479SAndreas Jaekel 
877e9a5e479SAndreas Jaekel 	zev_queue_release(q);
878e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
879e9a5e479SAndreas Jaekel 	return 0;
880c035b1e8SAndreas Jaekel }
881e9a5e479SAndreas Jaekel 
882e9a5e479SAndreas Jaekel static int
883e9a5e479SAndreas Jaekel zev_ioc_get_queue_properties(zev_queue_t *req_q, intptr_t arg, int mode)
884e9a5e479SAndreas Jaekel {
885e9a5e479SAndreas Jaekel 	zev_ioctl_get_queue_properties_t qp;
886e9a5e479SAndreas Jaekel 	zev_queue_t *q;
8874ca7dd5eSAndreas Jaekel 	int ret;
888e9a5e479SAndreas Jaekel 
889e9a5e479SAndreas Jaekel 	if (ddi_copyin((void *)arg, &qp, sizeof(qp), mode) != 0)
890e9a5e479SAndreas Jaekel 		return EFAULT;
891e9a5e479SAndreas Jaekel 
8924ca7dd5eSAndreas Jaekel 	ret = zev_find_queue(&q, req_q, &qp.zev_queue_name);
8934ca7dd5eSAndreas Jaekel 	if (ret)
8944ca7dd5eSAndreas Jaekel 		return ret;
895e9a5e479SAndreas Jaekel 
896e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
897e9a5e479SAndreas Jaekel 	qp.zev_max_queue_len = q->zq_max_queue_len;
898e9a5e479SAndreas Jaekel 	qp.zev_flags = q->zq_flags;
899e9a5e479SAndreas Jaekel 	qp.zev_poll_wakeup_threshold = q->zq_wakeup_threshold;
900e9a5e479SAndreas Jaekel 	zev_queue_release(q);
901e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
902e9a5e479SAndreas Jaekel 
903e9a5e479SAndreas Jaekel 	if (ddi_copyout(&qp, (void *)arg, sizeof(qp), mode) != 0)
904e9a5e479SAndreas Jaekel 		return EFAULT;
905e9a5e479SAndreas Jaekel 	return 0;
906e9a5e479SAndreas Jaekel }
907e9a5e479SAndreas Jaekel 
908e9a5e479SAndreas Jaekel static int
909e9a5e479SAndreas Jaekel zev_ioc_add_queue(zev_queue_t *req_q, intptr_t arg, int mode)
910e9a5e479SAndreas Jaekel {
911e9a5e479SAndreas Jaekel 	zev_ioctl_add_queue_t aq;
912e9a5e479SAndreas Jaekel 	zev_queue_t *new_q;
913e9a5e479SAndreas Jaekel 	char name[ZEV_MAX_QUEUE_NAME_LEN+1];
914e9a5e479SAndreas Jaekel 
915e9a5e479SAndreas Jaekel 	if (ddi_copyin((void *)arg, &aq, sizeof(aq), mode) != 0)
916e9a5e479SAndreas Jaekel 		return EFAULT;
917e9a5e479SAndreas Jaekel 
918e9a5e479SAndreas Jaekel 	if (aq.zev_namelen > ZEV_MAX_QUEUE_NAME_LEN)
919e9a5e479SAndreas Jaekel 		return EINVAL;
920e9a5e479SAndreas Jaekel 	strncpy(name, aq.zev_name, aq.zev_namelen);
921e9a5e479SAndreas Jaekel 	name[aq.zev_namelen] = '\0';
9226450d95eSAndreas Jaekel 	if (!strncmp(name, ZEV_TMPQUEUE_DEVICE_NAME,
9236450d95eSAndreas Jaekel 	             strlen(ZEV_TMPQUEUE_DEVICE_NAME)))
9246450d95eSAndreas Jaekel 		return EINVAL;
925e9a5e479SAndreas Jaekel 
926e9a5e479SAndreas Jaekel 	return zev_queue_new(&new_q, req_q->zq_dip, name,
927e9a5e479SAndreas Jaekel 	                     aq.zev_max_queue_len, aq.zev_flags);
928e9a5e479SAndreas Jaekel }
929e9a5e479SAndreas Jaekel 
930e9a5e479SAndreas Jaekel static int
931e9a5e479SAndreas Jaekel zev_ioc_remove_queue(zev_queue_t *req_q, intptr_t arg, int mode)
932e9a5e479SAndreas Jaekel {
933e9a5e479SAndreas Jaekel 	zev_ioctl_remove_queue_t rq;
934e9a5e479SAndreas Jaekel 	zev_queue_t *q;
935e9a5e479SAndreas Jaekel 	char name[ZEV_MAX_QUEUE_NAME_LEN+1];
9364ca7dd5eSAndreas Jaekel 	int found = 0;
9374ca7dd5eSAndreas Jaekel 	int i;
938e9a5e479SAndreas Jaekel 
939e9a5e479SAndreas Jaekel 	if (ddi_copyin((void *)arg, &rq, sizeof(rq), mode) != 0)
940e9a5e479SAndreas Jaekel 		return EFAULT;
941e9a5e479SAndreas Jaekel 
9424ca7dd5eSAndreas Jaekel 	if (rq.zev_queue_name.zev_namelen > ZEV_MAX_QUEUE_NAME_LEN)
943e9a5e479SAndreas Jaekel 		return EINVAL;
9444ca7dd5eSAndreas Jaekel 	strncpy(name, rq.zev_queue_name.zev_name,
9454ca7dd5eSAndreas Jaekel 	        rq.zev_queue_name.zev_namelen);
9464ca7dd5eSAndreas Jaekel 	name[rq.zev_queue_name.zev_namelen] = '\0';
947e9a5e479SAndreas Jaekel 
948e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
9494ca7dd5eSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
9504ca7dd5eSAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
9514ca7dd5eSAndreas Jaekel 		if (!q)
9524ca7dd5eSAndreas Jaekel 			continue;
9534ca7dd5eSAndreas Jaekel 		if (!strcmp(q->zq_name, name)) {
9544ca7dd5eSAndreas Jaekel 			found = 1;
9554ca7dd5eSAndreas Jaekel 			break;
956e9a5e479SAndreas Jaekel 		}
9574ca7dd5eSAndreas Jaekel 	}
9584ca7dd5eSAndreas Jaekel 	if (!found) {
959e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
960e9a5e479SAndreas Jaekel 		return ENOENT;
9614ca7dd5eSAndreas Jaekel 	}
9624ca7dd5eSAndreas Jaekel 
963e9a5e479SAndreas Jaekel 	if (q->zq_busy) {
964e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
965e9a5e479SAndreas Jaekel 		return EBUSY;
966e9a5e479SAndreas Jaekel 	}
967e9a5e479SAndreas Jaekel 	/*
968e9a5e479SAndreas Jaekel 	 * clear flags, so that persistent queues are removed aswell
969e9a5e479SAndreas Jaekel 	 * and the queue becomes non-blocking.
970e9a5e479SAndreas Jaekel 	 */
971e9a5e479SAndreas Jaekel 	q->zq_flags = 0;
972e9a5e479SAndreas Jaekel 	if (q->zq_to_be_removed == B_FALSE) {
973e9a5e479SAndreas Jaekel 		q->zq_to_be_removed = B_TRUE;
974e9a5e479SAndreas Jaekel 		zev_queue_release(q);
975e9a5e479SAndreas Jaekel 	}
976e9a5e479SAndreas Jaekel 	/* some threads might be waiting for this queue to become writable */
977e9a5e479SAndreas Jaekel 	cv_broadcast(&zev_condvar);
978e9a5e479SAndreas Jaekel 
979e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
980e9a5e479SAndreas Jaekel 	return 0;
981e9a5e479SAndreas Jaekel }
982e9a5e479SAndreas Jaekel 
983e9a5e479SAndreas Jaekel static int
984e9a5e479SAndreas Jaekel zev_ioc_get_debug_info(zev_queue_t *req_q, intptr_t arg, int mode)
985e9a5e479SAndreas Jaekel {
986e9a5e479SAndreas Jaekel 	zev_ioctl_debug_info_t di;
987e9a5e479SAndreas Jaekel 	uint64_t mem_allocated = atomic_add_64_nv(&zev_memory_allocated, 0);
988e9a5e479SAndreas Jaekel 	uint64_t mem_freed     = atomic_add_64_nv(&zev_memory_freed, 0);
989e9a5e479SAndreas Jaekel 
9905e286361SAndreas Jaekel 	zev_chksum_stats(&di.zev_chksum_cache_size,
9915e286361SAndreas Jaekel 	                 &di.zev_chksum_cache_hits,
9925e286361SAndreas Jaekel 	                 &di.zev_chksum_cache_misses);
993e9a5e479SAndreas Jaekel 	di.zev_memory_allocated = mem_allocated - mem_freed;
994e9a5e479SAndreas Jaekel 	if (ddi_copyout(&di, (void *)arg, sizeof(di), mode) != 0)
995e9a5e479SAndreas Jaekel 		return EFAULT;
996e9a5e479SAndreas Jaekel 	return 0;
997e9a5e479SAndreas Jaekel }
998e9a5e479SAndreas Jaekel 
999e9a5e479SAndreas Jaekel static int
1000e9a5e479SAndreas Jaekel zev_ioc_get_queue_list(zev_queue_t *req_q, intptr_t arg, int mode)
1001e9a5e479SAndreas Jaekel {
1002e9a5e479SAndreas Jaekel 	zev_ioctl_get_queue_list_t gql;
1003e9a5e479SAndreas Jaekel 	zev_queue_t *q;
1004e9a5e479SAndreas Jaekel 	int i = 0;
10054ca7dd5eSAndreas Jaekel 	int count = 0;
1006e9a5e479SAndreas Jaekel 
1007e9a5e479SAndreas Jaekel 	memset(&gql, 0, sizeof(gql));
1008e9a5e479SAndreas Jaekel 
1009e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
10104ca7dd5eSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
10114ca7dd5eSAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
10124ca7dd5eSAndreas Jaekel 		if (!q)
10134ca7dd5eSAndreas Jaekel 			continue;
10144ca7dd5eSAndreas Jaekel 		strncpy(gql.zev_queue_name[count].zev_name,
1015e9a5e479SAndreas Jaekel 		    q->zq_name, ZEV_MAX_QUEUE_NAME_LEN);
10164ca7dd5eSAndreas Jaekel 		gql.zev_queue_name[count].zev_namelen = strlen(q->zq_name);
10174ca7dd5eSAndreas Jaekel 		count++;
1018e9a5e479SAndreas Jaekel 	}
10194ca7dd5eSAndreas Jaekel 	gql.zev_n_queues = count;
1020e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
1021e9a5e479SAndreas Jaekel 
1022e9a5e479SAndreas Jaekel 	if (ddi_copyout(&gql, (void *)arg, sizeof(gql), mode) != 0)
1023e9a5e479SAndreas Jaekel 		return EFAULT;
1024e9a5e479SAndreas Jaekel 	return 0;
1025c035b1e8SAndreas Jaekel }
1026c035b1e8SAndreas Jaekel 
10273e7eb2d1SAndreas Jaekel static int
10283e7eb2d1SAndreas Jaekel zev_ioc_set_max_queue_len(zev_queue_t *req_q, intptr_t arg, int mode)
10293e7eb2d1SAndreas Jaekel {
10303e7eb2d1SAndreas Jaekel 	uint64_t len;
10313e7eb2d1SAndreas Jaekel 	int i;
10323e7eb2d1SAndreas Jaekel 	zev_queue_t *q;
10333e7eb2d1SAndreas Jaekel 
10343e7eb2d1SAndreas Jaekel 	if (ddi_copyin((void *)arg, &len, sizeof(len), mode) != 0) {
10353e7eb2d1SAndreas Jaekel 		return EFAULT;
10363e7eb2d1SAndreas Jaekel 	}
10373e7eb2d1SAndreas Jaekel 	if (len > ZEV_MAX_QUEUE_LEN) {
10383e7eb2d1SAndreas Jaekel 		return EINVAL;
10393e7eb2d1SAndreas Jaekel 	}
10403e7eb2d1SAndreas Jaekel 	mutex_enter(&zev_mutex);
10413e7eb2d1SAndreas Jaekel 	zev_statistics.zev_max_queue_len = len;
10423e7eb2d1SAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
10433e7eb2d1SAndreas Jaekel 		q = zev_queues[i - ZEV_MINOR_MIN];
10443e7eb2d1SAndreas Jaekel 		if (!q)
10453e7eb2d1SAndreas Jaekel 			continue;
10463e7eb2d1SAndreas Jaekel 		if (q->zq_max_queue_len <=
10473e7eb2d1SAndreas Jaekel 		    zev_statistics.zev_max_queue_len)
10483e7eb2d1SAndreas Jaekel 			continue;
10493e7eb2d1SAndreas Jaekel 		q->zq_max_queue_len = zev_statistics.zev_max_queue_len;
10503e7eb2d1SAndreas Jaekel 	}
10513e7eb2d1SAndreas Jaekel 	cv_broadcast(&zev_condvar);
10523e7eb2d1SAndreas Jaekel 	mutex_exit(&zev_mutex);
10533e7eb2d1SAndreas Jaekel 	return 0;
10543e7eb2d1SAndreas Jaekel }
10553e7eb2d1SAndreas Jaekel 
1056c62d8367SAndreas Jaekel static int
1057c62d8367SAndreas Jaekel zev_ioc_get_zev_version(intptr_t arg, int mode)
1058c62d8367SAndreas Jaekel {
1059c62d8367SAndreas Jaekel 	zev_ioctl_get_zev_version vi;
1060c62d8367SAndreas Jaekel 	vi.zev_major_version = ZEV_MAJOR_VERSION;
1061c62d8367SAndreas Jaekel 	vi.zev_minor_version = ZEV_MINOR_VERSION;
1062c62d8367SAndreas Jaekel 	if (ddi_copyout(&vi, (void *)arg, sizeof(vi), mode) != 0)
1063c62d8367SAndreas Jaekel 		return EFAULT;
1064c62d8367SAndreas Jaekel 	return 0;
1065c62d8367SAndreas Jaekel }
1066c62d8367SAndreas Jaekel 
1067aea44f72SAndreas Jaekel /* ARGSUSED */
10682bb8e5e2SAndreas Jaekel static int
10692bb8e5e2SAndreas Jaekel zev_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
10702bb8e5e2SAndreas Jaekel {
10712bb8e5e2SAndreas Jaekel 	zev_statistics_t zs;
10722bb8e5e2SAndreas Jaekel 	zev_ioctl_poolarg_t pa;
107301c2c787SAndreas Jaekel 	zev_ioctl_mark_t mark;
107401c2c787SAndreas Jaekel 	zev_mark_t *rec;
107501c2c787SAndreas Jaekel 	int msg_size;
107601c2c787SAndreas Jaekel 	zev_msg_t *msg;
107701c2c787SAndreas Jaekel 	uint64_t mark_id;
1078e9a5e479SAndreas Jaekel 	minor_t minor;
1079e9a5e479SAndreas Jaekel 	zev_queue_t *req_q;
1080e9a5e479SAndreas Jaekel 	int ret = 0;
10812bb8e5e2SAndreas Jaekel 
1082e9a5e479SAndreas Jaekel 	minor = getminor(dev);
1083e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
1084e9a5e479SAndreas Jaekel 	if ((req_q = ddi_get_soft_state(statep, minor)) == NULL) {
1085e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
10862bb8e5e2SAndreas Jaekel 		return (ENXIO);
1087e9a5e479SAndreas Jaekel 	}
1088e9a5e479SAndreas Jaekel 	zev_queue_hold(req_q);
1089e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
109046c85740SAndreas Jaekel 	/*
109146c85740SAndreas Jaekel 	 * all structures passed between kernel and userspace
109246c85740SAndreas Jaekel 	 * are now compatible between 64 and 32 bit.  Model
1093e9a5e479SAndreas Jaekel 	 * conversion can be ignored.
109446c85740SAndreas Jaekel 	 */
10952bb8e5e2SAndreas Jaekel 	switch (cmd) {
1096e9a5e479SAndreas Jaekel 	case ZEV_IOC_GET_GLOBAL_STATISTICS:
10972bb8e5e2SAndreas Jaekel 		/* ddi_copyout() can take a long time.  Better make
10982bb8e5e2SAndreas Jaekel 		   a copy to be able to release the mutex faster. */
10992bb8e5e2SAndreas Jaekel 		mutex_enter(&zev_mutex);
1100aea44f72SAndreas Jaekel 		(void) memcpy(&zs, &zev_statistics, sizeof(zs));
11012bb8e5e2SAndreas Jaekel 		mutex_exit(&zev_mutex);
11022bb8e5e2SAndreas Jaekel 		if (ddi_copyout(&zs, (void *)arg, sizeof(zs), mode) != 0)
1103e9a5e479SAndreas Jaekel 			ret = EFAULT;
1104e9a5e479SAndreas Jaekel 		break;
1105e9a5e479SAndreas Jaekel 	case ZEV_IOC_GET_QUEUE_STATISTICS:
1106e9a5e479SAndreas Jaekel 		ret = zev_ioc_get_queue_statistics(req_q, arg, mode);
11072bb8e5e2SAndreas Jaekel 		break;
11082bb8e5e2SAndreas Jaekel 	case ZEV_IOC_MUTE_POOL:
11092bb8e5e2SAndreas Jaekel 	case ZEV_IOC_UNMUTE_POOL:
1110e9a5e479SAndreas Jaekel 		if (ddi_copyin((void *)arg, &pa, sizeof(pa), mode) != 0) {
1111e9a5e479SAndreas Jaekel 			ret = EFAULT;
1112e9a5e479SAndreas Jaekel 			break;
1113e9a5e479SAndreas Jaekel 		}
1114e9a5e479SAndreas Jaekel 		if (pa.zev_poolname_len >=MAXPATHLEN) {
1115e9a5e479SAndreas Jaekel 			ret = EINVAL;
1116e9a5e479SAndreas Jaekel 			break;
1117e9a5e479SAndreas Jaekel 		}
11182bb8e5e2SAndreas Jaekel 		pa.zev_poolname[pa.zev_poolname_len] = '\0';
11192bb8e5e2SAndreas Jaekel 		if (cmd == ZEV_IOC_MUTE_POOL) {
1120e9a5e479SAndreas Jaekel 			ret = zev_ioc_mute_pool(pa.zev_poolname);
11212bb8e5e2SAndreas Jaekel 		} else {
1122e9a5e479SAndreas Jaekel 			ret = zev_ioc_unmute_pool(pa.zev_poolname);
11232bb8e5e2SAndreas Jaekel 		}
1124e9a5e479SAndreas Jaekel 		break;
11252bb8e5e2SAndreas Jaekel 	case ZEV_IOC_SET_MAX_QUEUE_LEN:
11263e7eb2d1SAndreas Jaekel 		ret = zev_ioc_set_max_queue_len(req_q, arg, mode);
11272bb8e5e2SAndreas Jaekel 		break;
1128e9a5e479SAndreas Jaekel 	case ZEV_IOC_GET_QUEUE_PROPERTIES:
1129e9a5e479SAndreas Jaekel 		ret = zev_ioc_get_queue_properties(req_q, arg, mode);
1130e9a5e479SAndreas Jaekel 		break;
1131e9a5e479SAndreas Jaekel 	case ZEV_IOC_SET_QUEUE_PROPERTIES:
1132e9a5e479SAndreas Jaekel 		ret = zev_ioc_set_queue_properties(req_q, arg, mode);
1133205a9bc9SAndreas Jaekel 		break;
113401c2c787SAndreas Jaekel 	case ZEV_IOC_MARK:
1135e9a5e479SAndreas Jaekel 		if (ddi_copyin((void *)arg, &mark, sizeof(mark), mode) != 0) {
1136e9a5e479SAndreas Jaekel 			ret = EFAULT;
1137e9a5e479SAndreas Jaekel 			break;
1138e9a5e479SAndreas Jaekel 		}
113901c2c787SAndreas Jaekel 		/* prepare message */
114001c2c787SAndreas Jaekel 		msg_size = sizeof(*rec) + mark.zev_payload_len + 1;
11415e286361SAndreas Jaekel 		msg = zev_alloc(sizeof(*msg) + msg_size);
114201c2c787SAndreas Jaekel 		msg->size = msg_size;
114301c2c787SAndreas Jaekel 		rec = (zev_mark_t *)(msg + 1);
114401c2c787SAndreas Jaekel 		rec->record_len = msg_size;
114501c2c787SAndreas Jaekel 		rec->op = ZEV_OP_MARK;
114601c2c787SAndreas Jaekel 		rec->op_time = ddi_get_time();
114701c2c787SAndreas Jaekel 		rec->guid = mark.zev_guid;
114801c2c787SAndreas Jaekel 		rec->payload_len = mark.zev_payload_len;
114901c2c787SAndreas Jaekel 		/* get payload */
115001c2c787SAndreas Jaekel 		if (ddi_copyin(((char *)arg) + sizeof(mark),
115101c2c787SAndreas Jaekel 		               ZEV_PAYLOAD(rec),
115201c2c787SAndreas Jaekel 		               mark.zev_payload_len, mode) != 0) {
11535e286361SAndreas Jaekel 			zev_free(msg, msg_size);
1154e9a5e479SAndreas Jaekel 			ret = EFAULT;
1155e9a5e479SAndreas Jaekel 			break;
115601c2c787SAndreas Jaekel 		}
115701c2c787SAndreas Jaekel 		*(ZEV_PAYLOAD(rec) + mark.zev_payload_len) = '\0';
115801c2c787SAndreas Jaekel 		/* get mark id and queue message */
115901c2c787SAndreas Jaekel 		mutex_enter(&zev_mark_id_mutex);
116001c2c787SAndreas Jaekel 		mark_id = zev_mark_id++;
116101c2c787SAndreas Jaekel 		mutex_exit(&zev_mark_id_mutex);
116201c2c787SAndreas Jaekel 		rec->mark_id = mark_id;
116301c2c787SAndreas Jaekel 		zev_queue_message(ZEV_OP_MARK, msg);
116401c2c787SAndreas Jaekel 		/* report mark id to userland, ignore errors */
116501c2c787SAndreas Jaekel 		mark.zev_mark_id = mark_id;
116601c2c787SAndreas Jaekel 		ddi_copyout(&mark, (void *)arg, sizeof(mark), mode);
116701c2c787SAndreas Jaekel 		break;
1168e9a5e479SAndreas Jaekel 	case ZEV_IOC_ADD_QUEUE:
1169e9a5e479SAndreas Jaekel 		if (minor != ZEV_CONTROL_DEVICE_MINOR) {
1170e9a5e479SAndreas Jaekel 			ret = EACCES;
1171e9a5e479SAndreas Jaekel 			break;
1172e9a5e479SAndreas Jaekel 		}
1173e9a5e479SAndreas Jaekel 		ret = zev_ioc_add_queue(req_q, arg, mode);
1174e9a5e479SAndreas Jaekel 		break;
1175e9a5e479SAndreas Jaekel 	case ZEV_IOC_REMOVE_QUEUE:
1176e9a5e479SAndreas Jaekel 		if (minor != ZEV_CONTROL_DEVICE_MINOR) {
1177e9a5e479SAndreas Jaekel 			ret = EACCES;
1178e9a5e479SAndreas Jaekel 			break;
1179e9a5e479SAndreas Jaekel 		}
1180e9a5e479SAndreas Jaekel 		ret = zev_ioc_remove_queue(req_q, arg, mode);
1181e9a5e479SAndreas Jaekel 		break;
1182e9a5e479SAndreas Jaekel 	case ZEV_IOC_GET_DEBUG_INFO:
1183e9a5e479SAndreas Jaekel 		ret = zev_ioc_get_debug_info(req_q, arg, mode);
1184e9a5e479SAndreas Jaekel 		break;
1185e9a5e479SAndreas Jaekel 	case ZEV_IOC_GET_QUEUE_LIST:
1186e9a5e479SAndreas Jaekel 		ret = zev_ioc_get_queue_list(req_q, arg, mode);
1187e9a5e479SAndreas Jaekel 		break;
1188b9710123SAndreas Jaekel 	case ZEV_IOC_GET_FILE_SIGNATURES:
1189b9710123SAndreas Jaekel 		ret = zev_ioc_get_signatures(arg, mode);
1190b9710123SAndreas Jaekel 		break;
1191c62d8367SAndreas Jaekel 	case ZEV_IOC_GET_ZEV_VERSION:
1192c62d8367SAndreas Jaekel 		ret = zev_ioc_get_zev_version(arg, mode);
1193c62d8367SAndreas Jaekel 		break;
11942bb8e5e2SAndreas Jaekel 	default:
11952bb8e5e2SAndreas Jaekel 		/* generic "ioctl unknown" error */
1196e9a5e479SAndreas Jaekel 		ret = ENOTTY;
11972bb8e5e2SAndreas Jaekel 	}
1198e9a5e479SAndreas Jaekel 
1199e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
1200e9a5e479SAndreas Jaekel 	zev_queue_release(req_q);
1201e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
1202b9710123SAndreas Jaekel 	if (ret)
1203*df8caf2dSSimon Klinkert 		return(SET_ERROR(ret));
1204e9a5e479SAndreas Jaekel 	return (ret);
12052bb8e5e2SAndreas Jaekel }
12062bb8e5e2SAndreas Jaekel 
12072bb8e5e2SAndreas Jaekel static int
12082bb8e5e2SAndreas Jaekel zev_chpoll(dev_t dev, short events, int anyyet,
12092bb8e5e2SAndreas Jaekel     short *reventsp, struct pollhead **phpp)
12102bb8e5e2SAndreas Jaekel {
1211e9a5e479SAndreas Jaekel 	int minor;
12122bb8e5e2SAndreas Jaekel 	short revent = 0;
1213e9a5e479SAndreas Jaekel 	zev_queue_t *q;
12142bb8e5e2SAndreas Jaekel 
1215e9a5e479SAndreas Jaekel 	/* use minor-specific queue context and it's pollhead */
1216e9a5e479SAndreas Jaekel 	minor = getminor(dev);
1217e9a5e479SAndreas Jaekel 	if (minor == ZEV_CONTROL_DEVICE_MINOR)
1218e9a5e479SAndreas Jaekel 		return (EINVAL);
1219e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
1220e9a5e479SAndreas Jaekel 	if ((q = ddi_get_soft_state(statep, minor)) == NULL) {
1221e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
12222bb8e5e2SAndreas Jaekel 		return (ENXIO);
1223e9a5e479SAndreas Jaekel 	}
12242bb8e5e2SAndreas Jaekel 	revent = 0;
12252bb8e5e2SAndreas Jaekel 	if ((events & POLLIN)) {
1226e9a5e479SAndreas Jaekel 		if (q->zq_oldest)
12272bb8e5e2SAndreas Jaekel 			revent |= POLLIN;
12282bb8e5e2SAndreas Jaekel 	}
12292bb8e5e2SAndreas Jaekel 	if (revent == 0) {
12302bb8e5e2SAndreas Jaekel 		if (!anyyet) {
1231e9a5e479SAndreas Jaekel 			*phpp = &q->zq_pollhead;
12322bb8e5e2SAndreas Jaekel 		}
12332bb8e5e2SAndreas Jaekel 	}
12342bb8e5e2SAndreas Jaekel 	*reventsp = revent;
1235e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
12362bb8e5e2SAndreas Jaekel 	return (0);
12372bb8e5e2SAndreas Jaekel }
12382bb8e5e2SAndreas Jaekel 
1239aea44f72SAndreas Jaekel /* ARGSUSED */
12402bb8e5e2SAndreas Jaekel static int
12412bb8e5e2SAndreas Jaekel zev_read(dev_t dev, struct uio *uio_p, cred_t *crep_p)
12422bb8e5e2SAndreas Jaekel {
1243e9a5e479SAndreas Jaekel 	minor_t minor;
12442bb8e5e2SAndreas Jaekel 	offset_t off;
12452bb8e5e2SAndreas Jaekel 	int ret = 0;
12469193e9c2SAndreas Jaekel 	zev_msg_t *msg;
12479193e9c2SAndreas Jaekel 	char *data;
1248e9a5e479SAndreas Jaekel 	zev_queue_t *q;
12492bb8e5e2SAndreas Jaekel 
1250e9a5e479SAndreas Jaekel 	minor = getminor(dev);
1251e9a5e479SAndreas Jaekel 	if (minor == ZEV_CONTROL_DEVICE_MINOR)
1252e9a5e479SAndreas Jaekel 		return (EINVAL);
1253e9a5e479SAndreas Jaekel 
12542bb8e5e2SAndreas Jaekel 	mutex_enter(&zev_mutex);
1255e9a5e479SAndreas Jaekel 	q = ddi_get_soft_state(statep, minor);
1256e9a5e479SAndreas Jaekel 	if (q == NULL) {
1257e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
1258e9a5e479SAndreas Jaekel 		return (ENXIO);
1259e9a5e479SAndreas Jaekel 	}
1260e9a5e479SAndreas Jaekel 	off = uio_p->uio_loffset;
1261e9a5e479SAndreas Jaekel 	msg = q->zq_oldest;
12624ca7dd5eSAndreas Jaekel 	while (msg == NULL) {
12634ca7dd5eSAndreas Jaekel 		if (!ddi_can_receive_sig()) {
12644ca7dd5eSAndreas Jaekel 			/*
12654ca7dd5eSAndreas Jaekel 			 * read() shouldn't block because this thread
12664ca7dd5eSAndreas Jaekel 			 * can't receive signals. (e.g., it might be
12674ca7dd5eSAndreas Jaekel 			 * torn down by exit() right now.)
12684ca7dd5eSAndreas Jaekel 			 */
12699193e9c2SAndreas Jaekel 			mutex_exit(&zev_mutex);
12709193e9c2SAndreas Jaekel 			return 0;
12712bb8e5e2SAndreas Jaekel 		}
12724ca7dd5eSAndreas Jaekel 		if (cv_wait_sig(&q->zq_condvar, &zev_mutex) == 0) {
12734ca7dd5eSAndreas Jaekel 			/* signal received. */
12744ca7dd5eSAndreas Jaekel 			mutex_exit(&zev_mutex);
12754ca7dd5eSAndreas Jaekel 			return EINTR;
12764ca7dd5eSAndreas Jaekel 		}
12774ca7dd5eSAndreas Jaekel 		msg = q->zq_oldest;
12784ca7dd5eSAndreas Jaekel 	}
12799193e9c2SAndreas Jaekel 	if (msg->size > uio_p->uio_resid) {
12809193e9c2SAndreas Jaekel 		mutex_exit(&zev_mutex);
12819193e9c2SAndreas Jaekel 		return E2BIG;
12822bb8e5e2SAndreas Jaekel 	}
128368a46c64SAndreas Jaekel 	while (msg && uio_p->uio_resid >= msg->size) {
12849193e9c2SAndreas Jaekel 		data = (char *)(msg + 1);
12859193e9c2SAndreas Jaekel 		ret = uiomove(data, msg->size, UIO_READ, uio_p);
12869193e9c2SAndreas Jaekel 		if (ret != 0) {
12872bb8e5e2SAndreas Jaekel 			mutex_exit(&zev_mutex);
128868a46c64SAndreas Jaekel 			cmn_err(CE_WARN, "zev: uiomove failed; messages lost");
12892bb8e5e2SAndreas Jaekel 			uio_p->uio_loffset = off;
12902bb8e5e2SAndreas Jaekel 			return (ret);
12912bb8e5e2SAndreas Jaekel 		}
1292e9a5e479SAndreas Jaekel 		q->zq_oldest = msg->next;
1293e9a5e479SAndreas Jaekel 		q->zq_bytes_read += msg->size;
1294e9a5e479SAndreas Jaekel 		q->zq_queue_len -= msg->size;
1295e9a5e479SAndreas Jaekel 		q->zq_queue_messages--;
1296e9a5e479SAndreas Jaekel 		msg->read++;
1297e9a5e479SAndreas Jaekel 		msg = q->zq_oldest;
129868a46c64SAndreas Jaekel 	}
1299e325fb13SAndreas Jaekel 	zev_queue_trim();
13009193e9c2SAndreas Jaekel 	cv_broadcast(&zev_condvar);
13019193e9c2SAndreas Jaekel 	mutex_exit(&zev_mutex);
13029193e9c2SAndreas Jaekel 	uio_p->uio_loffset = off;
13039193e9c2SAndreas Jaekel 	return 0;
13049193e9c2SAndreas Jaekel }
13052bb8e5e2SAndreas Jaekel 
1306aea44f72SAndreas Jaekel /* ARGSUSED */
13072bb8e5e2SAndreas Jaekel static int
13082bb8e5e2SAndreas Jaekel zev_close(dev_t dev, int flag, int otyp, cred_t *crepd)
13092bb8e5e2SAndreas Jaekel {
1310e9a5e479SAndreas Jaekel 	zev_queue_t *q;
1311e9a5e479SAndreas Jaekel 	int minor;
13122bb8e5e2SAndreas Jaekel 
1313e9a5e479SAndreas Jaekel 	minor = getminor(dev);
13142bb8e5e2SAndreas Jaekel 	if (otyp != OTYP_CHR)
13152bb8e5e2SAndreas Jaekel 		return (EINVAL);
1316e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
1317e9a5e479SAndreas Jaekel 	if ((q = ddi_get_soft_state(statep, minor)) == NULL) {
1318e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
1319e9a5e479SAndreas Jaekel 		return (ENXIO);
1320e9a5e479SAndreas Jaekel 	}
1321e9a5e479SAndreas Jaekel 	if (q->zq_busy != B_TRUE) {
1322e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
13232bb8e5e2SAndreas Jaekel 		return (EINVAL);
13242bb8e5e2SAndreas Jaekel 	}
1325e9a5e479SAndreas Jaekel 	q->zq_busy = B_FALSE;
1326e9a5e479SAndreas Jaekel 	if ((q->zq_flags & ZEV_FL_PERSISTENT) == 0)
1327e9a5e479SAndreas Jaekel 		zev_queue_release(q);
1328e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
13292bb8e5e2SAndreas Jaekel 	return (0);
13302bb8e5e2SAndreas Jaekel }
13312bb8e5e2SAndreas Jaekel 
1332aea44f72SAndreas Jaekel /* ARGSUSED */
13332bb8e5e2SAndreas Jaekel static int
13342bb8e5e2SAndreas Jaekel zev_open(dev_t *devp, int flag, int otyp, cred_t *credp)
13352bb8e5e2SAndreas Jaekel {
1336e9a5e479SAndreas Jaekel 	zev_queue_t *q;
1337e9a5e479SAndreas Jaekel 	minor_t minor;
13386450d95eSAndreas Jaekel 	char zq_name[ZEV_MAX_QUEUE_NAME_LEN];
13396450d95eSAndreas Jaekel 	int ret;
13402bb8e5e2SAndreas Jaekel 
1341e9a5e479SAndreas Jaekel 	minor = getminor(*devp);
13422bb8e5e2SAndreas Jaekel 	if (otyp != OTYP_CHR)
13432bb8e5e2SAndreas Jaekel 		return (EINVAL);
13442bb8e5e2SAndreas Jaekel 	if (drv_priv(credp) != 0)
13452bb8e5e2SAndreas Jaekel 		return (EPERM);
13466450d95eSAndreas Jaekel 	if (minor == ZEV_TMPQUEUE_DEVICE_MINOR) {
13476450d95eSAndreas Jaekel 		/* get control queue soft state to have dip */
13486450d95eSAndreas Jaekel 		if ((q = ddi_get_soft_state(statep,
13496450d95eSAndreas Jaekel 		                            ZEV_CONTROL_DEVICE_MINOR)) == NULL){
13506450d95eSAndreas Jaekel 			mutex_exit(&zev_mutex);
13516450d95eSAndreas Jaekel 			return (ENXIO);
13526450d95eSAndreas Jaekel 		}
13536450d95eSAndreas Jaekel 
13546450d95eSAndreas Jaekel 		/* create new temporary queue and return it. */
13556450d95eSAndreas Jaekel 
13566450d95eSAndreas Jaekel 		snprintf(zq_name, sizeof(zq_name),
13576450d95eSAndreas Jaekel 		         ZEV_TMPQUEUE_DEVICE_NAME ".%d", zev_tmpqueue_num++);
13586450d95eSAndreas Jaekel 
13591756dea7SAndreas Jaekel 		ret = zev_queue_new(&q, q->zq_dip, zq_name, 0,
13601756dea7SAndreas Jaekel 		                    ZEV_FL_INITIALLY_EMPTY);
13616450d95eSAndreas Jaekel 		if (ret) {
13626450d95eSAndreas Jaekel 			return ret;
13636450d95eSAndreas Jaekel 		}
13646450d95eSAndreas Jaekel 
13656450d95eSAndreas Jaekel 		q->zq_busy = B_TRUE;
13666450d95eSAndreas Jaekel 		*devp = makedevice(getmajor(*devp), q->zq_minor_number);
13676450d95eSAndreas Jaekel 		return 0;
13686450d95eSAndreas Jaekel 	}
1369e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
1370e9a5e479SAndreas Jaekel 	if ((q = ddi_get_soft_state(statep, minor)) == NULL) {
1371e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
1372e9a5e479SAndreas Jaekel 		return (ENXIO);
1373e9a5e479SAndreas Jaekel 	}
1374e9a5e479SAndreas Jaekel 	if (minor == ZEV_CONTROL_DEVICE_MINOR) {
1375e9a5e479SAndreas Jaekel 		/* control device may be used in parallel */
1376e9a5e479SAndreas Jaekel 		q->zq_busy = B_TRUE;
1377e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
1378e9a5e479SAndreas Jaekel 		return 0;
1379e9a5e479SAndreas Jaekel 	}
1380e9a5e479SAndreas Jaekel 	if (q->zq_busy == B_TRUE) {
1381e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
13822bb8e5e2SAndreas Jaekel 		return (EBUSY);
13832bb8e5e2SAndreas Jaekel 	}
1384e9a5e479SAndreas Jaekel 	q->zq_busy = B_TRUE;	/* can only be opened exclusively */
1385e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
13862bb8e5e2SAndreas Jaekel 	return (0);
13872bb8e5e2SAndreas Jaekel }
13882bb8e5e2SAndreas Jaekel 
13892bb8e5e2SAndreas Jaekel static struct cb_ops zev_cb_ops = {
13902bb8e5e2SAndreas Jaekel 	zev_open,		/* open */
13912bb8e5e2SAndreas Jaekel 	zev_close,		/* close */
13922bb8e5e2SAndreas Jaekel 	nodev,			/* strategy */
13932bb8e5e2SAndreas Jaekel 	nodev,			/* print */
13942bb8e5e2SAndreas Jaekel 	nodev,			/* dump */
13952bb8e5e2SAndreas Jaekel 	zev_read,		/* read */
13962bb8e5e2SAndreas Jaekel 	nodev,			/* write */
13972bb8e5e2SAndreas Jaekel 	zev_ioctl,		/* ioctl */
13982bb8e5e2SAndreas Jaekel 	nodev,			/* devmap */
13992bb8e5e2SAndreas Jaekel 	nodev,			/* mmap */
14002bb8e5e2SAndreas Jaekel 	nodev,			/* segmap */
14012bb8e5e2SAndreas Jaekel 	zev_chpoll,		/* chpoll */
14022bb8e5e2SAndreas Jaekel 	ddi_prop_op,		/* prop_op */
14032bb8e5e2SAndreas Jaekel 	NULL,			/* streamtab */
14042bb8e5e2SAndreas Jaekel 	D_MP | D_64BIT,		/* cb_flag */
14052bb8e5e2SAndreas Jaekel 	CB_REV,			/* cb_rev */
14062bb8e5e2SAndreas Jaekel 	nodev,			/* aread */
14072bb8e5e2SAndreas Jaekel 	nodev,			/* awrite */
14082bb8e5e2SAndreas Jaekel };
14092bb8e5e2SAndreas Jaekel 
14102bb8e5e2SAndreas Jaekel static void
14112bb8e5e2SAndreas Jaekel zev_free_instance(dev_info_t *dip)
14122bb8e5e2SAndreas Jaekel {
14132bb8e5e2SAndreas Jaekel 	int instance;
1414e9a5e479SAndreas Jaekel 	zev_queue_t *q;
14154ca7dd5eSAndreas Jaekel 	int i;
1416e9a5e479SAndreas Jaekel 
14172bb8e5e2SAndreas Jaekel 	instance = ddi_get_instance(dip);
1418e9a5e479SAndreas Jaekel 	if (instance != 0) {
1419e9a5e479SAndreas Jaekel 		cmn_err(CE_WARN, "zev: tried to free instance != 0 (%d)",
1420e9a5e479SAndreas Jaekel 		        instance);
1421e9a5e479SAndreas Jaekel 		return;
14222bb8e5e2SAndreas Jaekel 	}
1423e9a5e479SAndreas Jaekel 
1424e9a5e479SAndreas Jaekel 	ddi_remove_minor_node(dip, NULL);
1425d5b96be3SAndreas Jaekel 	devfs_clean(ddi_root_node() ? ddi_root_node() : dip,
1426d5b96be3SAndreas Jaekel 	            NULL, DV_CLEAN_FORCE);
1427e9a5e479SAndreas Jaekel 
1428e9a5e479SAndreas Jaekel 	/* stop pollwakeup thread */
1429e9a5e479SAndreas Jaekel 	zev_wakeup_thread_run = 0;
1430e9a5e479SAndreas Jaekel 	if (zev_poll_wakeup_thread != NULL) {
1431e9a5e479SAndreas Jaekel 		thread_join(zev_poll_wakeup_thread->t_did);
1432e9a5e479SAndreas Jaekel 		zev_poll_wakeup_thread = NULL;
1433e9a5e479SAndreas Jaekel 	}
1434e9a5e479SAndreas Jaekel 
1435e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
1436e9a5e479SAndreas Jaekel 
1437e9a5e479SAndreas Jaekel 	/* remove "ctrl" dummy queue */
1438e9a5e479SAndreas Jaekel 	q = ddi_get_soft_state(statep, ZEV_CONTROL_DEVICE_MINOR);
1439e9a5e479SAndreas Jaekel 	if (q) {
1440e9a5e479SAndreas Jaekel 		ddi_soft_state_free(statep, ZEV_CONTROL_DEVICE_MINOR);
1441e9a5e479SAndreas Jaekel 		ZEV_MEM_SUB(sizeof(zev_queue_t));
1442e9a5e479SAndreas Jaekel 	}
1443e9a5e479SAndreas Jaekel 
1444e9a5e479SAndreas Jaekel 	/* remove all other queues */
14454ca7dd5eSAndreas Jaekel 	for (i = ZEV_MINOR_MIN; i <= ZEV_MINOR_MAX; i++) {
14464ca7dd5eSAndreas Jaekel 		q = zev_queues[i- ZEV_MINOR_MIN];
14474ca7dd5eSAndreas Jaekel 		if (!q)
14484ca7dd5eSAndreas Jaekel 			continue;
1449e9a5e479SAndreas Jaekel 		ASSERT(q->zq_refcnt == 1);
1450e9a5e479SAndreas Jaekel 		zev_queue_release(q);
1451e9a5e479SAndreas Jaekel 	}
1452e9a5e479SAndreas Jaekel 	zev_queue_trim();
14534ca7dd5eSAndreas Jaekel 	bzero(&zev_queues, sizeof(zev_queues));
1454e9a5e479SAndreas Jaekel 
1455e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
1456e9a5e479SAndreas Jaekel 
14572bb8e5e2SAndreas Jaekel }
14582bb8e5e2SAndreas Jaekel 
14592bb8e5e2SAndreas Jaekel static int
14602bb8e5e2SAndreas Jaekel zev_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
14612bb8e5e2SAndreas Jaekel {
14622bb8e5e2SAndreas Jaekel 	int instance;
1463e9a5e479SAndreas Jaekel 	zev_queue_t *q;
1464e9a5e479SAndreas Jaekel 
14652bb8e5e2SAndreas Jaekel 	/* called once per instance with DDI_DETACH,
14662bb8e5e2SAndreas Jaekel 	   may be called to suspend */
14672bb8e5e2SAndreas Jaekel 	switch (cmd) {
14682bb8e5e2SAndreas Jaekel 	case DDI_DETACH:
14692bb8e5e2SAndreas Jaekel 		/* instance busy? */
14702bb8e5e2SAndreas Jaekel 		instance = ddi_get_instance(dip);
1471e9a5e479SAndreas Jaekel 		if (instance != 0) {	/* hardcoded in zev.conf */
1472e9a5e479SAndreas Jaekel 			/* this module only supports one instance. */
14732fcfe2d7SAndreas Jaekel 			return (DDI_FAILURE);
14742bb8e5e2SAndreas Jaekel 		}
1475e9a5e479SAndreas Jaekel 
1476e9a5e479SAndreas Jaekel 		mutex_enter(&zev_mutex);
1477e9a5e479SAndreas Jaekel 		if (!zev_attached) {
1478e9a5e479SAndreas Jaekel 			mutex_exit(&zev_mutex);
1479e9a5e479SAndreas Jaekel 			return (DDI_FAILURE);
1480e9a5e479SAndreas Jaekel 		}
1481e9a5e479SAndreas Jaekel 
1482e9a5e479SAndreas Jaekel 		/* check "ctrl" queue to see if t is busy */
1483e9a5e479SAndreas Jaekel 		q = ddi_get_soft_state(statep, ZEV_CONTROL_DEVICE_MINOR);
1484e9a5e479SAndreas Jaekel 		if (q == NULL) {
1485e9a5e479SAndreas Jaekel 			mutex_exit(&zev_mutex);
1486e9a5e479SAndreas Jaekel 			return (DDI_FAILURE);
1487e9a5e479SAndreas Jaekel 		}
1488e9a5e479SAndreas Jaekel 		if (q->zq_busy) {
1489e9a5e479SAndreas Jaekel 			mutex_exit(&zev_mutex);
1490e9a5e479SAndreas Jaekel 			return (DDI_FAILURE);
1491e9a5e479SAndreas Jaekel 		}
1492e9a5e479SAndreas Jaekel 		/* are there any queues? */
14934ca7dd5eSAndreas Jaekel 		if (zev_queue_cnt > 0) {
1494e9a5e479SAndreas Jaekel 			mutex_exit(&zev_mutex);
1495e9a5e479SAndreas Jaekel 			return (DDI_FAILURE);
1496e9a5e479SAndreas Jaekel 		}
1497e9a5e479SAndreas Jaekel 
1498e9a5e479SAndreas Jaekel 		zev_attached = B_FALSE;
1499e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
1500e9a5e479SAndreas Jaekel 
1501e9a5e479SAndreas Jaekel 		/* switch ZFS event callbacks back to default */
1502e9a5e479SAndreas Jaekel 		rw_enter(&rz_zev_rwlock, RW_WRITER);
1503e9a5e479SAndreas Jaekel 		rz_zev_callbacks = rz_zev_default_callbacks;
15045e286361SAndreas Jaekel 		rz_zev_set_active(B_FALSE);
1505e9a5e479SAndreas Jaekel 		rw_exit(&rz_zev_rwlock);
1506e9a5e479SAndreas Jaekel 
1507e9a5e479SAndreas Jaekel 		/* no thread is inside of the callbacks anymore. */
1508e9a5e479SAndreas Jaekel 
15092bb8e5e2SAndreas Jaekel 		/* free resources allocated for this instance */
15102bb8e5e2SAndreas Jaekel 		zev_free_instance(dip);
15115e286361SAndreas Jaekel 		zev_chksum_fini();
15124ca7dd5eSAndreas Jaekel #if 0
1513e9a5e479SAndreas Jaekel 		cmn_err(CE_WARN, "zev: allocated memory at detach: %" PRIu64,
1514e9a5e479SAndreas Jaekel 			zev_memory_allocated - zev_memory_freed);
15154ca7dd5eSAndreas Jaekel #endif
15162bb8e5e2SAndreas Jaekel 		return (DDI_SUCCESS);
15172bb8e5e2SAndreas Jaekel 	case DDI_SUSPEND:
15182bb8e5e2SAndreas Jaekel 		/* kernel must not suspend zev devices while ZFS is running */
15192bb8e5e2SAndreas Jaekel 		return (DDI_FAILURE);
15202bb8e5e2SAndreas Jaekel 	default:
15212bb8e5e2SAndreas Jaekel 		return (DDI_FAILURE);
15222bb8e5e2SAndreas Jaekel 	}
15232bb8e5e2SAndreas Jaekel }
15242bb8e5e2SAndreas Jaekel 
15252bb8e5e2SAndreas Jaekel static int
15262bb8e5e2SAndreas Jaekel zev_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
15272bb8e5e2SAndreas Jaekel {
15282bb8e5e2SAndreas Jaekel 	/* called once per instance with DDI_ATTACH,
15292bb8e5e2SAndreas Jaekel 	   may be called to resume */
15302bb8e5e2SAndreas Jaekel 	int instance;
1531e9a5e479SAndreas Jaekel 	int error;
1532e9a5e479SAndreas Jaekel 	zev_queue_t *q;
15332bb8e5e2SAndreas Jaekel 	switch (cmd) {
15342bb8e5e2SAndreas Jaekel 	case DDI_ATTACH:
1535e9a5e479SAndreas Jaekel 		/* create instance state */
15362bb8e5e2SAndreas Jaekel 		instance = ddi_get_instance(dip);
1537e9a5e479SAndreas Jaekel 		if (instance != 0) {	/* hardcoded in zev.conf */
1538e9a5e479SAndreas Jaekel 			/* this module only supports one instance. */
15392bb8e5e2SAndreas Jaekel 			return (DDI_FAILURE);
15402bb8e5e2SAndreas Jaekel 		}
1541e9a5e479SAndreas Jaekel 
1542e9a5e479SAndreas Jaekel 		mutex_enter(&zev_mutex);
1543e9a5e479SAndreas Jaekel 		if (zev_attached) {
1544e9a5e479SAndreas Jaekel 			mutex_exit(&zev_mutex);
15452bb8e5e2SAndreas Jaekel 			return (DDI_FAILURE);
15462bb8e5e2SAndreas Jaekel 		}
1547e9a5e479SAndreas Jaekel 		if (ddi_soft_state_zalloc(statep, ZEV_CONTROL_DEVICE_MINOR) !=
1548e9a5e479SAndreas Jaekel 		    DDI_SUCCESS) {
1549e9a5e479SAndreas Jaekel 			mutex_exit(&zev_mutex);
1550e9a5e479SAndreas Jaekel 			return (DDI_FAILURE);
1551e9a5e479SAndreas Jaekel 		}
1552e9a5e479SAndreas Jaekel 		ZEV_MEM_ADD(sizeof(zev_queue_t));
1553e9a5e479SAndreas Jaekel 		zev_attached = B_TRUE;
1554e9a5e479SAndreas Jaekel 
1555e9a5e479SAndreas Jaekel 		/* init queue list */
15564ca7dd5eSAndreas Jaekel 		bzero(&zev_queues, sizeof(zev_queues));
1557e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
1558e9a5e479SAndreas Jaekel 
1559e9a5e479SAndreas Jaekel 		/* create a dummy queue for management of "ctrl" */
1560e9a5e479SAndreas Jaekel 
1561e9a5e479SAndreas Jaekel 		q = ddi_get_soft_state(statep, ZEV_CONTROL_DEVICE_MINOR);
1562e9a5e479SAndreas Jaekel 		q->zq_dip = dip;
1563e9a5e479SAndreas Jaekel 		q->zq_refcnt = 1;
1564e9a5e479SAndreas Jaekel 		q->zq_busy = B_FALSE;
1565e9a5e479SAndreas Jaekel 		q->zq_minor_number = ZEV_CONTROL_DEVICE_MINOR;
1566e9a5e479SAndreas Jaekel 		q->zq_flags = ZEV_FL_PERSISTENT;
1567e9a5e479SAndreas Jaekel 		strcpy(q->zq_name, ZEV_CONTROL_DEVICE_NAME);
1568e9a5e479SAndreas Jaekel 
1569e9a5e479SAndreas Jaekel 		/* create device node for "ctrl" */
1570e9a5e479SAndreas Jaekel 		if (ddi_create_minor_node(dip, ZEV_CONTROL_DEVICE_NAME,
1571e9a5e479SAndreas Jaekel 		    S_IFCHR, ZEV_CONTROL_DEVICE_MINOR,
1572e9a5e479SAndreas Jaekel 		    DDI_PSEUDO, 0) == DDI_FAILURE) {
1573e9a5e479SAndreas Jaekel 			goto fail;
1574e9a5e479SAndreas Jaekel 		}
1575e9a5e479SAndreas Jaekel 
1576e9a5e479SAndreas Jaekel 		/* note: intentionally not adding ctrl queue to queue list. */
1577e9a5e479SAndreas Jaekel 
15786450d95eSAndreas Jaekel 		/* create device node for "tmpqueue" */
15796450d95eSAndreas Jaekel 		if (ddi_create_minor_node(dip, ZEV_TMPQUEUE_DEVICE_NAME,
15806450d95eSAndreas Jaekel 		    S_IFCHR, ZEV_TMPQUEUE_DEVICE_MINOR,
15816450d95eSAndreas Jaekel 		    DDI_PSEUDO, 0) == DDI_FAILURE) {
15826450d95eSAndreas Jaekel 			goto fail;
15836450d95eSAndreas Jaekel 		}
15846450d95eSAndreas Jaekel 
1585e9a5e479SAndreas Jaekel 		/* default queue */
1586e9a5e479SAndreas Jaekel 		error = zev_queue_new(&q, dip,
1587e9a5e479SAndreas Jaekel 				      ZEV_DEFAULT_QUEUE_NAME,
1588e9a5e479SAndreas Jaekel 				      ZEV_MAX_QUEUE_LEN,
1589e9a5e479SAndreas Jaekel 				      ZEV_FL_BLOCK_WHILE_QUEUE_FULL|
1590e9a5e479SAndreas Jaekel 		                      ZEV_FL_PERSISTENT);
1591e9a5e479SAndreas Jaekel 		if (error)
1592e9a5e479SAndreas Jaekel 			goto fail;
1593e9a5e479SAndreas Jaekel 
1594e9a5e479SAndreas Jaekel 		/* start pollwakeup thread */
1595e9a5e479SAndreas Jaekel 		zev_wakeup_thread_run = 1;
1596e9a5e479SAndreas Jaekel 		zev_poll_wakeup_thread = thread_create(NULL, 0,
1597e9a5e479SAndreas Jaekel 		    zev_poll_wakeup_thread_main, NULL, 0, &p0,
1598e9a5e479SAndreas Jaekel 		    TS_RUN, minclsyspri);
1599e9a5e479SAndreas Jaekel 
16002bb8e5e2SAndreas Jaekel 		ddi_report_dev(dip);
1601e9a5e479SAndreas Jaekel 
16025e286361SAndreas Jaekel 		zev_chksum_init();
16035e286361SAndreas Jaekel 
1604e9a5e479SAndreas Jaekel 		/* switch ZFS event callbacks to zev module callbacks */
1605e9a5e479SAndreas Jaekel 		rw_enter(&rz_zev_rwlock, RW_WRITER);
1606e9a5e479SAndreas Jaekel 		rz_zev_callbacks = &zev_callbacks;
16075e286361SAndreas Jaekel 		rz_zev_set_active(B_TRUE);
1608e9a5e479SAndreas Jaekel 		rw_exit(&rz_zev_rwlock);
1609e9a5e479SAndreas Jaekel 
16102bb8e5e2SAndreas Jaekel 		return (DDI_SUCCESS);
16112bb8e5e2SAndreas Jaekel 	case DDI_RESUME:
16122bb8e5e2SAndreas Jaekel 		/* suspendeding zev devices should never happen */
16132bb8e5e2SAndreas Jaekel 		return (DDI_SUCCESS);
16142bb8e5e2SAndreas Jaekel 	default:
16152bb8e5e2SAndreas Jaekel 		return (DDI_FAILURE);
16162bb8e5e2SAndreas Jaekel 	}
1617e9a5e479SAndreas Jaekel fail:
1618e9a5e479SAndreas Jaekel 	cmn_err(CE_WARN, "zev: attach failed");
1619e9a5e479SAndreas Jaekel 	zev_free_instance(dip);
1620e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
1621e9a5e479SAndreas Jaekel 	zev_attached = B_FALSE;
1622e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
1623e9a5e479SAndreas Jaekel 	return (DDI_FAILURE);
16242bb8e5e2SAndreas Jaekel }
16252bb8e5e2SAndreas Jaekel 
1626aea44f72SAndreas Jaekel /* ARGSUSED */
16272bb8e5e2SAndreas Jaekel static int
16282bb8e5e2SAndreas Jaekel zev_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp)
16292bb8e5e2SAndreas Jaekel {
1630e9a5e479SAndreas Jaekel 	minor_t minor;
1631e9a5e479SAndreas Jaekel 	zev_queue_t *q;
1632e9a5e479SAndreas Jaekel 
1633e9a5e479SAndreas Jaekel 	/* arg is dev_t */
1634e9a5e479SAndreas Jaekel 	minor = getminor((dev_t)arg);
1635e9a5e479SAndreas Jaekel 	mutex_enter(&zev_mutex);
1636e9a5e479SAndreas Jaekel 	q = ddi_get_soft_state(statep, minor);
1637e9a5e479SAndreas Jaekel 	if (q == NULL) {
1638e9a5e479SAndreas Jaekel 		*resultp = NULL;
1639e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
1640e9a5e479SAndreas Jaekel 		return (DDI_FAILURE);
1641e9a5e479SAndreas Jaekel 	}
1642e9a5e479SAndreas Jaekel 
16432bb8e5e2SAndreas Jaekel 	switch (infocmd) {
16442bb8e5e2SAndreas Jaekel 	case DDI_INFO_DEVT2DEVINFO:
1645e9a5e479SAndreas Jaekel 		*resultp = q->zq_dip;
1646e9a5e479SAndreas Jaekel 		break;
16472bb8e5e2SAndreas Jaekel 	case DDI_INFO_DEVT2INSTANCE:
1648e9a5e479SAndreas Jaekel 		*resultp = (void *)(uintptr_t)ddi_get_instance(q->zq_dip);
1649e9a5e479SAndreas Jaekel 		break;
1650e9a5e479SAndreas Jaekel 	default:
1651e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
16522bb8e5e2SAndreas Jaekel 		return (DDI_FAILURE);
16532bb8e5e2SAndreas Jaekel 	}
1654e9a5e479SAndreas Jaekel 	mutex_exit(&zev_mutex);
1655e9a5e479SAndreas Jaekel 	return (DDI_SUCCESS);
16562bb8e5e2SAndreas Jaekel }
16572bb8e5e2SAndreas Jaekel 
16582bb8e5e2SAndreas Jaekel static struct dev_ops zev_dev_ops = {
16592bb8e5e2SAndreas Jaekel 	DEVO_REV,			/* driver build revision */
16602bb8e5e2SAndreas Jaekel 	0,				/* driver reference count */
16612bb8e5e2SAndreas Jaekel 	zev_getinfo,			/* getinfo */
16622bb8e5e2SAndreas Jaekel 	nulldev,			/* identify (obsolete) */
16632bb8e5e2SAndreas Jaekel 	nulldev,			/* probe (search for devices) */
16642bb8e5e2SAndreas Jaekel 	zev_attach,			/* attach */
16652bb8e5e2SAndreas Jaekel 	zev_detach,			/* detach */
16662bb8e5e2SAndreas Jaekel 	nodev,				/* reset (obsolete, use quiesce) */
16672bb8e5e2SAndreas Jaekel 	&zev_cb_ops,			/* character and block device ops */
16682bb8e5e2SAndreas Jaekel 	NULL,				/* bus driver ops */
16692bb8e5e2SAndreas Jaekel 	NULL,				/* power management, not needed */
16702bb8e5e2SAndreas Jaekel 	ddi_quiesce_not_needed,		/* quiesce */
16712bb8e5e2SAndreas Jaekel };
16722bb8e5e2SAndreas Jaekel 
16732bb8e5e2SAndreas Jaekel static struct modldrv zev_modldrv = {
16742bb8e5e2SAndreas Jaekel 	&mod_driverops,			/* all loadable modules use this */
1675c62d8367SAndreas Jaekel 	"ZFS event provider, v"
1676dc1d6f1aSAndreas Jaekel 		XSTRING(ZEV_MAJOR_VERSION) "."
1677dc1d6f1aSAndreas Jaekel 		XSTRING(ZEV_MINOR_VERSION),
1678c62d8367SAndreas Jaekel 					/* driver name and version info */
16792bb8e5e2SAndreas Jaekel 	&zev_dev_ops			/* ops method pointers */
16802bb8e5e2SAndreas Jaekel };
16812bb8e5e2SAndreas Jaekel 
16822bb8e5e2SAndreas Jaekel static struct modlinkage zev_modlinkage = {
16832bb8e5e2SAndreas Jaekel 	MODREV_1,	/* fixed value */
16842bb8e5e2SAndreas Jaekel 	{
16852bb8e5e2SAndreas Jaekel 		&zev_modldrv,	/* driver linkage structure */
16862bb8e5e2SAndreas Jaekel 		NULL		/* list terminator */
16872bb8e5e2SAndreas Jaekel 	}
16882bb8e5e2SAndreas Jaekel };
16892bb8e5e2SAndreas Jaekel 
16902bb8e5e2SAndreas Jaekel int
16912bb8e5e2SAndreas Jaekel _init(void)
16922bb8e5e2SAndreas Jaekel {
16932bb8e5e2SAndreas Jaekel 	int error;
16942bb8e5e2SAndreas Jaekel 
1695e9a5e479SAndreas Jaekel 	if ((error = ddi_soft_state_init(&statep, sizeof(zev_queue_t), 1)) != 0)
16962bb8e5e2SAndreas Jaekel 		return (error);
1697e9a5e479SAndreas Jaekel 	zev_attached = B_FALSE;
1698e9a5e479SAndreas Jaekel 
1699e9a5e479SAndreas Jaekel 	zev_queue_head = NULL;
1700e9a5e479SAndreas Jaekel 	zev_queue_tail = NULL;
1701e9a5e479SAndreas Jaekel 	zev_queue_len = 0;
1702e9a5e479SAndreas Jaekel 	zev_muted_pools_head = NULL;
1703e9a5e479SAndreas Jaekel 	zev_memory_allocated = 0;
1704e9a5e479SAndreas Jaekel 	zev_memory_freed = 0;
17054ca7dd5eSAndreas Jaekel 	zev_queue_cnt = 0;
170607bff397SAndreas Jaekel 	zev_have_blocking_queues = 1;
17072bb8e5e2SAndreas Jaekel 
17082bb8e5e2SAndreas Jaekel 	mutex_init(&zev_mutex, NULL, MUTEX_DRIVER, NULL);
17092bb8e5e2SAndreas Jaekel 	cv_init(&zev_condvar, NULL, CV_DRIVER, NULL);
17102bb8e5e2SAndreas Jaekel 	rw_init(&zev_pool_list_rwlock, NULL, RW_DRIVER, NULL);
171101c2c787SAndreas Jaekel 	mutex_init(&zev_mark_id_mutex, NULL, MUTEX_DRIVER, NULL);
171201c2c787SAndreas Jaekel 	zev_mark_id = gethrtime();
17134ca7dd5eSAndreas Jaekel 	mutex_init(&zev_queue_msg_mutex, NULL, MUTEX_DRIVER, NULL);
1714e9a5e479SAndreas Jaekel 	zev_msg_sequence_number = gethrtime();
17152bb8e5e2SAndreas Jaekel 	bzero(&zev_statistics, sizeof(zev_statistics));
1716e9a5e479SAndreas Jaekel 	bzero(&zev_pollhead, sizeof(zev_pollhead));
17174ca7dd5eSAndreas Jaekel 	bzero(&zev_queues, sizeof(zev_queues));
17182bb8e5e2SAndreas Jaekel 	zev_statistics.zev_max_queue_len = ZEV_MAX_QUEUE_LEN;
17192bb8e5e2SAndreas Jaekel 	if (zev_ioc_mute_pool("zg0")) {
17202bb8e5e2SAndreas Jaekel 		cmn_err(CE_WARN, "zev: could not init mute list");
17212bb8e5e2SAndreas Jaekel 		goto FAIL;
17222bb8e5e2SAndreas Jaekel 	}
17232bb8e5e2SAndreas Jaekel 
17242bb8e5e2SAndreas Jaekel 	if ((error = mod_install(&zev_modlinkage)) != 0) {
17252bb8e5e2SAndreas Jaekel 		cmn_err(CE_WARN, "zev: could not install module");
17262bb8e5e2SAndreas Jaekel 		goto FAIL;
17272bb8e5e2SAndreas Jaekel 	}
1728231caef2SAndreas Jaekel 
17292bb8e5e2SAndreas Jaekel 	return (0);
17302bb8e5e2SAndreas Jaekel FAIL:
17312bb8e5e2SAndreas Jaekel 	/* free resources */
1732e9a5e479SAndreas Jaekel 	cmn_err(CE_WARN, "zev: _init failed");
17332bb8e5e2SAndreas Jaekel 	mutex_destroy(&zev_mutex);
17342bb8e5e2SAndreas Jaekel 	ddi_soft_state_fini(&statep);
17352bb8e5e2SAndreas Jaekel 	return (error);
17362bb8e5e2SAndreas Jaekel }
17372bb8e5e2SAndreas Jaekel 
17382bb8e5e2SAndreas Jaekel int
17392bb8e5e2SAndreas Jaekel _info(struct modinfo *modinfop)
17402bb8e5e2SAndreas Jaekel {
17412bb8e5e2SAndreas Jaekel 	return (mod_info(&zev_modlinkage, modinfop));
17422bb8e5e2SAndreas Jaekel }
17432bb8e5e2SAndreas Jaekel 
17442bb8e5e2SAndreas Jaekel int
17452bb8e5e2SAndreas Jaekel _fini(void)
17462bb8e5e2SAndreas Jaekel {
17472bb8e5e2SAndreas Jaekel 	int error = 0;
17489193e9c2SAndreas Jaekel 	zev_msg_t *msg;
17492bb8e5e2SAndreas Jaekel 	zev_pool_list_entry_t *pe, *npe;
17502bb8e5e2SAndreas Jaekel 
17512bb8e5e2SAndreas Jaekel 	mutex_enter(&zev_mutex);
1752e9a5e479SAndreas Jaekel 	if (zev_attached == B_TRUE) {
17532bb8e5e2SAndreas Jaekel 		mutex_exit(&zev_mutex);
17542bb8e5e2SAndreas Jaekel 		return (SET_ERROR(EBUSY));
17552bb8e5e2SAndreas Jaekel 	}
17564ca7dd5eSAndreas Jaekel 	if (zev_queue_cnt != 0) {
1757e9a5e479SAndreas Jaekel 		/* should never happen */
1758e9a5e479SAndreas Jaekel 		mutex_exit(&zev_mutex);
1759e9a5e479SAndreas Jaekel 		return (SET_ERROR(EBUSY));
1760e9a5e479SAndreas Jaekel 	}
1761e9a5e479SAndreas Jaekel 
1762e9a5e479SAndreas Jaekel 	/*
1763e9a5e479SAndreas Jaekel 	 * avoid deadlock if event list is full: make sure threads currently
1764e9a5e479SAndreas Jaekel 	 * blocking on the event list can append their event and then release
1765e9a5e479SAndreas Jaekel 	 * rz_zev_rwlock.  Since there should be no queues left when we
1766e9a5e479SAndreas Jaekel 	 * reach this point we can simply empty the event list and then
1767e9a5e479SAndreas Jaekel 	 * wake everybody.
1768e9a5e479SAndreas Jaekel 	 */
1769e9a5e479SAndreas Jaekel 	while (zev_queue_head) {
1770e9a5e479SAndreas Jaekel 		msg = zev_queue_head;
1771e9a5e479SAndreas Jaekel 		zev_queue_head = msg->next;
17725e286361SAndreas Jaekel 		zev_free(msg, sizeof(*msg) + msg->size);
1773e9a5e479SAndreas Jaekel 	}
1774e9a5e479SAndreas Jaekel 	cv_broadcast(&zev_condvar);
17752bb8e5e2SAndreas Jaekel 	mutex_exit(&zev_mutex);
17762bb8e5e2SAndreas Jaekel 
1777e9a5e479SAndreas Jaekel 	/* switch ZFS event callbacks back to default (again) */
1778231caef2SAndreas Jaekel 	rw_enter(&rz_zev_rwlock, RW_WRITER);
1779231caef2SAndreas Jaekel 	rz_zev_callbacks = rz_zev_default_callbacks;
17805e286361SAndreas Jaekel 	rz_zev_set_active(B_FALSE);
1781231caef2SAndreas Jaekel 	rw_exit(&rz_zev_rwlock);
1782231caef2SAndreas Jaekel 
17832bb8e5e2SAndreas Jaekel 	/* no thread is inside of the callbacks anymore.  Safe to remove. */
1784e9a5e479SAndreas Jaekel 
1785e9a5e479SAndreas Jaekel 	/* unload module callbacks */
17862bb8e5e2SAndreas Jaekel 	if ((error = mod_remove(&zev_modlinkage)) != 0) {
17872bb8e5e2SAndreas Jaekel 		cmn_err(CE_WARN, "mod_remove failed: %d", error);
17882bb8e5e2SAndreas Jaekel 		return (error);
17892bb8e5e2SAndreas Jaekel 	}
17902bb8e5e2SAndreas Jaekel 
17912bb8e5e2SAndreas Jaekel 	/* free resources */
17922bb8e5e2SAndreas Jaekel 	mutex_enter(&zev_mutex);
17939193e9c2SAndreas Jaekel 	while (zev_queue_head) {
17949193e9c2SAndreas Jaekel 		msg = zev_queue_head;
17959193e9c2SAndreas Jaekel 		zev_queue_head = msg->next;
17965e286361SAndreas Jaekel 		zev_free(msg, sizeof(*msg) + msg->size);
17972bb8e5e2SAndreas Jaekel 	}
17982bb8e5e2SAndreas Jaekel 	mutex_exit(&zev_mutex);
17992bb8e5e2SAndreas Jaekel 	rw_enter(&zev_pool_list_rwlock, RW_WRITER);
18002bb8e5e2SAndreas Jaekel 	pe = zev_muted_pools_head;
18012bb8e5e2SAndreas Jaekel 	while (pe) {
18022bb8e5e2SAndreas Jaekel 		npe = pe;
18032bb8e5e2SAndreas Jaekel 		pe = pe->next;
18045e286361SAndreas Jaekel 		zev_free(npe, sizeof(*npe));
18052bb8e5e2SAndreas Jaekel 	}
18062bb8e5e2SAndreas Jaekel 	rw_exit(&zev_pool_list_rwlock);
18072bb8e5e2SAndreas Jaekel 	ddi_soft_state_fini(&statep);
18082bb8e5e2SAndreas Jaekel 	rw_destroy(&zev_pool_list_rwlock);
18092bb8e5e2SAndreas Jaekel 	cv_destroy(&zev_condvar);
18102bb8e5e2SAndreas Jaekel 	mutex_destroy(&zev_mutex);
181101c2c787SAndreas Jaekel 	mutex_destroy(&zev_mark_id_mutex);
18124ca7dd5eSAndreas Jaekel 	mutex_destroy(&zev_queue_msg_mutex);
18132bb8e5e2SAndreas Jaekel 
18142bb8e5e2SAndreas Jaekel 	return (0);
18152bb8e5e2SAndreas Jaekel }
18162bb8e5e2SAndreas Jaekel 
1817