xref: /titanic_54/usr/src/uts/common/fs/getgen/getgen.c (revision 629337b39ee9656c3872d30188fc6a73d4d9d4af)
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
gg_ioc_get_gen(intptr_t arg,int mode)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;
4903d38ed9SDavid 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 	}
7403d38ed9SDavid 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 */
8003d38ed9SDavid Hanisch 	gg.guid = ds_phys->ds_guid;
81372a60c3SAndreas Jaekel 
8203d38ed9SDavid Hanisch 	if (gg.inode != 0) {
83372a60c3SAndreas Jaekel 		/* get generation and crtime */
84372a60c3SAndreas Jaekel 		osp = zfsvfs->z_os;
8503d38ed9SDavid Hanisch 		ret = sa_setup(osp, gg.inode, zfs_attr_table, ZPL_END,
8603d38ed9SDavid 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;
11803d38ed9SDavid Hanisch 	} else {
11903d38ed9SDavid Hanisch 		gg.generation = ds_phys->ds_bp.blk_birth;
12003d38ed9SDavid Hanisch 		gg.crtime = 0;
12103d38ed9SDavid 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 
131*629337b3SArne Jansen int
gg_ioc_get_txg(intptr_t arg,int mode)132*629337b3SArne Jansen gg_ioc_get_txg(intptr_t arg, int mode)
133*629337b3SArne Jansen {
134*629337b3SArne Jansen 	gg_ioctl_get_txg_t gg;
135*629337b3SArne Jansen 	dsl_pool_t *dp;
136*629337b3SArne Jansen 	dsl_dataset_t *ds;
137*629337b3SArne Jansen 	dsl_dataset_phys_t *ds_phys;
138*629337b3SArne Jansen 	int error;
139*629337b3SArne Jansen 
140*629337b3SArne Jansen 	if (ddi_copyin((void *)arg, &gg, sizeof(gg), mode) != 0)
141*629337b3SArne Jansen 		return EFAULT;
142*629337b3SArne Jansen 
143*629337b3SArne Jansen 	error = dsl_pool_hold(gg.dataset, FTAG, &dp);
144*629337b3SArne Jansen 	if (error != 0)
145*629337b3SArne Jansen 		return (error);
146*629337b3SArne Jansen 
147*629337b3SArne Jansen 	error = dsl_dataset_hold(dp, gg.dataset, FTAG, &ds);
148*629337b3SArne Jansen 	if (error == 0) {
149*629337b3SArne Jansen 		ds_phys = dsl_dataset_phys(ds);
150*629337b3SArne Jansen 		gg.txg = ds_phys->ds_bp.blk_birth;
151*629337b3SArne Jansen 		dsl_dataset_rele(ds, &gg);
152*629337b3SArne Jansen 	}
153*629337b3SArne Jansen 	dsl_pool_rele(dp, FTAG);
154*629337b3SArne Jansen 
155*629337b3SArne Jansen 	ddi_copyout(&gg, (void *)arg, sizeof(gg), mode);
156*629337b3SArne Jansen 
157*629337b3SArne Jansen 	return (error);
158*629337b3SArne Jansen }
159*629337b3SArne Jansen 
160372a60c3SAndreas Jaekel /* ARGSUSED */
161372a60c3SAndreas Jaekel static int
gg_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)162372a60c3SAndreas Jaekel gg_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp)
163372a60c3SAndreas Jaekel {
164372a60c3SAndreas Jaekel 	int instance;
165372a60c3SAndreas Jaekel 
166372a60c3SAndreas Jaekel 	instance = getminor(dev);
167372a60c3SAndreas Jaekel 	if (ddi_get_soft_state(statep, instance) == NULL)
168372a60c3SAndreas Jaekel 		return (ENXIO);
169372a60c3SAndreas Jaekel 	/*
170372a60c3SAndreas Jaekel 	 * all structures passed between kernel and userspace
171372a60c3SAndreas Jaekel 	 * are compatible between 64 and 32 bit.  Model
172372a60c3SAndreas Jaekel 	 * conversion can be ignored.
173372a60c3SAndreas Jaekel 	 */
174372a60c3SAndreas Jaekel 	switch (cmd) {
175372a60c3SAndreas Jaekel 	case GG_IOC_GET_GENERATION:
176372a60c3SAndreas Jaekel 		return gg_ioc_get_gen(arg, mode);
177*629337b3SArne Jansen 	case GG_IOC_GET_TXG:
178*629337b3SArne Jansen 		return gg_ioc_get_txg(arg, mode);
179372a60c3SAndreas Jaekel 	default:
180372a60c3SAndreas Jaekel 		/* generic "ioctl unknown" error */
181372a60c3SAndreas Jaekel 		return ENOTTY;
182372a60c3SAndreas Jaekel 	}
183df8caf2dSSimon Klinkert 	/* NOTREACHED */
184372a60c3SAndreas Jaekel 	return (0);
185372a60c3SAndreas Jaekel }
186372a60c3SAndreas Jaekel 
187372a60c3SAndreas Jaekel /* gg_close() is called when the final fd/reference is removed. */
188372a60c3SAndreas Jaekel /* ARGSUSED */
189372a60c3SAndreas Jaekel static int
gg_close(dev_t dev,int flag,int otyp,cred_t * crepd)190372a60c3SAndreas Jaekel gg_close(dev_t dev, int flag, int otyp, cred_t *crepd)
191372a60c3SAndreas Jaekel {
192372a60c3SAndreas Jaekel 	gg_state_t *sp;
193372a60c3SAndreas Jaekel 	int instance;
194372a60c3SAndreas Jaekel 
195372a60c3SAndreas Jaekel 	instance = getminor(dev);
196372a60c3SAndreas Jaekel 	if ((sp = ddi_get_soft_state(statep, instance)) == NULL)
197372a60c3SAndreas Jaekel 		return (ENXIO);
198372a60c3SAndreas Jaekel 	if (otyp != OTYP_CHR)
199372a60c3SAndreas Jaekel 		return (EINVAL);
200372a60c3SAndreas Jaekel 	mutex_enter(&sp->mutex);
201372a60c3SAndreas Jaekel 	if (sp->busy != B_TRUE) {
202372a60c3SAndreas Jaekel 		mutex_exit(&sp->mutex);
203372a60c3SAndreas Jaekel 		return (EINVAL);
204372a60c3SAndreas Jaekel 	}
205372a60c3SAndreas Jaekel 	sp->busy = B_FALSE;
206372a60c3SAndreas Jaekel 	mutex_exit(&sp->mutex);
207372a60c3SAndreas Jaekel 	return (0);
208372a60c3SAndreas Jaekel }
209372a60c3SAndreas Jaekel 
210372a60c3SAndreas Jaekel /* gg_open() is called every time the device is opened/mounted/duped. */
211372a60c3SAndreas Jaekel /* ARGSUSED */
212372a60c3SAndreas Jaekel static int
gg_open(dev_t * devp,int flag,int otyp,cred_t * credp)213372a60c3SAndreas Jaekel gg_open(dev_t *devp, int flag, int otyp, cred_t *credp)
214372a60c3SAndreas Jaekel {
215372a60c3SAndreas Jaekel 	gg_state_t *sp;
216372a60c3SAndreas Jaekel 	int instance;
217372a60c3SAndreas Jaekel 
218372a60c3SAndreas Jaekel 	instance = getminor(*devp);
219372a60c3SAndreas Jaekel 	if ((sp = ddi_get_soft_state(statep, instance)) == NULL)
220372a60c3SAndreas Jaekel 		return (ENXIO);
221372a60c3SAndreas Jaekel 	if (otyp != OTYP_CHR)
222372a60c3SAndreas Jaekel 		return (EINVAL);
223372a60c3SAndreas Jaekel 	if (drv_priv(credp) != 0)
224372a60c3SAndreas Jaekel 		return (EPERM);
225372a60c3SAndreas Jaekel 	mutex_enter(&sp->mutex);
226372a60c3SAndreas Jaekel 	if ((flag & FEXCL) && (sp->busy == B_TRUE)) {
227372a60c3SAndreas Jaekel 		mutex_exit(&sp->mutex);
228372a60c3SAndreas Jaekel 		return (EBUSY);
229372a60c3SAndreas Jaekel 	}
230372a60c3SAndreas Jaekel 	sp->busy = B_TRUE;
231372a60c3SAndreas Jaekel 	mutex_exit(&sp->mutex);
232372a60c3SAndreas Jaekel 	return (0);
233372a60c3SAndreas Jaekel }
234372a60c3SAndreas Jaekel 
235372a60c3SAndreas Jaekel static struct cb_ops gg_cb_ops = {
236372a60c3SAndreas Jaekel 	gg_open,		/* open */
237372a60c3SAndreas Jaekel 	gg_close,		/* close */
238372a60c3SAndreas Jaekel 	nodev,			/* strategy */
239372a60c3SAndreas Jaekel 	nodev,			/* print */
240372a60c3SAndreas Jaekel 	nodev,			/* dump */
241372a60c3SAndreas Jaekel 	nodev,			/* read */
242372a60c3SAndreas Jaekel 	nodev,			/* write */
243372a60c3SAndreas Jaekel 	gg_ioctl,		/* ioctl */
244372a60c3SAndreas Jaekel 	nodev,			/* devmap */
245372a60c3SAndreas Jaekel 	nodev,			/* mmap */
246372a60c3SAndreas Jaekel 	nodev,			/* segmap */
247372a60c3SAndreas Jaekel 	nochpoll,		/* chpoll */
248372a60c3SAndreas Jaekel 	ddi_prop_op,		/* prop_op */
249372a60c3SAndreas Jaekel 	NULL,			/* streamtab */
250372a60c3SAndreas Jaekel 	D_MP | D_64BIT,		/* cb_flag */
251372a60c3SAndreas Jaekel 	CB_REV,			/* cb_rev */
252372a60c3SAndreas Jaekel 	nodev,			/* aread */
253372a60c3SAndreas Jaekel 	nodev,			/* awrite */
254372a60c3SAndreas Jaekel };
255372a60c3SAndreas Jaekel 
256372a60c3SAndreas Jaekel static int
gg_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)257372a60c3SAndreas Jaekel gg_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
258372a60c3SAndreas Jaekel {
259372a60c3SAndreas Jaekel 	int instance;
260372a60c3SAndreas Jaekel 	gg_state_t *sp;
261372a60c3SAndreas Jaekel 	/* called once per instance with DDI_DETACH,
262372a60c3SAndreas Jaekel 	   may be called to suspend */
263372a60c3SAndreas Jaekel 	switch (cmd) {
264372a60c3SAndreas Jaekel 	case DDI_DETACH:
265372a60c3SAndreas Jaekel 		/* instance busy? */
266372a60c3SAndreas Jaekel 		instance = ddi_get_instance(dip);
267372a60c3SAndreas Jaekel 		if ((sp = ddi_get_soft_state(statep, instance)) == NULL)
268372a60c3SAndreas Jaekel 			return (DDI_FAILURE);
269372a60c3SAndreas Jaekel 		mutex_enter(&sp->mutex);
270372a60c3SAndreas Jaekel 		if (sp->busy == B_TRUE) {
271372a60c3SAndreas Jaekel 			mutex_exit(&sp->mutex);
272372a60c3SAndreas Jaekel 			return (DDI_FAILURE);
273372a60c3SAndreas Jaekel 		}
274372a60c3SAndreas Jaekel 		mutex_exit(&sp->mutex);
275372a60c3SAndreas Jaekel 		/* free resources allocated for this instance */
276372a60c3SAndreas Jaekel 		mutex_destroy(&sp->mutex);
277372a60c3SAndreas Jaekel 		ddi_remove_minor_node(dip, NULL);
278372a60c3SAndreas Jaekel 		ddi_soft_state_free(statep, instance);
279372a60c3SAndreas Jaekel 		mutex_enter(&gg_mutex);
280372a60c3SAndreas Jaekel 		gg_instances--;
281372a60c3SAndreas Jaekel 		mutex_exit(&gg_mutex);
282372a60c3SAndreas Jaekel 		return (DDI_SUCCESS);
283372a60c3SAndreas Jaekel 	case DDI_SUSPEND:
284372a60c3SAndreas Jaekel 		return (DDI_FAILURE);
285372a60c3SAndreas Jaekel 	default:
286372a60c3SAndreas Jaekel 		return (DDI_FAILURE);
287372a60c3SAndreas Jaekel 	}
288372a60c3SAndreas Jaekel }
289372a60c3SAndreas Jaekel 
290372a60c3SAndreas Jaekel static int
gg_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)291372a60c3SAndreas Jaekel gg_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
292372a60c3SAndreas Jaekel {
293372a60c3SAndreas Jaekel 	/* called once per instance with DDI_ATTACH,
294372a60c3SAndreas Jaekel 	   may be called to resume */
295372a60c3SAndreas Jaekel 	int instance;
296372a60c3SAndreas Jaekel 	gg_state_t *sp;
297372a60c3SAndreas Jaekel 	switch (cmd) {
298372a60c3SAndreas Jaekel 	case DDI_ATTACH:
299372a60c3SAndreas Jaekel 		instance = ddi_get_instance(dip);
300372a60c3SAndreas Jaekel 		if (ddi_soft_state_zalloc(statep, instance) != DDI_SUCCESS) {
301372a60c3SAndreas Jaekel 			return (DDI_FAILURE);
302372a60c3SAndreas Jaekel 		}
303372a60c3SAndreas Jaekel 		sp = ddi_get_soft_state(statep, instance);
304372a60c3SAndreas Jaekel 		ddi_set_driver_private(dip, sp);
305372a60c3SAndreas Jaekel 		sp->dip = dip;
306372a60c3SAndreas Jaekel 		sp->busy = B_FALSE;
307372a60c3SAndreas Jaekel 		if (ddi_create_minor_node(dip, ddi_get_name(dip),
308372a60c3SAndreas Jaekel 		    S_IFCHR, instance, DDI_PSEUDO, 0) == DDI_FAILURE) {
309372a60c3SAndreas Jaekel 			ddi_soft_state_free(statep, instance);
310372a60c3SAndreas Jaekel 			return (DDI_FAILURE);
311372a60c3SAndreas Jaekel 		}
312372a60c3SAndreas Jaekel 		mutex_init(&sp->mutex, NULL, MUTEX_DRIVER, NULL);
313372a60c3SAndreas Jaekel 		ddi_report_dev(dip);
314372a60c3SAndreas Jaekel 		mutex_enter(&gg_mutex);
315372a60c3SAndreas Jaekel 		gg_instances++;
316372a60c3SAndreas Jaekel 		mutex_exit(&gg_mutex);
317372a60c3SAndreas Jaekel 		return (DDI_SUCCESS);
318372a60c3SAndreas Jaekel 	case DDI_RESUME:
319372a60c3SAndreas Jaekel 		return (DDI_SUCCESS);
320372a60c3SAndreas Jaekel 	default:
321372a60c3SAndreas Jaekel 		return (DDI_FAILURE);
322372a60c3SAndreas Jaekel 	}
323372a60c3SAndreas Jaekel }
324372a60c3SAndreas Jaekel 
325372a60c3SAndreas Jaekel /* ARGSUSED */
326372a60c3SAndreas Jaekel static int
gg_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** resultp)327372a60c3SAndreas Jaekel gg_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp)
328372a60c3SAndreas Jaekel {
329372a60c3SAndreas Jaekel 	int instance;
330372a60c3SAndreas Jaekel 	gg_state_t *sp;
331372a60c3SAndreas Jaekel 	switch (infocmd) {
332372a60c3SAndreas Jaekel 	case DDI_INFO_DEVT2DEVINFO:
333372a60c3SAndreas Jaekel 		/* arg is dev_t */
334372a60c3SAndreas Jaekel 		instance = getminor((dev_t)arg);
335372a60c3SAndreas Jaekel 		if ((sp = ddi_get_soft_state(statep, instance)) != NULL) {
336372a60c3SAndreas Jaekel 			mutex_enter(&sp->mutex);
337372a60c3SAndreas Jaekel 			*resultp = sp->dip;
338372a60c3SAndreas Jaekel 			mutex_exit(&sp->mutex);
339372a60c3SAndreas Jaekel 			return (DDI_SUCCESS);
340372a60c3SAndreas Jaekel 		}
341372a60c3SAndreas Jaekel 		*resultp = NULL;
342372a60c3SAndreas Jaekel 		return (DDI_FAILURE);
343372a60c3SAndreas Jaekel 	case DDI_INFO_DEVT2INSTANCE:
344372a60c3SAndreas Jaekel 		/* arg is dev_t */
345372a60c3SAndreas Jaekel 		instance = getminor((dev_t)arg);
346372a60c3SAndreas Jaekel 		*resultp = (void *)(uintptr_t)instance;
347372a60c3SAndreas Jaekel 		return (DDI_FAILURE);
348372a60c3SAndreas Jaekel 	}
349372a60c3SAndreas Jaekel 	return (DDI_FAILURE);
350372a60c3SAndreas Jaekel }
351372a60c3SAndreas Jaekel 
352372a60c3SAndreas Jaekel static struct dev_ops gg_dev_ops = {
353372a60c3SAndreas Jaekel 	DEVO_REV,			/* driver build revision */
354372a60c3SAndreas Jaekel 	0,				/* driver reference count */
355372a60c3SAndreas Jaekel 	gg_getinfo,			/* getinfo */
356372a60c3SAndreas Jaekel 	nulldev,			/* identify (obsolete) */
357372a60c3SAndreas Jaekel 	nulldev,			/* probe (search for devices) */
358372a60c3SAndreas Jaekel 	gg_attach,			/* attach */
359372a60c3SAndreas Jaekel 	gg_detach,			/* detach */
360372a60c3SAndreas Jaekel 	nodev,				/* reset (obsolete, use quiesce) */
361372a60c3SAndreas Jaekel 	&gg_cb_ops,			/* character and block device ops */
362372a60c3SAndreas Jaekel 	NULL,				/* bus driver ops */
363372a60c3SAndreas Jaekel 	NULL,				/* power management, not needed */
364372a60c3SAndreas Jaekel 	ddi_quiesce_not_needed,		/* quiesce */
365372a60c3SAndreas Jaekel };
366372a60c3SAndreas Jaekel 
367372a60c3SAndreas Jaekel static struct modldrv gg_modldrv = {
368372a60c3SAndreas Jaekel 	&mod_driverops,			/* all loadable modules use this */
36903d38ed9SDavid Hanisch 	"getgen - znode info, v1.1",	/* driver name and version info */
370372a60c3SAndreas Jaekel 	&gg_dev_ops			/* ops method pointers */
371372a60c3SAndreas Jaekel };
372372a60c3SAndreas Jaekel 
373372a60c3SAndreas Jaekel static struct modlinkage gg_modlinkage = {
374372a60c3SAndreas Jaekel 	MODREV_1,	/* fixed value */
375372a60c3SAndreas Jaekel 	{
376372a60c3SAndreas Jaekel 		&gg_modldrv,	/* driver linkage structure */
377372a60c3SAndreas Jaekel 		NULL		/* list terminator */
378372a60c3SAndreas Jaekel 	}
379372a60c3SAndreas Jaekel };
380372a60c3SAndreas Jaekel 
381372a60c3SAndreas Jaekel int
_init(void)382372a60c3SAndreas Jaekel _init(void)
383372a60c3SAndreas Jaekel {
384372a60c3SAndreas Jaekel 	int error;
385372a60c3SAndreas Jaekel 
386372a60c3SAndreas Jaekel 	if ((error = ddi_soft_state_init(&statep, sizeof(gg_state_t), 1)) != 0)
387372a60c3SAndreas Jaekel 		return (error);
388372a60c3SAndreas Jaekel 	gg_instances = 0;
389372a60c3SAndreas Jaekel 
390372a60c3SAndreas Jaekel 	mutex_init(&gg_mutex, NULL, MUTEX_DRIVER, NULL);
391372a60c3SAndreas Jaekel 
392372a60c3SAndreas Jaekel 	if ((error = mod_install(&gg_modlinkage)) != 0) {
393372a60c3SAndreas Jaekel 		cmn_err(CE_WARN, "getgen: could not install module");
394372a60c3SAndreas Jaekel 		mutex_destroy(&gg_mutex);
395372a60c3SAndreas Jaekel 		ddi_soft_state_fini(&statep);
396372a60c3SAndreas Jaekel 		return (error);
397372a60c3SAndreas Jaekel 	}
398372a60c3SAndreas Jaekel 	return (0);
399372a60c3SAndreas Jaekel }
400372a60c3SAndreas Jaekel 
401372a60c3SAndreas Jaekel int
_info(struct modinfo * modinfop)402372a60c3SAndreas Jaekel _info(struct modinfo *modinfop)
403372a60c3SAndreas Jaekel {
404372a60c3SAndreas Jaekel 	return (mod_info(&gg_modlinkage, modinfop));
405372a60c3SAndreas Jaekel }
406372a60c3SAndreas Jaekel 
407372a60c3SAndreas Jaekel int
_fini(void)408372a60c3SAndreas Jaekel _fini(void)
409372a60c3SAndreas Jaekel {
410372a60c3SAndreas Jaekel 	int error = 0;
411372a60c3SAndreas Jaekel 
412372a60c3SAndreas Jaekel 	mutex_enter(&gg_mutex);
413372a60c3SAndreas Jaekel 	if (gg_instances > 0) {
414372a60c3SAndreas Jaekel 		mutex_exit(&gg_mutex);
415372a60c3SAndreas Jaekel 		return (SET_ERROR(EBUSY));
416372a60c3SAndreas Jaekel 	}
417372a60c3SAndreas Jaekel 	mutex_exit(&gg_mutex);
418372a60c3SAndreas Jaekel 
419372a60c3SAndreas Jaekel 	if ((error = mod_remove(&gg_modlinkage)) != 0) {
420372a60c3SAndreas Jaekel 		cmn_err(CE_WARN, "mod_remove failed: %d", error);
421372a60c3SAndreas Jaekel 		return (error);
422372a60c3SAndreas Jaekel 	}
423372a60c3SAndreas Jaekel 
424372a60c3SAndreas Jaekel 	/* free resources */
425372a60c3SAndreas Jaekel 	ddi_soft_state_fini(&statep);
426372a60c3SAndreas Jaekel 	mutex_destroy(&gg_mutex);
427372a60c3SAndreas Jaekel 
428372a60c3SAndreas Jaekel 	return (0);
429372a60c3SAndreas Jaekel }
430372a60c3SAndreas Jaekel 
431