1d62bc4baSyz147064 /*
2d62bc4baSyz147064 * CDDL HEADER START
3d62bc4baSyz147064 *
4d62bc4baSyz147064 * The contents of this file are subject to the terms of the
5d62bc4baSyz147064 * Common Development and Distribution License (the "License").
6d62bc4baSyz147064 * You may not use this file except in compliance with the License.
7d62bc4baSyz147064 *
8d62bc4baSyz147064 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d62bc4baSyz147064 * or http://www.opensolaris.org/os/licensing.
10d62bc4baSyz147064 * See the License for the specific language governing permissions
11d62bc4baSyz147064 * and limitations under the License.
12d62bc4baSyz147064 *
13d62bc4baSyz147064 * When distributing Covered Code, include this CDDL HEADER in each
14d62bc4baSyz147064 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d62bc4baSyz147064 * If applicable, add the following below this CDDL HEADER, with the
16d62bc4baSyz147064 * fields enclosed by brackets "[]" replaced with your own identifying
17d62bc4baSyz147064 * information: Portions Copyright [yyyy] [name of copyright owner]
18d62bc4baSyz147064 *
19d62bc4baSyz147064 * CDDL HEADER END
20d62bc4baSyz147064 */
21d62bc4baSyz147064 /*
222b24ab6bSSebastien Roy * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23d62bc4baSyz147064 * Use is subject to license terms.
24d62bc4baSyz147064 */
25d62bc4baSyz147064
26d62bc4baSyz147064 /*
27d62bc4baSyz147064 * vnode ops for the /dev/net directory
28d62bc4baSyz147064 *
29d62bc4baSyz147064 * The lookup is based on the internal vanity naming node table. We also
30d62bc4baSyz147064 * override readdir in order to delete net nodes no longer in-use.
31d62bc4baSyz147064 */
32d62bc4baSyz147064
33d62bc4baSyz147064 #include <sys/types.h>
34d62bc4baSyz147064 #include <sys/param.h>
35d62bc4baSyz147064 #include <sys/sysmacros.h>
36d62bc4baSyz147064 #include <sys/sunndi.h>
37d62bc4baSyz147064 #include <fs/fs_subr.h>
38d62bc4baSyz147064 #include <sys/fs/dv_node.h>
39d62bc4baSyz147064 #include <sys/fs/sdev_impl.h>
40d62bc4baSyz147064 #include <sys/policy.h>
41d62bc4baSyz147064 #include <sys/zone.h>
42d62bc4baSyz147064 #include <sys/dls.h>
43d62bc4baSyz147064
44d62bc4baSyz147064 struct vnodeops *devnet_vnodeops;
45d62bc4baSyz147064
46d62bc4baSyz147064 /*
47d62bc4baSyz147064 * Check if a net sdev_node is still valid - i.e. it represents a current
48d62bc4baSyz147064 * network link.
49d62bc4baSyz147064 * This serves two purposes
50d62bc4baSyz147064 * - only valid net nodes are returned during lookup() and readdir().
51d62bc4baSyz147064 * - since net sdev_nodes are not actively destroyed when a network link
52d62bc4baSyz147064 * goes away, we use the validator to do deferred cleanup i.e. when such
53d62bc4baSyz147064 * nodes are encountered during subsequent lookup() and readdir().
54d62bc4baSyz147064 */
55d62bc4baSyz147064 int
devnet_validate(struct sdev_node * dv)56d62bc4baSyz147064 devnet_validate(struct sdev_node *dv)
57d62bc4baSyz147064 {
58d62bc4baSyz147064 datalink_id_t linkid;
592b24ab6bSSebastien Roy zoneid_t zoneid;
60d62bc4baSyz147064
61d62bc4baSyz147064 ASSERT(dv->sdev_state == SDEV_READY);
62d62bc4baSyz147064
632b24ab6bSSebastien Roy if (dls_mgmt_get_linkid(dv->sdev_name, &linkid) != 0)
642b24ab6bSSebastien Roy return (SDEV_VTOR_INVALID);
652b24ab6bSSebastien Roy if (SDEV_IS_GLOBAL(dv))
662b24ab6bSSebastien Roy return (SDEV_VTOR_VALID);
672b24ab6bSSebastien Roy zoneid = getzoneid();
682b24ab6bSSebastien Roy return (zone_check_datalink(&zoneid, linkid) == 0 ?
692b24ab6bSSebastien Roy SDEV_VTOR_VALID : SDEV_VTOR_INVALID);
70d62bc4baSyz147064 }
71d62bc4baSyz147064
72d62bc4baSyz147064 /*
73d62bc4baSyz147064 * This callback is invoked from devname_lookup_func() to create
74d62bc4baSyz147064 * a net entry when the node is not found in the cache.
75d62bc4baSyz147064 */
76d62bc4baSyz147064 static int
devnet_create_rvp(const char * nm,struct vattr * vap,dls_dl_handle_t * ddhp)77d62bc4baSyz147064 devnet_create_rvp(const char *nm, struct vattr *vap, dls_dl_handle_t *ddhp)
78d62bc4baSyz147064 {
79d62bc4baSyz147064 timestruc_t now;
80d62bc4baSyz147064 dev_t dev;
81d62bc4baSyz147064 int error;
82d62bc4baSyz147064
83d62bc4baSyz147064 if ((error = dls_devnet_open(nm, ddhp, &dev)) != 0) {
84d62bc4baSyz147064 sdcmn_err12(("devnet_create_rvp: not a valid vanity name "
85d62bc4baSyz147064 "network node: %s\n", nm));
86d62bc4baSyz147064 return (error);
87d62bc4baSyz147064 }
88d62bc4baSyz147064
89d62bc4baSyz147064 /*
90d62bc4baSyz147064 * This is a valid network device (at least at this point in time).
91d62bc4baSyz147064 * Create the node by setting the attribute; the rest is taken care
92d62bc4baSyz147064 * of by devname_lookup_func().
93d62bc4baSyz147064 */
94d62bc4baSyz147064 *vap = sdev_vattr_chr;
95d62bc4baSyz147064 vap->va_mode |= 0666;
96d62bc4baSyz147064 vap->va_rdev = dev;
97d62bc4baSyz147064
98d62bc4baSyz147064 gethrestime(&now);
99d62bc4baSyz147064 vap->va_atime = now;
100d62bc4baSyz147064 vap->va_mtime = now;
101d62bc4baSyz147064 vap->va_ctime = now;
102d62bc4baSyz147064 return (0);
103d62bc4baSyz147064 }
104d62bc4baSyz147064
105d62bc4baSyz147064 /*
106d62bc4baSyz147064 * Lookup for /dev/net directory
107d62bc4baSyz147064 * If the entry does not exist, the devnet_create_rvp() callback
108d62bc4baSyz147064 * is invoked to create it. Nodes do not persist across reboot.
109d62bc4baSyz147064 */
110d62bc4baSyz147064 /*ARGSUSED3*/
111d62bc4baSyz147064 static int
devnet_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)112d62bc4baSyz147064 devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
113d62bc4baSyz147064 struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
114d62bc4baSyz147064 caller_context_t *ct, int *direntflags, pathname_t *realpnp)
115d62bc4baSyz147064 {
116d62bc4baSyz147064 struct sdev_node *ddv = VTOSDEV(dvp);
117d62bc4baSyz147064 struct sdev_node *dv = NULL;
118d62bc4baSyz147064 dls_dl_handle_t ddh = NULL;
119d62bc4baSyz147064 struct vattr vattr;
120d62bc4baSyz147064 int nmlen;
121d62bc4baSyz147064 int error = ENOENT;
122d62bc4baSyz147064
123d62bc4baSyz147064 if (SDEVTOV(ddv)->v_type != VDIR)
124d62bc4baSyz147064 return (ENOTDIR);
125d62bc4baSyz147064
126d62bc4baSyz147064 /*
127d62bc4baSyz147064 * Empty name or ., return node itself.
128d62bc4baSyz147064 */
129d62bc4baSyz147064 nmlen = strlen(nm);
130d62bc4baSyz147064 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
131d62bc4baSyz147064 *vpp = SDEVTOV(ddv);
132d62bc4baSyz147064 VN_HOLD(*vpp);
133d62bc4baSyz147064 return (0);
134d62bc4baSyz147064 }
135d62bc4baSyz147064
136d62bc4baSyz147064 /*
137d62bc4baSyz147064 * .., return the parent directory
138d62bc4baSyz147064 */
139d62bc4baSyz147064 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
140d62bc4baSyz147064 *vpp = SDEVTOV(ddv->sdev_dotdot);
141d62bc4baSyz147064 VN_HOLD(*vpp);
142d62bc4baSyz147064 return (0);
143d62bc4baSyz147064 }
144d62bc4baSyz147064
145d62bc4baSyz147064 rw_enter(&ddv->sdev_contents, RW_WRITER);
146d62bc4baSyz147064
147d62bc4baSyz147064 /*
148d62bc4baSyz147064 * directory cache lookup:
149d62bc4baSyz147064 */
150d62bc4baSyz147064 if ((dv = sdev_cache_lookup(ddv, nm)) != NULL) {
151*9e5aa9d8SRobert Mustacchi ASSERT(dv->sdev_state == SDEV_READY);
152d62bc4baSyz147064 if (!(dv->sdev_flags & SDEV_ATTR_INVALID))
153d62bc4baSyz147064 goto found;
154d62bc4baSyz147064 }
155d62bc4baSyz147064
156d62bc4baSyz147064 /*
157d62bc4baSyz147064 * ZOMBIED parent does not allow new node creation, bail out early.
158d62bc4baSyz147064 */
159d62bc4baSyz147064 if (ddv->sdev_state == SDEV_ZOMBIE)
160d62bc4baSyz147064 goto failed;
161d62bc4baSyz147064
162d62bc4baSyz147064 error = devnet_create_rvp(nm, &vattr, &ddh);
163d62bc4baSyz147064 if (error != 0)
164d62bc4baSyz147064 goto failed;
165d62bc4baSyz147064
166d62bc4baSyz147064 error = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL, cred, SDEV_READY);
167d62bc4baSyz147064 if (error != 0) {
168d62bc4baSyz147064 dls_devnet_close(ddh);
169d62bc4baSyz147064 goto failed;
170d62bc4baSyz147064 }
171d62bc4baSyz147064
172d62bc4baSyz147064 ASSERT(dv != NULL);
173d62bc4baSyz147064
174d62bc4baSyz147064 rw_enter(&dv->sdev_contents, RW_WRITER);
175d62bc4baSyz147064 if (dv->sdev_flags & SDEV_ATTR_INVALID) {
176d62bc4baSyz147064 /*
177d62bc4baSyz147064 * SDEV_ATTR_INVALID means that this device has been
178d62bc4baSyz147064 * detached, and its dev_t might've been changed too.
179d62bc4baSyz147064 * Therefore, sdev_node's 'vattr' needs to be updated.
180d62bc4baSyz147064 */
181d62bc4baSyz147064 SDEVTOV(dv)->v_rdev = vattr.va_rdev;
182d62bc4baSyz147064 ASSERT(dv->sdev_attr != NULL);
183d62bc4baSyz147064 dv->sdev_attr->va_rdev = vattr.va_rdev;
184d62bc4baSyz147064 dv->sdev_flags &= ~SDEV_ATTR_INVALID;
185d62bc4baSyz147064 }
186d62bc4baSyz147064 ASSERT(dv->sdev_private == NULL);
187d62bc4baSyz147064 dv->sdev_private = ddh;
188d62bc4baSyz147064 rw_exit(&dv->sdev_contents);
189d62bc4baSyz147064
190d62bc4baSyz147064 found:
191d62bc4baSyz147064 ASSERT(SDEV_HELD(dv));
192d62bc4baSyz147064 rw_exit(&ddv->sdev_contents);
193d62bc4baSyz147064 return (sdev_to_vp(dv, vpp));
194d62bc4baSyz147064
195d62bc4baSyz147064 failed:
196d62bc4baSyz147064 rw_exit(&ddv->sdev_contents);
197d62bc4baSyz147064
198d62bc4baSyz147064 if (dv != NULL)
199d62bc4baSyz147064 SDEV_RELE(dv);
200d62bc4baSyz147064
201d62bc4baSyz147064 *vpp = NULL;
202d62bc4baSyz147064 return (error);
203d62bc4baSyz147064 }
204d62bc4baSyz147064
205d62bc4baSyz147064 static int
devnet_filldir_datalink(datalink_id_t linkid,void * arg)2062b24ab6bSSebastien Roy devnet_filldir_datalink(datalink_id_t linkid, void *arg)
207d62bc4baSyz147064 {
208d62bc4baSyz147064 struct sdev_node *ddv = arg;
209d62bc4baSyz147064 struct vattr vattr;
210d62bc4baSyz147064 struct sdev_node *dv;
211d62bc4baSyz147064 dls_dl_handle_t ddh = NULL;
2122b24ab6bSSebastien Roy char link[MAXLINKNAMELEN];
213d62bc4baSyz147064
214d62bc4baSyz147064 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
2152b24ab6bSSebastien Roy
2162b24ab6bSSebastien Roy if (dls_mgmt_get_linkinfo(linkid, link, NULL, NULL, NULL) != 0)
2172b24ab6bSSebastien Roy return (0);
2182b24ab6bSSebastien Roy
219d62bc4baSyz147064 if ((dv = sdev_cache_lookup(ddv, (char *)link)) != NULL)
220d62bc4baSyz147064 goto found;
221d62bc4baSyz147064
222d62bc4baSyz147064 if (devnet_create_rvp(link, &vattr, &ddh) != 0)
223d62bc4baSyz147064 return (0);
224d62bc4baSyz147064
225d62bc4baSyz147064 ASSERT(ddh != NULL);
226d62bc4baSyz147064 dls_devnet_close(ddh);
227d62bc4baSyz147064
228d62bc4baSyz147064 if (sdev_mknode(ddv, (char *)link, &dv, &vattr, NULL, NULL, kcred,
229d62bc4baSyz147064 SDEV_READY) != 0) {
230d62bc4baSyz147064 return (0);
231d62bc4baSyz147064 }
232d62bc4baSyz147064
233d62bc4baSyz147064 /*
234d62bc4baSyz147064 * As there is no reference holding the network device, it could be
235d62bc4baSyz147064 * detached. Set SDEV_ATTR_INVALID so that the 'vattr' will be updated
236d62bc4baSyz147064 * later.
237d62bc4baSyz147064 */
238d62bc4baSyz147064 rw_enter(&dv->sdev_contents, RW_WRITER);
239d62bc4baSyz147064 dv->sdev_flags |= SDEV_ATTR_INVALID;
240d62bc4baSyz147064 rw_exit(&dv->sdev_contents);
241d62bc4baSyz147064
242d62bc4baSyz147064 found:
243d62bc4baSyz147064 SDEV_SIMPLE_RELE(dv);
244d62bc4baSyz147064 return (0);
245d62bc4baSyz147064 }
246d62bc4baSyz147064
247d62bc4baSyz147064 static void
devnet_filldir(struct sdev_node * ddv)248d62bc4baSyz147064 devnet_filldir(struct sdev_node *ddv)
249d62bc4baSyz147064 {
250d62bc4baSyz147064 sdev_node_t *dv, *next;
251d62bc4baSyz147064 datalink_id_t linkid;
252d62bc4baSyz147064
253d62bc4baSyz147064 ASSERT(RW_READ_HELD(&ddv->sdev_contents));
254d62bc4baSyz147064 if (rw_tryupgrade(&ddv->sdev_contents) == NULL) {
255d62bc4baSyz147064 rw_exit(&ddv->sdev_contents);
256d62bc4baSyz147064 rw_enter(&ddv->sdev_contents, RW_WRITER);
257d62bc4baSyz147064 }
258d62bc4baSyz147064
259aac43a5fSjg for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
260aac43a5fSjg next = SDEV_NEXT_ENTRY(ddv, dv);
261d62bc4baSyz147064
262d62bc4baSyz147064 /* validate and prune only ready nodes */
263d62bc4baSyz147064 if (dv->sdev_state != SDEV_READY)
264d62bc4baSyz147064 continue;
265d62bc4baSyz147064
266d62bc4baSyz147064 switch (devnet_validate(dv)) {
267d62bc4baSyz147064 case SDEV_VTOR_VALID:
268d62bc4baSyz147064 case SDEV_VTOR_SKIP:
269d62bc4baSyz147064 continue;
270d62bc4baSyz147064 case SDEV_VTOR_INVALID:
271b127ac41SPhilip Kirk case SDEV_VTOR_STALE:
272d62bc4baSyz147064 sdcmn_err12(("devnet_filldir: destroy invalid "
273d62bc4baSyz147064 "node: %s(%p)\n", dv->sdev_name, (void *)dv));
274d62bc4baSyz147064 break;
275d62bc4baSyz147064 }
276d62bc4baSyz147064
277d62bc4baSyz147064 if (SDEVTOV(dv)->v_count > 0)
278d62bc4baSyz147064 continue;
279d62bc4baSyz147064 SDEV_HOLD(dv);
280d62bc4baSyz147064 /* remove the cache node */
281d62bc4baSyz147064 (void) sdev_cache_update(ddv, &dv, dv->sdev_name,
282d62bc4baSyz147064 SDEV_CACHE_DELETE);
283*9e5aa9d8SRobert Mustacchi SDEV_RELE(dv);
284d62bc4baSyz147064 }
285d62bc4baSyz147064
286d62bc4baSyz147064 if (((ddv->sdev_flags & SDEV_BUILD) == 0) && !dls_devnet_rebuild())
287d62bc4baSyz147064 goto done;
288d62bc4baSyz147064
289d62bc4baSyz147064 if (SDEV_IS_GLOBAL(ddv)) {
290d62bc4baSyz147064 linkid = DATALINK_INVALID_LINKID;
291d62bc4baSyz147064 do {
292d62bc4baSyz147064 linkid = dls_mgmt_get_next(linkid, DATALINK_CLASS_ALL,
293d62bc4baSyz147064 DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE);
2942b24ab6bSSebastien Roy if (linkid != DATALINK_INVALID_LINKID)
2952b24ab6bSSebastien Roy (void) devnet_filldir_datalink(linkid, ddv);
296d62bc4baSyz147064 } while (linkid != DATALINK_INVALID_LINKID);
297d62bc4baSyz147064 } else {
298d62bc4baSyz147064 (void) zone_datalink_walk(getzoneid(),
299d62bc4baSyz147064 devnet_filldir_datalink, ddv);
300d62bc4baSyz147064 }
301d62bc4baSyz147064
302d62bc4baSyz147064 ddv->sdev_flags &= ~SDEV_BUILD;
303d62bc4baSyz147064
304d62bc4baSyz147064 done:
305d62bc4baSyz147064 rw_downgrade(&ddv->sdev_contents);
306d62bc4baSyz147064 }
307d62bc4baSyz147064
308d62bc4baSyz147064 /*
309d62bc4baSyz147064 * Display all instantiated network datalink device nodes.
310d62bc4baSyz147064 * A /dev/net entry will be created only after the first lookup of
311d62bc4baSyz147064 * the network datalink device succeeds.
312d62bc4baSyz147064 */
313d62bc4baSyz147064 /*ARGSUSED4*/
314d62bc4baSyz147064 static int
devnet_readdir(struct vnode * dvp,struct uio * uiop,struct cred * cred,int * eofp,caller_context_t * ct,int flags)315d62bc4baSyz147064 devnet_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
316d62bc4baSyz147064 int *eofp, caller_context_t *ct, int flags)
317d62bc4baSyz147064 {
318d62bc4baSyz147064 struct sdev_node *sdvp = VTOSDEV(dvp);
319d62bc4baSyz147064
320d62bc4baSyz147064 ASSERT(sdvp);
321d62bc4baSyz147064
322d62bc4baSyz147064 if (uiop->uio_offset == 0)
323d62bc4baSyz147064 devnet_filldir(sdvp);
324d62bc4baSyz147064
325d62bc4baSyz147064 return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
326d62bc4baSyz147064 }
327d62bc4baSyz147064
328d62bc4baSyz147064 /*
329d62bc4baSyz147064 * This callback is invoked from devname_inactive_func() to release
330d62bc4baSyz147064 * the net entry which was held in devnet_create_rvp().
331d62bc4baSyz147064 */
332d62bc4baSyz147064 static void
devnet_inactive_callback(struct vnode * dvp)333d62bc4baSyz147064 devnet_inactive_callback(struct vnode *dvp)
334d62bc4baSyz147064 {
335d62bc4baSyz147064 struct sdev_node *sdvp = VTOSDEV(dvp);
336d62bc4baSyz147064 dls_dl_handle_t ddh;
337d62bc4baSyz147064
338d62bc4baSyz147064 if (dvp->v_type == VDIR)
339d62bc4baSyz147064 return;
340d62bc4baSyz147064
341d62bc4baSyz147064 ASSERT(dvp->v_type == VCHR);
342d62bc4baSyz147064 rw_enter(&sdvp->sdev_contents, RW_WRITER);
343d62bc4baSyz147064 ddh = sdvp->sdev_private;
344d62bc4baSyz147064 sdvp->sdev_private = NULL;
345d62bc4baSyz147064 sdvp->sdev_flags |= SDEV_ATTR_INVALID;
346d62bc4baSyz147064 rw_exit(&sdvp->sdev_contents);
347d62bc4baSyz147064
348d62bc4baSyz147064 /*
349d62bc4baSyz147064 * "ddh" (sdev_private) could be NULL if devnet_lookup fails.
350d62bc4baSyz147064 */
351d62bc4baSyz147064 if (ddh != NULL)
352d62bc4baSyz147064 dls_devnet_close(ddh);
353d62bc4baSyz147064 }
354d62bc4baSyz147064
355d62bc4baSyz147064 /*ARGSUSED*/
356d62bc4baSyz147064 static void
devnet_inactive(struct vnode * dvp,struct cred * cred,caller_context_t * ct)357d62bc4baSyz147064 devnet_inactive(struct vnode *dvp, struct cred *cred, caller_context_t *ct)
358d62bc4baSyz147064 {
359d62bc4baSyz147064 devname_inactive_func(dvp, cred, devnet_inactive_callback);
360d62bc4baSyz147064 }
361d62bc4baSyz147064
362d62bc4baSyz147064 /*
363d62bc4baSyz147064 * We override lookup and readdir to build entries based on the
364d62bc4baSyz147064 * in kernel vanity naming node table.
365d62bc4baSyz147064 */
366d62bc4baSyz147064 const fs_operation_def_t devnet_vnodeops_tbl[] = {
367d62bc4baSyz147064 VOPNAME_READDIR, { .vop_readdir = devnet_readdir },
368d62bc4baSyz147064 VOPNAME_LOOKUP, { .vop_lookup = devnet_lookup },
369d62bc4baSyz147064 VOPNAME_INACTIVE, { .vop_inactive = devnet_inactive },
370d62bc4baSyz147064 VOPNAME_CREATE, { .error = fs_nosys },
371d62bc4baSyz147064 VOPNAME_REMOVE, { .error = fs_nosys },
372d62bc4baSyz147064 VOPNAME_MKDIR, { .error = fs_nosys },
373d62bc4baSyz147064 VOPNAME_RMDIR, { .error = fs_nosys },
374d62bc4baSyz147064 VOPNAME_SYMLINK, { .error = fs_nosys },
375d62bc4baSyz147064 VOPNAME_SETSECATTR, { .error = fs_nosys },
376d62bc4baSyz147064 NULL, NULL
377d62bc4baSyz147064 };
378