#include #include #include #include #include #include #include #include #include #define OP_STATISTICS (1 << 0) #define OP_POLL_EVENTS (1 << 1) #define OP_MUTE_POOL (1 << 2) #define OP_UNMUTE_POOL (1 << 3) #define OP_SET_MAX_QUEUE_LEN (1 << 4) #define ZEV_DEVICE "/devices/pseudo/zev@0:zev" static char *zev_device = ZEV_DEVICE; static char *zev_op_name[] = { "ZEV_OP_ERROR", "ZEV_OP_MARK", "ZEV_OP_ZFS_MOUNT", "ZEV_OP_ZFS_UMOUNT", "ZEV_OP_ZVOL_WRITE", "ZEV_OP_ZVOL_TRUNCATE", "ZEV_OP_ZNODE_CLOSE_AFTER_UPDATE", "ZEV_OP_ZNODE_CREATE", "ZEV_OP_ZNODE_MKDIR", "ZEV_OP_ZNODE_MAKE_XATTR_DIR", "ZEV_OP_ZNODE_REMOVE", "ZEV_OP_ZNODE_RMDIR", "ZEV_OP_ZNODE_LINK", "ZEV_OP_ZNODE_SYMLINK", "ZEV_OP_ZNODE_RENAME", "ZEV_OP_ZNODE_WRITE", "ZEV_OP_ZNODE_TRUNCATE", "ZEV_OP_ZNODE_SETATTR", "ZEV_OP_ZNODE_ACL", NULL }; static void zev_statistics(int fd) { zev_statistics_t zs; if (ioctl(fd, ZEV_IOC_GET_STATISTICS, &zs)) { perror("getting statistics data failed"); exit (EXIT_FAILURE); } printf("ZEV module state:\n"); printf(" queue length in bytes : %lu\n", zs.zev_queue_len); printf(" queue length limit : %lu\n", zs.zev_max_queue_len); printf(" poll wakeup throttle : %lu\n\n", zs.zev_poll_wakeup_queue_len); printf(" bytes read from device : %lu\n", zs.zev_bytes_read); printf(" module internal errors : %lu\n\n", zs.zev_cnt_errors); printf("ZFS event statistics:\n"); printf(" total ZFS events : %lu\n", zs.zev_cnt_total_events); printf(" ZFS mount : %lu\n", zs.zev_cnt_zfs_mount); printf(" ZFS umount : %lu\n", zs.zev_cnt_zfs_umount); printf(" ZVOL write : %lu\n", zs.zev_cnt_zvol_write); printf(" ZVOL truncate : %lu\n", zs.zev_cnt_zvol_truncate); printf(" ZNODE close after update: %lu\n", zs.zev_cnt_znode_close_after_update); printf(" ZNODE create : %lu\n", zs.zev_cnt_znode_create); printf(" ZNODE remove : %lu\n", zs.zev_cnt_znode_remove); printf(" ZNODE link : %lu\n", zs.zev_cnt_znode_link); printf(" ZNODE symlink : %lu\n", zs.zev_cnt_znode_symlink); printf(" ZNODE rename : %lu\n", zs.zev_cnt_znode_rename); printf(" ZNODE write : %lu\n", zs.zev_cnt_znode_write); printf(" ZNODE truncate : %lu\n", zs.zev_cnt_znode_truncate); printf(" ZNODE setattr : %lu\n", zs.zev_cnt_znode_setattr); printf(" ZNODE acl : %lu\n", zs.zev_cnt_znode_acl); } static void zev_print_error(char *buf) { zev_error_t *rec = (zev_error_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; printf("%s %s: failed_op=%s msg=%s\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], zev_op_name[rec->failed_op - ZEV_OP_MIN], ZEV_ERRSTR(rec)); } static void zev_print_mark(char *buf) { zev_mark_t *rec = (zev_mark_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; printf("%s %s: guid=%llu mark_id=%lld payload_len=%ld\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid, rec->mark_id, rec->payload_len); } static void zev_print_zfs_mount(char *buf) { zev_zfs_mount_t *rec = (zev_zfs_mount_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; printf("%s %s: guid=%llu remount=%s dataset='%s' mountpoint='%s'\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid, rec->remount ? "true" : "false", ZEV_DATASET(rec), ZEV_MOUNTPOINT(rec)); } static void zev_print_zfs_umount(char *buf) { zev_zfs_umount_t *rec = (zev_zfs_umount_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; printf("%s %s: guid=%llu\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid); } static void zev_print_zvol_truncate(char *buf) { zev_zvol_truncate_t *rec = (zev_zvol_truncate_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; printf("%s %s: guid=%llu offset=%llu length=%llu\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid, rec->offset, rec->length); } static void zev_print_zvol_write(char *buf) { zev_print_zvol_truncate(buf); } static void zev_print_znode_close_after_update(char *buf) { zev_znode_close_after_update_t *rec = (zev_znode_close_after_update_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; printf("%s %s: guid=%llu file=%llu.%llu\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid, rec->file.ino, rec->file.gen); } static void zev_print_znode_create(char *buf) { zev_znode_create_t *rec = (zev_znode_create_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; printf("%s %s: guid=%llu parent=%llu.%llu file=%llu.%llu " "file.mtime=%llu, parent.mtime=%llu, name='%s'\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid, rec->parent.ino, rec->parent.gen, rec->file.ino, rec->file.gen, rec->file.mtime, rec->parent.mtime, ZEV_NAME(rec)); } static void zev_print_znode_mkdir(char *buf) { zev_print_znode_create(buf); } static void zev_print_znode_make_xattr_dir(char *buf) { zev_print_znode_create(buf); } static void zev_print_znode_remove(char *buf) { zev_znode_remove_t *rec = (zev_znode_remove_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; printf("%s %s: guid=%llu parent=%llu.%llu file.mtime=%llu name='%s'\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->guid, rec->parent.ino, rec->parent.gen, rec->file.mtime, ZEV_NAME(rec)); } static void zev_print_znode_rmdir(char *buf) { zev_print_znode_remove(buf); } static void zev_print_znode_link(char *buf) { zev_znode_link_t *rec = (zev_znode_link_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; printf("%s %s: parent=%llu.%llu file=%llu.%llu " "file.ctime=%llu parent.ctime=%llu name='%s'\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->parent.ino, rec->parent.gen, rec->file.ino, rec->file.gen, rec->file.ctime, rec->parent.ctime, ZEV_NAME(rec)); printf("links: %d\n", rec->file.links); } static void zev_print_znode_symlink(char *buf) { zev_znode_symlink_t *rec = (zev_znode_symlink_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; printf("%s %s: parent=%llu.%llu file=%llu.%llu name='%s' link='%s'\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->parent.ino, rec->parent.gen, rec->file.ino, rec->file.gen, ZEV_NAME(rec), ZEV_LINK(rec)); } static void zev_print_znode_rename(char *buf) { zev_znode_rename_t *rec = (zev_znode_rename_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; printf("%s %s: srcdir=%llu.%llu dstdir=%llu.%llu file=%llu.%llu " "file.mtime=%llu, file.ctime=%llu, srcdir.mtime=%llu, " "srcdir.ctime=%llu, dstdir.mtime=%llu, dstdir.ctime=%llu, " "srcname='%s' dstname='%s'\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->srcdir.ino, rec->srcdir.gen, rec->dstdir.ino, rec->dstdir.gen, rec->file.ino, rec->file.gen, rec->file.mtime, rec->file.ctime, rec->srcdir.mtime, rec->srcdir.ctime, rec->dstdir.mtime, rec->dstdir.ctime, ZEV_SRCNAME(rec), ZEV_DSTNAME(rec)); } static void zev_print_znode_write(char *buf) { zev_znode_write_t *rec = (zev_znode_write_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; printf("%s %s: file=%llu.%llu offset=%llu length=%llu\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->file.ino, rec->file.gen, rec->offset, rec->length); } static void zev_print_znode_truncate(char *buf) { zev_print_znode_write(buf); } static void zev_print_znode_setattr(char *buf) { zev_znode_setattr_t *rec = (zev_znode_setattr_t *)buf; time_t op_time = rec->op_time; char *ct = ctime(&op_time); ct[24] = '\0'; printf("%s %s: file=%llu.%llu mtime=%llu\n", ct, zev_op_name[rec->op - ZEV_OP_MIN], rec->file.ino, rec->file.gen, rec->file.mtime); } static void zev_print_znode_acl(char *buf) { zev_print_znode_setattr(buf); } static void zev_print_event(char *buf, int len) { int record_len; int op; record_len = *(uint32_t *)buf; if (record_len != len) { fprintf(stderr, "record length mismatch: got %d, expected %d\n", record_len, len); exit(1); } op = *((uint32_t *)buf + 1); if (op < ZEV_OP_MIN || op > ZEV_OP_MAX) { fprintf(stderr, "unknown op code: %d\n", op); exit(1); } switch (op) { case ZEV_OP_ERROR: zev_print_error(buf); break; case ZEV_OP_MARK: zev_print_mark(buf); break; case ZEV_OP_ZFS_MOUNT: zev_print_zfs_mount(buf); break; case ZEV_OP_ZFS_UMOUNT: zev_print_zfs_umount(buf); break; case ZEV_OP_ZVOL_TRUNCATE: zev_print_zvol_truncate(buf); break; case ZEV_OP_ZVOL_WRITE: zev_print_zvol_write(buf); break; case ZEV_OP_ZNODE_CLOSE_AFTER_UPDATE: zev_print_znode_close_after_update(buf); break; case ZEV_OP_ZNODE_CREATE: zev_print_znode_create(buf); break; case ZEV_OP_ZNODE_MKDIR: zev_print_znode_mkdir(buf); break; case ZEV_OP_ZNODE_MAKE_XATTR_DIR: zev_print_znode_make_xattr_dir(buf); break; case ZEV_OP_ZNODE_REMOVE: zev_print_znode_remove(buf); break; case ZEV_OP_ZNODE_RMDIR: zev_print_znode_rmdir(buf); break; case ZEV_OP_ZNODE_LINK: zev_print_znode_link(buf); break; case ZEV_OP_ZNODE_SYMLINK: zev_print_znode_symlink(buf); break; case ZEV_OP_ZNODE_RENAME: zev_print_znode_rename(buf); break; case ZEV_OP_ZNODE_WRITE: zev_print_znode_write(buf); break; case ZEV_OP_ZNODE_TRUNCATE: zev_print_znode_truncate(buf); break; case ZEV_OP_ZNODE_SETATTR: zev_print_znode_setattr(buf); break; case ZEV_OP_ZNODE_ACL: zev_print_znode_acl(buf); break; default: fprintf(stderr, "unhandled op code: %d\n", op); exit(1); } } static void zev_poll_events(int fd) { struct pollfd pfd[1]; int ret; char buf[4096]; zev_event_t *ev; int off = 0; while (1) { pfd[0].fd = fd; pfd[0].events = POLLIN; ret = poll(pfd, 1, 1000); if (ret < 0) { perror("poll failed"); exit(EXIT_FAILURE); } if (!(pfd[0].revents & POLLIN)) continue; /* data available */ ret = read(fd, buf, sizeof(buf)); if (ret < 0) { perror("read failed"); exit(EXIT_FAILURE); } if (ret == 0) continue; while (ret > off) { ev = (zev_event_t *)(buf + off); zev_print_event(buf + off, ev->header.record_len); off += ev->header.record_len; } off = 0; } return; } static void usage(char *progname) { fprintf(stderr, "usage: %s [-s] [-d ]\n", progname); fprintf(stderr, " -s show zev statistics\n"); fprintf(stderr, " -p poll for ZFS events\n"); fprintf(stderr, " -q set maximum event queue length\n"); fprintf(stderr, " -t set queue length poll throttle\n"); fprintf(stderr, " -m mute pool, no events for this pool\n"); fprintf(stderr, " -M unmute pool\n"); fprintf(stderr, " -d device file to use. default is '%s'\n", ZEV_DEVICE); fprintf(stderr, " -k : queue mark event\n"); exit (EXIT_FAILURE); } static int zev_set_max_queue_len(int fd, char *optarg) { uint64_t maxqueuelen; errno = 0; maxqueuelen = strtol(optarg, (char **)NULL, 10); if (errno) { fprintf(stderr, "invalid queue length parameter: %s\n", optarg); return (EXIT_FAILURE); } if (ioctl(fd, ZEV_IOC_SET_MAX_QUEUE_LEN, &maxqueuelen)) { perror("setting max queue length failed"); return (EXIT_FAILURE); } return (0); } static int zev_set_poll_wakeup_queue_len(int fd, char *optarg) { uint64_t queuelen; errno = 0; queuelen = strtol(optarg, (char **)NULL, 10); if (errno) { fprintf(stderr, "invalid queue length parameter: %s\n", optarg); return (EXIT_FAILURE); } if (ioctl(fd, ZEV_IOC_SET_POLL_WAKEUP_QUEUE_LEN, &queuelen)) { perror("setting poll wakeup queue length failed"); return (EXIT_FAILURE); } return (0); } static int zev_mute_unmute_impl(int fd, char *poolname, int mute) { zev_ioctl_poolarg_t pa; int len; int op = mute ? ZEV_IOC_MUTE_POOL : ZEV_IOC_UNMUTE_POOL; len = strlen(poolname); if (len <= 0 || len >= sizeof(pa.zev_poolname)) { fprintf(stderr, "invalid poolname: %s\n", poolname); return (EXIT_FAILURE); } strcpy(pa.zev_poolname, poolname); pa.zev_poolname_len = len; if (ioctl(fd, op, &pa)) { perror("muting pool data failed"); return (EXIT_FAILURE); } return (0); } int zev_mute_pool(int fd, char *poolname) { return zev_mute_unmute_impl(fd, poolname, 1); } int zev_unmute_pool(int fd, char *poolname) { return zev_mute_unmute_impl(fd, poolname, 0); } static int zev_mark(int fd, char *arg) { zev_ioctl_mark_t *mark; uint64_t guid; int len; char *p; p = strchr(arg, ':'); if (!p) { fprintf(stderr, "expected value is :, " "e.g. '123:hello'\n"); exit (EXIT_FAILURE); } *p = '\n'; p++; errno = 0; guid = strtoll(optarg, (char **)NULL, 10); if (errno) { fprintf(stderr, "guid must be a number.\n"); exit (EXIT_FAILURE); } len = strlen(p); mark = malloc(sizeof(*mark) + len + 1); if (!mark) { fprintf(stderr, "can't allocate mark structure: %s\n", strerror(errno)); exit (EXIT_FAILURE); } mark->zev_guid = guid; mark->zev_mark_id = 0; mark->zev_payload_len = len; strcpy(ZEV_PAYLOAD(mark), p); if (ioctl(fd, ZEV_IOC_MARK, mark)) { perror("queueing mark failed"); return (EXIT_FAILURE); } printf("mark id: %lu\n", mark->zev_mark_id); return (0); } int main(int argc, char **argv) { int fd; int c; int ops = 0; extern char *optarg; /* open device */ fd = open(zev_device, O_RDONLY); if (fd < 0) { perror("opening zev device failed"); return EXIT_FAILURE; } while ((c = getopt(argc, argv, "spdk:q:t:m:M:h?")) != -1) { switch(c) { case 's': ops |= OP_STATISTICS; break; case 'p': ops |= OP_POLL_EVENTS; break; case 'd': zev_device = optarg; break; case 'q': return zev_set_max_queue_len(fd, optarg); case 't': return zev_set_poll_wakeup_queue_len(fd, optarg); case 'm': return zev_mute_pool(fd, optarg); case 'M': return zev_unmute_pool(fd, optarg); case 'k': return zev_mark(fd, optarg); case 'h': case '?': default: usage(argv[0]); } } if (!ops) usage(argv[0]); if (ops & OP_STATISTICS) zev_statistics(fd); if (ops & OP_POLL_EVENTS) zev_poll_events(fd); close(fd); return EXIT_SUCCESS; }