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