1b127ac41SPhilip Kirk /*
2b127ac41SPhilip Kirk * CDDL HEADER START
3b127ac41SPhilip Kirk *
4b127ac41SPhilip Kirk * The contents of this file are subject to the terms of the
5b127ac41SPhilip Kirk * Common Development and Distribution License (the "License").
6b127ac41SPhilip Kirk * You may not use this file except in compliance with the License.
7b127ac41SPhilip Kirk *
8b127ac41SPhilip Kirk * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9b127ac41SPhilip Kirk * or http://www.opensolaris.org/os/licensing.
10b127ac41SPhilip Kirk * See the License for the specific language governing permissions
11b127ac41SPhilip Kirk * and limitations under the License.
12b127ac41SPhilip Kirk *
13b127ac41SPhilip Kirk * When distributing Covered Code, include this CDDL HEADER in each
14b127ac41SPhilip Kirk * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15b127ac41SPhilip Kirk * If applicable, add the following below this CDDL HEADER, with the
16b127ac41SPhilip Kirk * fields enclosed by brackets "[]" replaced with your own identifying
17b127ac41SPhilip Kirk * information: Portions Copyright [yyyy] [name of copyright owner]
18b127ac41SPhilip Kirk *
19b127ac41SPhilip Kirk * CDDL HEADER END
20b127ac41SPhilip Kirk */
21b127ac41SPhilip Kirk /*
22b127ac41SPhilip Kirk * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23b127ac41SPhilip Kirk * Use is subject to license terms.
24b127ac41SPhilip Kirk */
25b127ac41SPhilip Kirk
26b127ac41SPhilip Kirk /*
27b127ac41SPhilip Kirk * vnode ops for the /dev/ipnet directory
28b127ac41SPhilip Kirk * The lookup is based on the ipnetif nodes held
29b127ac41SPhilip Kirk * in the ipnet module. We also override readdir
30b127ac41SPhilip Kirk * in order to delete ipnet nodes no longer in use.
31b127ac41SPhilip Kirk */
32b127ac41SPhilip Kirk
33b127ac41SPhilip Kirk #include <sys/types.h>
34b127ac41SPhilip Kirk #include <sys/param.h>
35b127ac41SPhilip Kirk #include <sys/sysmacros.h>
36b127ac41SPhilip Kirk #include <sys/sunndi.h>
37b127ac41SPhilip Kirk #include <fs/fs_subr.h>
38b127ac41SPhilip Kirk #include <sys/fs/dv_node.h>
39b127ac41SPhilip Kirk #include <sys/fs/sdev_impl.h>
40b127ac41SPhilip Kirk #include <sys/policy.h>
41b127ac41SPhilip Kirk #include <inet/ipnet.h>
42b127ac41SPhilip Kirk #include <sys/zone.h>
43b127ac41SPhilip Kirk
44b127ac41SPhilip Kirk struct vnodeops *devipnet_vnodeops;
45b127ac41SPhilip Kirk
46b127ac41SPhilip Kirk static void
devipnet_fill_vattr(struct vattr * vap,dev_t dev)47b127ac41SPhilip Kirk devipnet_fill_vattr(struct vattr *vap, dev_t dev)
48b127ac41SPhilip Kirk {
49b127ac41SPhilip Kirk timestruc_t now;
50b127ac41SPhilip Kirk
51b127ac41SPhilip Kirk *vap = sdev_vattr_chr;
52b127ac41SPhilip Kirk vap->va_rdev = dev;
53b127ac41SPhilip Kirk vap->va_mode |= 0666;
54b127ac41SPhilip Kirk
55b127ac41SPhilip Kirk gethrestime(&now);
56b127ac41SPhilip Kirk vap->va_atime = now;
57b127ac41SPhilip Kirk vap->va_mtime = now;
58b127ac41SPhilip Kirk vap->va_ctime = now;
59b127ac41SPhilip Kirk }
60b127ac41SPhilip Kirk
61b127ac41SPhilip Kirk /*
62b127ac41SPhilip Kirk * Check if an ipnet sdev_node is still valid.
63b127ac41SPhilip Kirk */
64b127ac41SPhilip Kirk int
devipnet_validate(struct sdev_node * dv)65b127ac41SPhilip Kirk devipnet_validate(struct sdev_node *dv)
66b127ac41SPhilip Kirk {
67b127ac41SPhilip Kirk dev_t dev;
68b127ac41SPhilip Kirk
69b127ac41SPhilip Kirk dev = ipnet_if_getdev(dv->sdev_name, getzoneid());
70b127ac41SPhilip Kirk if (dev == (dev_t)-1)
71b127ac41SPhilip Kirk return (SDEV_VTOR_INVALID);
72b127ac41SPhilip Kirk if (getminor(SDEVTOV(dv)->v_rdev) != getminor(dev))
73b127ac41SPhilip Kirk return (SDEV_VTOR_STALE);
74b127ac41SPhilip Kirk return (SDEV_VTOR_VALID);
75b127ac41SPhilip Kirk }
76b127ac41SPhilip Kirk
77b127ac41SPhilip Kirk /*
78b127ac41SPhilip Kirk * This callback is invoked from devname_lookup_func() to create
79b127ac41SPhilip Kirk * an ipnet entry when the node is not found in the cache.
80b127ac41SPhilip Kirk */
81b127ac41SPhilip Kirk /*ARGSUSED*/
82b127ac41SPhilip Kirk static int
devipnet_create_rvp(struct sdev_node * ddv,char * nm,void ** arg,cred_t * cred,void * whatever,char * whichever)83b127ac41SPhilip Kirk devipnet_create_rvp(struct sdev_node *ddv, char *nm,
84b127ac41SPhilip Kirk void **arg, cred_t *cred, void *whatever, char *whichever)
85b127ac41SPhilip Kirk {
86b127ac41SPhilip Kirk dev_t dev;
87b127ac41SPhilip Kirk struct vattr *vap = (struct vattr *)arg;
88b127ac41SPhilip Kirk int err = 0;
89b127ac41SPhilip Kirk
90b127ac41SPhilip Kirk if ((dev = ipnet_if_getdev(nm, getzoneid())) == (dev_t)-1)
91b127ac41SPhilip Kirk err = ENOENT;
92b127ac41SPhilip Kirk else
93b127ac41SPhilip Kirk devipnet_fill_vattr(vap, dev);
94b127ac41SPhilip Kirk
95b127ac41SPhilip Kirk return (err);
96b127ac41SPhilip Kirk }
97b127ac41SPhilip Kirk
98b127ac41SPhilip Kirk /*
99b127ac41SPhilip Kirk * Lookup for /dev/ipnet directory
100b127ac41SPhilip Kirk * If the entry does not exist, the devipnet_create_rvp() callback
101b127ac41SPhilip Kirk * is invoked to create it. Nodes do not persist across reboot.
102b127ac41SPhilip Kirk */
103b127ac41SPhilip Kirk /*ARGSUSED3*/
104b127ac41SPhilip Kirk static int
devipnet_lookup(struct vnode * dvp,char * nm,struct vnode ** vpp,struct pathname * pnp,int flags,struct vnode * rdir,struct cred * cred,caller_context_t * ct,int * direntflags,pathname_t * realpnp)105b127ac41SPhilip Kirk devipnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
106b127ac41SPhilip Kirk struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
107b127ac41SPhilip Kirk caller_context_t *ct, int *direntflags, pathname_t *realpnp)
108b127ac41SPhilip Kirk {
109b127ac41SPhilip Kirk struct sdev_node *sdvp = VTOSDEV(dvp);
110b127ac41SPhilip Kirk struct sdev_node *dv;
111b127ac41SPhilip Kirk struct vnode *rvp = NULL;
112b127ac41SPhilip Kirk int error;
113b127ac41SPhilip Kirk
114b127ac41SPhilip Kirk error = devname_lookup_func(sdvp, nm, vpp, cred, devipnet_create_rvp,
115b127ac41SPhilip Kirk SDEV_VATTR);
116b127ac41SPhilip Kirk
117b127ac41SPhilip Kirk if (error == 0) {
118b127ac41SPhilip Kirk switch ((*vpp)->v_type) {
119b127ac41SPhilip Kirk case VCHR:
120b127ac41SPhilip Kirk dv = VTOSDEV(VTOS(*vpp)->s_realvp);
121b127ac41SPhilip Kirk ASSERT(VOP_REALVP(SDEVTOV(dv), &rvp, NULL) == ENOSYS);
122b127ac41SPhilip Kirk break;
123b127ac41SPhilip Kirk case VDIR:
124b127ac41SPhilip Kirk dv = VTOSDEV(*vpp);
125b127ac41SPhilip Kirk break;
126b127ac41SPhilip Kirk default:
127b127ac41SPhilip Kirk cmn_err(CE_PANIC, "devipnet_lookup: Unsupported node "
128b127ac41SPhilip Kirk "type: %p: %d", (void *)(*vpp), (*vpp)->v_type);
129b127ac41SPhilip Kirk break;
130b127ac41SPhilip Kirk }
131b127ac41SPhilip Kirk ASSERT(SDEV_HELD(dv));
132b127ac41SPhilip Kirk }
133b127ac41SPhilip Kirk
134b127ac41SPhilip Kirk return (error);
135b127ac41SPhilip Kirk }
136b127ac41SPhilip Kirk
137b127ac41SPhilip Kirk static void
devipnet_filldir_entry(const char * name,void * arg,dev_t dev)138b127ac41SPhilip Kirk devipnet_filldir_entry(const char *name, void *arg, dev_t dev)
139b127ac41SPhilip Kirk {
140b127ac41SPhilip Kirk struct sdev_node *ddv = arg;
141b127ac41SPhilip Kirk struct vattr vattr;
142b127ac41SPhilip Kirk struct sdev_node *dv;
143b127ac41SPhilip Kirk
144b127ac41SPhilip Kirk ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
145b127ac41SPhilip Kirk
146b127ac41SPhilip Kirk if ((dv = sdev_cache_lookup(ddv, (char *)name)) == NULL) {
147b127ac41SPhilip Kirk devipnet_fill_vattr(&vattr, dev);
148b127ac41SPhilip Kirk if (sdev_mknode(ddv, (char *)name, &dv, &vattr, NULL, NULL,
149b127ac41SPhilip Kirk kcred, SDEV_READY) != 0)
150b127ac41SPhilip Kirk return;
151b127ac41SPhilip Kirk }
152b127ac41SPhilip Kirk SDEV_SIMPLE_RELE(dv);
153b127ac41SPhilip Kirk }
154b127ac41SPhilip Kirk
155b127ac41SPhilip Kirk static void
devipnet_filldir(struct sdev_node * ddv)156b127ac41SPhilip Kirk devipnet_filldir(struct sdev_node *ddv)
157b127ac41SPhilip Kirk {
158b127ac41SPhilip Kirk sdev_node_t *dv, *next;
159b127ac41SPhilip Kirk
160b127ac41SPhilip Kirk ASSERT(RW_READ_HELD(&ddv->sdev_contents));
161b127ac41SPhilip Kirk if (rw_tryupgrade(&ddv->sdev_contents) == NULL) {
162b127ac41SPhilip Kirk rw_exit(&ddv->sdev_contents);
163b127ac41SPhilip Kirk rw_enter(&ddv->sdev_contents, RW_WRITER);
164*9e5aa9d8SRobert Mustacchi /*
165*9e5aa9d8SRobert Mustacchi * We've been made a zombie while we weren't looking. We'll bail
166*9e5aa9d8SRobert Mustacchi * if that's the case.
167*9e5aa9d8SRobert Mustacchi */
168*9e5aa9d8SRobert Mustacchi if (ddv->sdev_state == SDEV_ZOMBIE) {
169*9e5aa9d8SRobert Mustacchi rw_exit(&ddv->sdev_contents);
170*9e5aa9d8SRobert Mustacchi return;
171*9e5aa9d8SRobert Mustacchi }
172b127ac41SPhilip Kirk }
173b127ac41SPhilip Kirk
174b127ac41SPhilip Kirk for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
175b127ac41SPhilip Kirk next = SDEV_NEXT_ENTRY(ddv, dv);
176b127ac41SPhilip Kirk
177b127ac41SPhilip Kirk /* validate and prune only ready nodes */
178b127ac41SPhilip Kirk if (dv->sdev_state != SDEV_READY)
179b127ac41SPhilip Kirk continue;
180b127ac41SPhilip Kirk switch (devipnet_validate(dv)) {
181b127ac41SPhilip Kirk case SDEV_VTOR_VALID:
182b127ac41SPhilip Kirk case SDEV_VTOR_SKIP:
183b127ac41SPhilip Kirk continue;
184b127ac41SPhilip Kirk case SDEV_VTOR_INVALID:
185b127ac41SPhilip Kirk case SDEV_VTOR_STALE:
186b127ac41SPhilip Kirk sdcmn_err12(("devipnet_filldir: destroy invalid "
187b127ac41SPhilip Kirk "node: %s(%p)\n", dv->sdev_name, (void *)dv));
188b127ac41SPhilip Kirk break;
189b127ac41SPhilip Kirk }
190b127ac41SPhilip Kirk
191b127ac41SPhilip Kirk if (SDEVTOV(dv)->v_count > 0)
192b127ac41SPhilip Kirk continue;
193b127ac41SPhilip Kirk SDEV_HOLD(dv);
194b127ac41SPhilip Kirk /* remove the cache node */
195b127ac41SPhilip Kirk (void) sdev_cache_update(ddv, &dv, dv->sdev_name,
196b127ac41SPhilip Kirk SDEV_CACHE_DELETE);
197*9e5aa9d8SRobert Mustacchi SDEV_RELE(dv);
198b127ac41SPhilip Kirk }
199b127ac41SPhilip Kirk
200b127ac41SPhilip Kirk ipnet_walk_if(devipnet_filldir_entry, ddv, getzoneid());
201b127ac41SPhilip Kirk
202b127ac41SPhilip Kirk rw_downgrade(&ddv->sdev_contents);
203b127ac41SPhilip Kirk }
204b127ac41SPhilip Kirk
205b127ac41SPhilip Kirk /*
206b127ac41SPhilip Kirk * Display all instantiated ipnet device nodes.
207b127ac41SPhilip Kirk */
208b127ac41SPhilip Kirk /* ARGSUSED */
209b127ac41SPhilip Kirk static int
devipnet_readdir(struct vnode * dvp,struct uio * uiop,struct cred * cred,int * eofp,caller_context_t * ct,int flags)210b127ac41SPhilip Kirk devipnet_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
211b127ac41SPhilip Kirk int *eofp, caller_context_t *ct, int flags)
212b127ac41SPhilip Kirk {
213b127ac41SPhilip Kirk struct sdev_node *sdvp = VTOSDEV(dvp);
214b127ac41SPhilip Kirk
215b127ac41SPhilip Kirk if (uiop->uio_offset == 0)
216b127ac41SPhilip Kirk devipnet_filldir(sdvp);
217b127ac41SPhilip Kirk
218b127ac41SPhilip Kirk return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
219b127ac41SPhilip Kirk }
220b127ac41SPhilip Kirk
221b127ac41SPhilip Kirk /*
222b127ac41SPhilip Kirk * We override lookup and readdir to build entries based on the
223b127ac41SPhilip Kirk * in kernel ipnet table.
224b127ac41SPhilip Kirk */
225b127ac41SPhilip Kirk const fs_operation_def_t devipnet_vnodeops_tbl[] = {
226b127ac41SPhilip Kirk VOPNAME_READDIR, { .vop_readdir = devipnet_readdir },
227b127ac41SPhilip Kirk VOPNAME_LOOKUP, { .vop_lookup = devipnet_lookup },
228b127ac41SPhilip Kirk VOPNAME_CREATE, { .error = fs_nosys },
229b127ac41SPhilip Kirk VOPNAME_REMOVE, { .error = fs_nosys },
230b127ac41SPhilip Kirk VOPNAME_MKDIR, { .error = fs_nosys },
231b127ac41SPhilip Kirk VOPNAME_RMDIR, { .error = fs_nosys },
232b127ac41SPhilip Kirk VOPNAME_SYMLINK, { .error = fs_nosys },
233b127ac41SPhilip Kirk VOPNAME_SETSECATTR, { .error = fs_nosys },
234b127ac41SPhilip Kirk NULL, NULL
235b127ac41SPhilip Kirk };
236