1372a60c3SAndreas Jaekel #include <sys/modctl.h> 2372a60c3SAndreas Jaekel #include <sys/ddi.h> 3372a60c3SAndreas Jaekel #include <sys/sunddi.h> 4372a60c3SAndreas Jaekel #include <sys/conf.h> 5372a60c3SAndreas Jaekel #include <sys/devops.h> 6372a60c3SAndreas Jaekel #include <sys/stat.h> 7372a60c3SAndreas Jaekel #include <sys/zfs_znode.h> 8372a60c3SAndreas Jaekel #include <sys/time.h> 9372a60c3SAndreas Jaekel #include <sys/sa.h> 10372a60c3SAndreas Jaekel #include <sys/zap.h> 11372a60c3SAndreas Jaekel #include <sys/time.h> 12372a60c3SAndreas Jaekel #include <sys/dsl_dataset.h> 13372a60c3SAndreas Jaekel #include <sys/zfs_vfsops.h> 14372a60c3SAndreas Jaekel #include <sys/dmu.h> 15372a60c3SAndreas Jaekel #include <sys/dmu_objset.h> 16372a60c3SAndreas Jaekel #include <sys/dsl_dir.h> 17372a60c3SAndreas Jaekel 18372a60c3SAndreas Jaekel #include <getgen/getgen.h> 19372a60c3SAndreas Jaekel 20372a60c3SAndreas Jaekel /* per-instance context */ 21372a60c3SAndreas Jaekel typedef struct gg_state { 22372a60c3SAndreas Jaekel kmutex_t mutex; 23372a60c3SAndreas Jaekel dev_info_t *dip; 24372a60c3SAndreas Jaekel boolean_t busy; 25372a60c3SAndreas Jaekel } gg_state_t; 26372a60c3SAndreas Jaekel 27372a60c3SAndreas Jaekel static void *statep; 28372a60c3SAndreas Jaekel static kmutex_t gg_mutex; 29372a60c3SAndreas Jaekel static int gg_instances = 0; 30372a60c3SAndreas Jaekel 31372a60c3SAndreas Jaekel int 32372a60c3SAndreas Jaekel gg_ioc_get_gen(intptr_t arg, int mode) 33372a60c3SAndreas Jaekel { 34372a60c3SAndreas Jaekel gg_ioctl_get_generation_t gg; 35372a60c3SAndreas Jaekel file_t *fp; 36372a60c3SAndreas Jaekel uint64_t gen; 37372a60c3SAndreas Jaekel uint64_t crtime[2]; 38372a60c3SAndreas Jaekel int ret = 0; 39819a7669SAndreas Jaekel zfsvfs_t *zfsvfs = NULL; 40372a60c3SAndreas Jaekel objset_t *osp; 41372a60c3SAndreas Jaekel sa_attr_type_t *sa_table; 42372a60c3SAndreas Jaekel sa_handle_t *hdl; 43372a60c3SAndreas Jaekel dmu_buf_t *db; 44372a60c3SAndreas Jaekel sa_bulk_attr_t bulk[2]; 45372a60c3SAndreas Jaekel int count = 0; 46372a60c3SAndreas Jaekel dmu_object_info_t doi; 47372a60c3SAndreas Jaekel timestruc_t crtime_s; 48819a7669SAndreas Jaekel znode_t *zp; 49*03d38ed9SDavid Hanisch dsl_dataset_phys_t *ds_phys; 50372a60c3SAndreas Jaekel 51372a60c3SAndreas Jaekel if (ddi_copyin((void *)arg, &gg, sizeof(gg), mode) != 0) 52372a60c3SAndreas Jaekel return EFAULT; 53372a60c3SAndreas Jaekel fp = getf(gg.fd); 54372a60c3SAndreas Jaekel if (fp == NULL) 55372a60c3SAndreas Jaekel return EBADF; 56372a60c3SAndreas Jaekel if (fp->f_vnode->v_vfsp->vfs_fstype != zfsfstype) { 57372a60c3SAndreas Jaekel ret = EINVAL; 58372a60c3SAndreas Jaekel goto out; 59372a60c3SAndreas Jaekel } 60372a60c3SAndreas Jaekel 61819a7669SAndreas Jaekel zp = (znode_t *)fp->f_vnode->v_data; 62819a7669SAndreas Jaekel /* modified version of ZFS_ENTER() macro - we need to clean up fp */ 63819a7669SAndreas Jaekel zfsvfs = zp->z_zfsvfs; 64819a7669SAndreas Jaekel rrm_enter_read(&zfsvfs->z_teardown_lock, FTAG); 65819a7669SAndreas Jaekel if (zp->z_zfsvfs->z_unmounted) { 66819a7669SAndreas Jaekel ret = EIO; 67819a7669SAndreas Jaekel goto out; 68819a7669SAndreas Jaekel } 69819a7669SAndreas Jaekel /* modified version of ZFS_VERIFY_ZP() macro */ 70819a7669SAndreas Jaekel if (zp->z_sa_hdl == NULL) { 71819a7669SAndreas Jaekel ret = EIO; 72819a7669SAndreas Jaekel goto out; 73819a7669SAndreas Jaekel } 74*03d38ed9SDavid Hanisch ds_phys = dsl_dataset_phys(zfsvfs->z_os->os_dsl_dataset); 75819a7669SAndreas Jaekel 76372a60c3SAndreas Jaekel /* get dataset name */ 77372a60c3SAndreas Jaekel dsl_dataset_name(zfsvfs->z_os->os_dsl_dataset, gg.dataset); 78372a60c3SAndreas Jaekel 79372a60c3SAndreas Jaekel /* get guid */ 80*03d38ed9SDavid Hanisch gg.guid = ds_phys->ds_guid; 81372a60c3SAndreas Jaekel 82*03d38ed9SDavid Hanisch if (gg.inode != 0) { 83372a60c3SAndreas Jaekel /* get generation and crtime */ 84372a60c3SAndreas Jaekel osp = zfsvfs->z_os; 85*03d38ed9SDavid Hanisch ret = sa_setup(osp, gg.inode, zfs_attr_table, ZPL_END, 86*03d38ed9SDavid Hanisch &sa_table); 87372a60c3SAndreas Jaekel if (ret) 88372a60c3SAndreas Jaekel goto out; 89372a60c3SAndreas Jaekel ret = sa_buf_hold(osp, gg.inode, FTAG, &db); 90372a60c3SAndreas Jaekel if (ret) 91372a60c3SAndreas Jaekel goto out; 92372a60c3SAndreas Jaekel dmu_object_info_from_db(db, &doi); 93372a60c3SAndreas Jaekel if ((doi.doi_bonus_type != DMU_OT_SA && 94372a60c3SAndreas Jaekel doi.doi_bonus_type != DMU_OT_ZNODE) || 95372a60c3SAndreas Jaekel doi.doi_bonus_type == DMU_OT_ZNODE && 96372a60c3SAndreas Jaekel doi.doi_bonus_size < sizeof (znode_phys_t)) { 97372a60c3SAndreas Jaekel sa_buf_rele(db, FTAG); 98372a60c3SAndreas Jaekel ret = ENOTSUP; 99372a60c3SAndreas Jaekel goto out; 100372a60c3SAndreas Jaekel } 101372a60c3SAndreas Jaekel ret = sa_handle_get(osp, gg.inode, NULL, SA_HDL_PRIVATE, &hdl); 102372a60c3SAndreas Jaekel if (ret) { 103372a60c3SAndreas Jaekel sa_buf_rele(db, FTAG); 104372a60c3SAndreas Jaekel goto out; 105372a60c3SAndreas Jaekel } 106372a60c3SAndreas Jaekel SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_GEN], NULL, 107372a60c3SAndreas Jaekel &gen, sizeof(gen)); 108372a60c3SAndreas Jaekel SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_CRTIME], NULL, 109372a60c3SAndreas Jaekel &crtime, sizeof(crtime)); 110372a60c3SAndreas Jaekel ret = sa_bulk_lookup(hdl, bulk, count); 111372a60c3SAndreas Jaekel sa_handle_destroy(hdl); 112372a60c3SAndreas Jaekel sa_buf_rele(db, FTAG); 113372a60c3SAndreas Jaekel if (ret) 114372a60c3SAndreas Jaekel goto out; 115372a60c3SAndreas Jaekel ZFS_TIME_DECODE(&crtime_s, crtime); 116372a60c3SAndreas Jaekel gg.generation = gen; 117372a60c3SAndreas Jaekel gg.crtime = crtime_s.tv_sec; 118*03d38ed9SDavid Hanisch } else { 119*03d38ed9SDavid Hanisch gg.generation = ds_phys->ds_bp.blk_birth; 120*03d38ed9SDavid Hanisch gg.crtime = 0; 121*03d38ed9SDavid Hanisch } 122372a60c3SAndreas Jaekel 123372a60c3SAndreas Jaekel ddi_copyout(&gg, (void *)arg, sizeof(gg), mode); 124372a60c3SAndreas Jaekel out: 125819a7669SAndreas Jaekel if (zfsvfs) 126819a7669SAndreas Jaekel ZFS_EXIT(zfsvfs); 127372a60c3SAndreas Jaekel releasef(gg.fd); 128372a60c3SAndreas Jaekel return ret; 129372a60c3SAndreas Jaekel } 130372a60c3SAndreas Jaekel 131372a60c3SAndreas Jaekel /* ARGSUSED */ 132372a60c3SAndreas Jaekel static int 133372a60c3SAndreas Jaekel gg_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) 134372a60c3SAndreas Jaekel { 135372a60c3SAndreas Jaekel int instance; 136372a60c3SAndreas Jaekel 137372a60c3SAndreas Jaekel instance = getminor(dev); 138372a60c3SAndreas Jaekel if (ddi_get_soft_state(statep, instance) == NULL) 139372a60c3SAndreas Jaekel return (ENXIO); 140372a60c3SAndreas Jaekel /* 141372a60c3SAndreas Jaekel * all structures passed between kernel and userspace 142372a60c3SAndreas Jaekel * are compatible between 64 and 32 bit. Model 143372a60c3SAndreas Jaekel * conversion can be ignored. 144372a60c3SAndreas Jaekel */ 145372a60c3SAndreas Jaekel switch (cmd) { 146372a60c3SAndreas Jaekel case GG_IOC_GET_GENERATION: 147372a60c3SAndreas Jaekel return gg_ioc_get_gen(arg, mode); 148372a60c3SAndreas Jaekel default: 149372a60c3SAndreas Jaekel /* generic "ioctl unknown" error */ 150372a60c3SAndreas Jaekel return ENOTTY; 151372a60c3SAndreas Jaekel } 152df8caf2dSSimon Klinkert /* NOTREACHED */ 153372a60c3SAndreas Jaekel return (0); 154372a60c3SAndreas Jaekel } 155372a60c3SAndreas Jaekel 156372a60c3SAndreas Jaekel /* gg_close() is called when the final fd/reference is removed. */ 157372a60c3SAndreas Jaekel /* ARGSUSED */ 158372a60c3SAndreas Jaekel static int 159372a60c3SAndreas Jaekel gg_close(dev_t dev, int flag, int otyp, cred_t *crepd) 160372a60c3SAndreas Jaekel { 161372a60c3SAndreas Jaekel gg_state_t *sp; 162372a60c3SAndreas Jaekel int instance; 163372a60c3SAndreas Jaekel 164372a60c3SAndreas Jaekel instance = getminor(dev); 165372a60c3SAndreas Jaekel if ((sp = ddi_get_soft_state(statep, instance)) == NULL) 166372a60c3SAndreas Jaekel return (ENXIO); 167372a60c3SAndreas Jaekel if (otyp != OTYP_CHR) 168372a60c3SAndreas Jaekel return (EINVAL); 169372a60c3SAndreas Jaekel mutex_enter(&sp->mutex); 170372a60c3SAndreas Jaekel if (sp->busy != B_TRUE) { 171372a60c3SAndreas Jaekel mutex_exit(&sp->mutex); 172372a60c3SAndreas Jaekel return (EINVAL); 173372a60c3SAndreas Jaekel } 174372a60c3SAndreas Jaekel sp->busy = B_FALSE; 175372a60c3SAndreas Jaekel mutex_exit(&sp->mutex); 176372a60c3SAndreas Jaekel return (0); 177372a60c3SAndreas Jaekel } 178372a60c3SAndreas Jaekel 179372a60c3SAndreas Jaekel /* gg_open() is called every time the device is opened/mounted/duped. */ 180372a60c3SAndreas Jaekel /* ARGSUSED */ 181372a60c3SAndreas Jaekel static int 182372a60c3SAndreas Jaekel gg_open(dev_t *devp, int flag, int otyp, cred_t *credp) 183372a60c3SAndreas Jaekel { 184372a60c3SAndreas Jaekel gg_state_t *sp; 185372a60c3SAndreas Jaekel int instance; 186372a60c3SAndreas Jaekel 187372a60c3SAndreas Jaekel instance = getminor(*devp); 188372a60c3SAndreas Jaekel if ((sp = ddi_get_soft_state(statep, instance)) == NULL) 189372a60c3SAndreas Jaekel return (ENXIO); 190372a60c3SAndreas Jaekel if (otyp != OTYP_CHR) 191372a60c3SAndreas Jaekel return (EINVAL); 192372a60c3SAndreas Jaekel if (drv_priv(credp) != 0) 193372a60c3SAndreas Jaekel return (EPERM); 194372a60c3SAndreas Jaekel mutex_enter(&sp->mutex); 195372a60c3SAndreas Jaekel if ((flag & FEXCL) && (sp->busy == B_TRUE)) { 196372a60c3SAndreas Jaekel mutex_exit(&sp->mutex); 197372a60c3SAndreas Jaekel return (EBUSY); 198372a60c3SAndreas Jaekel } 199372a60c3SAndreas Jaekel sp->busy = B_TRUE; 200372a60c3SAndreas Jaekel mutex_exit(&sp->mutex); 201372a60c3SAndreas Jaekel return (0); 202372a60c3SAndreas Jaekel } 203372a60c3SAndreas Jaekel 204372a60c3SAndreas Jaekel static struct cb_ops gg_cb_ops = { 205372a60c3SAndreas Jaekel gg_open, /* open */ 206372a60c3SAndreas Jaekel gg_close, /* close */ 207372a60c3SAndreas Jaekel nodev, /* strategy */ 208372a60c3SAndreas Jaekel nodev, /* print */ 209372a60c3SAndreas Jaekel nodev, /* dump */ 210372a60c3SAndreas Jaekel nodev, /* read */ 211372a60c3SAndreas Jaekel nodev, /* write */ 212372a60c3SAndreas Jaekel gg_ioctl, /* ioctl */ 213372a60c3SAndreas Jaekel nodev, /* devmap */ 214372a60c3SAndreas Jaekel nodev, /* mmap */ 215372a60c3SAndreas Jaekel nodev, /* segmap */ 216372a60c3SAndreas Jaekel nochpoll, /* chpoll */ 217372a60c3SAndreas Jaekel ddi_prop_op, /* prop_op */ 218372a60c3SAndreas Jaekel NULL, /* streamtab */ 219372a60c3SAndreas Jaekel D_MP | D_64BIT, /* cb_flag */ 220372a60c3SAndreas Jaekel CB_REV, /* cb_rev */ 221372a60c3SAndreas Jaekel nodev, /* aread */ 222372a60c3SAndreas Jaekel nodev, /* awrite */ 223372a60c3SAndreas Jaekel }; 224372a60c3SAndreas Jaekel 225372a60c3SAndreas Jaekel static int 226372a60c3SAndreas Jaekel gg_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 227372a60c3SAndreas Jaekel { 228372a60c3SAndreas Jaekel int instance; 229372a60c3SAndreas Jaekel gg_state_t *sp; 230372a60c3SAndreas Jaekel /* called once per instance with DDI_DETACH, 231372a60c3SAndreas Jaekel may be called to suspend */ 232372a60c3SAndreas Jaekel switch (cmd) { 233372a60c3SAndreas Jaekel case DDI_DETACH: 234372a60c3SAndreas Jaekel /* instance busy? */ 235372a60c3SAndreas Jaekel instance = ddi_get_instance(dip); 236372a60c3SAndreas Jaekel if ((sp = ddi_get_soft_state(statep, instance)) == NULL) 237372a60c3SAndreas Jaekel return (DDI_FAILURE); 238372a60c3SAndreas Jaekel mutex_enter(&sp->mutex); 239372a60c3SAndreas Jaekel if (sp->busy == B_TRUE) { 240372a60c3SAndreas Jaekel mutex_exit(&sp->mutex); 241372a60c3SAndreas Jaekel return (DDI_FAILURE); 242372a60c3SAndreas Jaekel } 243372a60c3SAndreas Jaekel mutex_exit(&sp->mutex); 244372a60c3SAndreas Jaekel /* free resources allocated for this instance */ 245372a60c3SAndreas Jaekel mutex_destroy(&sp->mutex); 246372a60c3SAndreas Jaekel ddi_remove_minor_node(dip, NULL); 247372a60c3SAndreas Jaekel ddi_soft_state_free(statep, instance); 248372a60c3SAndreas Jaekel mutex_enter(&gg_mutex); 249372a60c3SAndreas Jaekel gg_instances--; 250372a60c3SAndreas Jaekel mutex_exit(&gg_mutex); 251372a60c3SAndreas Jaekel return (DDI_SUCCESS); 252372a60c3SAndreas Jaekel case DDI_SUSPEND: 253372a60c3SAndreas Jaekel return (DDI_FAILURE); 254372a60c3SAndreas Jaekel default: 255372a60c3SAndreas Jaekel return (DDI_FAILURE); 256372a60c3SAndreas Jaekel } 257372a60c3SAndreas Jaekel } 258372a60c3SAndreas Jaekel 259372a60c3SAndreas Jaekel static int 260372a60c3SAndreas Jaekel gg_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 261372a60c3SAndreas Jaekel { 262372a60c3SAndreas Jaekel /* called once per instance with DDI_ATTACH, 263372a60c3SAndreas Jaekel may be called to resume */ 264372a60c3SAndreas Jaekel int instance; 265372a60c3SAndreas Jaekel gg_state_t *sp; 266372a60c3SAndreas Jaekel switch (cmd) { 267372a60c3SAndreas Jaekel case DDI_ATTACH: 268372a60c3SAndreas Jaekel instance = ddi_get_instance(dip); 269372a60c3SAndreas Jaekel if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS) { 270372a60c3SAndreas Jaekel return (DDI_FAILURE); 271372a60c3SAndreas Jaekel } 272372a60c3SAndreas Jaekel sp = ddi_get_soft_state(statep, instance); 273372a60c3SAndreas Jaekel ddi_set_driver_private(dip, sp); 274372a60c3SAndreas Jaekel sp->dip = dip; 275372a60c3SAndreas Jaekel sp->busy = B_FALSE; 276372a60c3SAndreas Jaekel if (ddi_create_minor_node(dip, ddi_get_name(dip), 277372a60c3SAndreas Jaekel S_IFCHR, instance, DDI_PSEUDO, 0) == DDI_FAILURE) { 278372a60c3SAndreas Jaekel ddi_soft_state_free(statep, instance); 279372a60c3SAndreas Jaekel return (DDI_FAILURE); 280372a60c3SAndreas Jaekel } 281372a60c3SAndreas Jaekel mutex_init(&sp->mutex, NULL, MUTEX_DRIVER, NULL); 282372a60c3SAndreas Jaekel ddi_report_dev(dip); 283372a60c3SAndreas Jaekel mutex_enter(&gg_mutex); 284372a60c3SAndreas Jaekel gg_instances++; 285372a60c3SAndreas Jaekel mutex_exit(&gg_mutex); 286372a60c3SAndreas Jaekel return (DDI_SUCCESS); 287372a60c3SAndreas Jaekel case DDI_RESUME: 288372a60c3SAndreas Jaekel return (DDI_SUCCESS); 289372a60c3SAndreas Jaekel default: 290372a60c3SAndreas Jaekel return (DDI_FAILURE); 291372a60c3SAndreas Jaekel } 292372a60c3SAndreas Jaekel } 293372a60c3SAndreas Jaekel 294372a60c3SAndreas Jaekel /* ARGSUSED */ 295372a60c3SAndreas Jaekel static int 296372a60c3SAndreas Jaekel gg_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp) 297372a60c3SAndreas Jaekel { 298372a60c3SAndreas Jaekel int instance; 299372a60c3SAndreas Jaekel gg_state_t *sp; 300372a60c3SAndreas Jaekel switch (infocmd) { 301372a60c3SAndreas Jaekel case DDI_INFO_DEVT2DEVINFO: 302372a60c3SAndreas Jaekel /* arg is dev_t */ 303372a60c3SAndreas Jaekel instance = getminor((dev_t)arg); 304372a60c3SAndreas Jaekel if ((sp = ddi_get_soft_state(statep, instance)) != NULL) { 305372a60c3SAndreas Jaekel mutex_enter(&sp->mutex); 306372a60c3SAndreas Jaekel *resultp = sp->dip; 307372a60c3SAndreas Jaekel mutex_exit(&sp->mutex); 308372a60c3SAndreas Jaekel return (DDI_SUCCESS); 309372a60c3SAndreas Jaekel } 310372a60c3SAndreas Jaekel *resultp = NULL; 311372a60c3SAndreas Jaekel return (DDI_FAILURE); 312372a60c3SAndreas Jaekel case DDI_INFO_DEVT2INSTANCE: 313372a60c3SAndreas Jaekel /* arg is dev_t */ 314372a60c3SAndreas Jaekel instance = getminor((dev_t)arg); 315372a60c3SAndreas Jaekel *resultp = (void *)(uintptr_t)instance; 316372a60c3SAndreas Jaekel return (DDI_FAILURE); 317372a60c3SAndreas Jaekel } 318372a60c3SAndreas Jaekel return (DDI_FAILURE); 319372a60c3SAndreas Jaekel } 320372a60c3SAndreas Jaekel 321372a60c3SAndreas Jaekel static struct dev_ops gg_dev_ops = { 322372a60c3SAndreas Jaekel DEVO_REV, /* driver build revision */ 323372a60c3SAndreas Jaekel 0, /* driver reference count */ 324372a60c3SAndreas Jaekel gg_getinfo, /* getinfo */ 325372a60c3SAndreas Jaekel nulldev, /* identify (obsolete) */ 326372a60c3SAndreas Jaekel nulldev, /* probe (search for devices) */ 327372a60c3SAndreas Jaekel gg_attach, /* attach */ 328372a60c3SAndreas Jaekel gg_detach, /* detach */ 329372a60c3SAndreas Jaekel nodev, /* reset (obsolete, use quiesce) */ 330372a60c3SAndreas Jaekel &gg_cb_ops, /* character and block device ops */ 331372a60c3SAndreas Jaekel NULL, /* bus driver ops */ 332372a60c3SAndreas Jaekel NULL, /* power management, not needed */ 333372a60c3SAndreas Jaekel ddi_quiesce_not_needed, /* quiesce */ 334372a60c3SAndreas Jaekel }; 335372a60c3SAndreas Jaekel 336372a60c3SAndreas Jaekel static struct modldrv gg_modldrv = { 337372a60c3SAndreas Jaekel &mod_driverops, /* all loadable modules use this */ 338*03d38ed9SDavid Hanisch "getgen - znode info, v1.1", /* driver name and version info */ 339372a60c3SAndreas Jaekel &gg_dev_ops /* ops method pointers */ 340372a60c3SAndreas Jaekel }; 341372a60c3SAndreas Jaekel 342372a60c3SAndreas Jaekel static struct modlinkage gg_modlinkage = { 343372a60c3SAndreas Jaekel MODREV_1, /* fixed value */ 344372a60c3SAndreas Jaekel { 345372a60c3SAndreas Jaekel &gg_modldrv, /* driver linkage structure */ 346372a60c3SAndreas Jaekel NULL /* list terminator */ 347372a60c3SAndreas Jaekel } 348372a60c3SAndreas Jaekel }; 349372a60c3SAndreas Jaekel 350372a60c3SAndreas Jaekel int 351372a60c3SAndreas Jaekel _init(void) 352372a60c3SAndreas Jaekel { 353372a60c3SAndreas Jaekel int error; 354372a60c3SAndreas Jaekel 355372a60c3SAndreas Jaekel if ((error = ddi_soft_state_init(&statep, sizeof(gg_state_t), 1)) != 0) 356372a60c3SAndreas Jaekel return (error); 357372a60c3SAndreas Jaekel gg_instances = 0; 358372a60c3SAndreas Jaekel 359372a60c3SAndreas Jaekel mutex_init(&gg_mutex, NULL, MUTEX_DRIVER, NULL); 360372a60c3SAndreas Jaekel 361372a60c3SAndreas Jaekel if ((error = mod_install(&gg_modlinkage)) != 0) { 362372a60c3SAndreas Jaekel cmn_err(CE_WARN, "getgen: could not install module"); 363372a60c3SAndreas Jaekel mutex_destroy(&gg_mutex); 364372a60c3SAndreas Jaekel ddi_soft_state_fini(&statep); 365372a60c3SAndreas Jaekel return (error); 366372a60c3SAndreas Jaekel } 367372a60c3SAndreas Jaekel return (0); 368372a60c3SAndreas Jaekel } 369372a60c3SAndreas Jaekel 370372a60c3SAndreas Jaekel int 371372a60c3SAndreas Jaekel _info(struct modinfo *modinfop) 372372a60c3SAndreas Jaekel { 373372a60c3SAndreas Jaekel return (mod_info(&gg_modlinkage, modinfop)); 374372a60c3SAndreas Jaekel } 375372a60c3SAndreas Jaekel 376372a60c3SAndreas Jaekel int 377372a60c3SAndreas Jaekel _fini(void) 378372a60c3SAndreas Jaekel { 379372a60c3SAndreas Jaekel int error = 0; 380372a60c3SAndreas Jaekel 381372a60c3SAndreas Jaekel mutex_enter(&gg_mutex); 382372a60c3SAndreas Jaekel if (gg_instances > 0) { 383372a60c3SAndreas Jaekel mutex_exit(&gg_mutex); 384372a60c3SAndreas Jaekel return (SET_ERROR(EBUSY)); 385372a60c3SAndreas Jaekel } 386372a60c3SAndreas Jaekel mutex_exit(&gg_mutex); 387372a60c3SAndreas Jaekel 388372a60c3SAndreas Jaekel if ((error = mod_remove(&gg_modlinkage)) != 0) { 389372a60c3SAndreas Jaekel cmn_err(CE_WARN, "mod_remove failed: %d", error); 390372a60c3SAndreas Jaekel return (error); 391372a60c3SAndreas Jaekel } 392372a60c3SAndreas Jaekel 393372a60c3SAndreas Jaekel /* free resources */ 394372a60c3SAndreas Jaekel ddi_soft_state_fini(&statep); 395372a60c3SAndreas Jaekel mutex_destroy(&gg_mutex); 396372a60c3SAndreas Jaekel 397372a60c3SAndreas Jaekel return (0); 398372a60c3SAndreas Jaekel } 399372a60c3SAndreas Jaekel 400