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 *
zev_alloc(ssize_t sz)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 *
zev_zalloc(ssize_t sz)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
zev_free(void * ptr,ssize_t sz)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
zev_update_blockflag(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
zev_queue_cmp(const void * a,const void * b)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
zev_queue_trim(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
zev_queue_hold(zev_queue_t * q)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
zev_queue_release(zev_queue_t * q)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
zev_queue_new(zev_queue_t ** queue,dev_info_t * dip,char * name,uint64_t max_queue_len,uint16_t flags)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
zev_poll_wakeup(boolean_t flush_all)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
zev_poll_wakeup_thread_main(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
zev_ioc_mute_pool(char * poolname)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
zev_ioc_unmute_pool(char * poolname)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
zev_skip_pool(objset_t * os)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
zev_skip_fs(zfsvfs_t * fs)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
zev_update_statistics(int op,zev_statistics_t * stat)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
zev_queue_message(int op,zev_msg_t * msg)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
zev_queue_error(int op,char * fmt,...)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
zev_find_queue(zev_queue_t ** out,zev_queue_t * req_q,zev_queue_name_t * name)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
zev_ioc_get_queue_statistics(zev_queue_t * req_q,intptr_t arg,int mode)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
zev_ioc_set_queue_properties(zev_queue_t * req_q,intptr_t arg,int mode)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
zev_ioc_get_queue_properties(zev_queue_t * req_q,intptr_t arg,int mode)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
zev_ioc_add_queue(zev_queue_t * req_q,intptr_t arg,int mode)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
zev_ioc_remove_queue(zev_queue_t * req_q,intptr_t arg,int mode)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
zev_ioc_get_debug_info(zev_queue_t * req_q,intptr_t arg,int mode)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
zev_ioc_get_queue_list(zev_queue_t * req_q,intptr_t arg,int mode)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
zev_ioc_set_max_queue_len(zev_queue_t * req_q,intptr_t arg,int mode)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
zev_ioc_get_zev_version(intptr_t arg,int mode)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
zev_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)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
zev_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)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
zev_read(dev_t dev,struct uio * uio_p,cred_t * crep_p)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
zev_close(dev_t dev,int flag,int otyp,cred_t * crepd)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
zev_open(dev_t * devp,int flag,int otyp,cred_t * credp)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
zev_free_instance(dev_info_t * dip)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
zev_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
zev_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
zev_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** resultp)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
_init(void)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
_info(struct modinfo * modinfop)17392bb8e5e2SAndreas Jaekel _info(struct modinfo *modinfop)
17402bb8e5e2SAndreas Jaekel {
17412bb8e5e2SAndreas Jaekel return (mod_info(&zev_modlinkage, modinfop));
17422bb8e5e2SAndreas Jaekel }
17432bb8e5e2SAndreas Jaekel
17442bb8e5e2SAndreas Jaekel int
_fini(void)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