4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * vnode ops for the /dev/net directory
29 * The lookup is based on the internal vanity naming node table. We also
30 * override readdir in order to delete net nodes no longer in-use.
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include <sys/sysmacros.h>
36 #include <sys/sunndi.h>
37 #include <sys/fs_subr.h>
38 #include <sys/fs/dv_node.h>
39 #include <sys/fs/sdev_impl.h>
40 #include <sys/policy.h>
43 #include "sdev_vnops.h"
46 * Check if a net sdev_node is still valid - i.e. it represents a current
48 * This serves two purposes
49 * - only valid net nodes are returned during lookup() and readdir().
50 * - since net sdev_nodes are not actively destroyed when a network link
51 * goes away, we use the validator to do deferred cleanup i.e. when such
52 * nodes are encountered during subsequent lookup() and readdir().
55 devnet_validate(struct sdev_node
*dv
)
60 ASSERT(dv
->sdev_state
== SDEV_READY
);
62 if (dls_mgmt_get_linkid(dv
->sdev_name
, &linkid
) != 0)
63 return (SDEV_VTOR_INVALID
);
64 if (SDEV_IS_GLOBAL(dv
))
65 return (SDEV_VTOR_VALID
);
67 return (zone_check_datalink(&zoneid
, linkid
) == 0 ?
68 SDEV_VTOR_VALID
: SDEV_VTOR_INVALID
);
72 * This callback is invoked from devname_lookup_func() to create
73 * a net entry when the node is not found in the cache.
76 devnet_create_rvp(const char *nm
, struct vattr
*vap
, dls_dl_handle_t
*ddhp
)
82 if ((error
= dls_devnet_open(nm
, ddhp
, &dev
)) != 0) {
83 sdcmn_err12(("devnet_create_rvp: not a valid vanity name "
84 "network node: %s\n", nm
));
89 * This is a valid network device (at least at this point in time).
90 * Create the node by setting the attribute; the rest is taken care
91 * of by devname_lookup_func().
93 *vap
= sdev_vattr_chr
;
105 * Lookup for /dev/net directory
106 * If the entry does not exist, the devnet_create_rvp() callback
107 * is invoked to create it. Nodes do not persist across reboot.
111 devnet_lookup(struct vnode
*dvp
, char *nm
, struct vnode
**vpp
,
112 struct pathname
*pnp
, int flags
, struct vnode
*rdir
, struct cred
*cred
,
113 caller_context_t
*ct
, int *direntflags
, pathname_t
*realpnp
)
115 struct sdev_node
*ddv
= VTOSDEV(dvp
);
116 struct sdev_node
*dv
= NULL
;
117 dls_dl_handle_t ddh
= NULL
;
122 if (SDEVTOV(ddv
)->v_type
!= VDIR
)
126 * Empty name or ., return node itself.
129 if ((nmlen
== 0) || ((nmlen
== 1) && (nm
[0] == '.'))) {
136 * .., return the parent directory
138 if ((nmlen
== 2) && (strcmp(nm
, "..") == 0)) {
139 *vpp
= SDEVTOV(ddv
->sdev_dotdot
);
144 rw_enter(&ddv
->sdev_contents
, RW_WRITER
);
147 * directory cache lookup:
149 if ((dv
= sdev_cache_lookup(ddv
, nm
)) != NULL
) {
150 ASSERT(dv
->sdev_state
== SDEV_READY
);
151 if (!(dv
->sdev_flags
& SDEV_ATTR_INVALID
))
156 * ZOMBIED parent does not allow new node creation, bail out early.
158 if (ddv
->sdev_state
== SDEV_ZOMBIE
)
161 error
= devnet_create_rvp(nm
, &vattr
, &ddh
);
165 error
= sdev_mknode(ddv
, nm
, &dv
, &vattr
, NULL
, NULL
, cred
, SDEV_READY
);
167 dls_devnet_close(ddh
);
173 rw_enter(&dv
->sdev_contents
, RW_WRITER
);
174 if (dv
->sdev_flags
& SDEV_ATTR_INVALID
) {
176 * SDEV_ATTR_INVALID means that this device has been
177 * detached, and its dev_t might've been changed too.
178 * Therefore, sdev_node's 'vattr' needs to be updated.
180 SDEVTOV(dv
)->v_rdev
= vattr
.va_rdev
;
181 ASSERT(dv
->sdev_attr
!= NULL
);
182 dv
->sdev_attr
->va_rdev
= vattr
.va_rdev
;
183 dv
->sdev_flags
&= ~SDEV_ATTR_INVALID
;
185 ASSERT(dv
->sdev_private
== NULL
);
186 dv
->sdev_private
= ddh
;
187 rw_exit(&dv
->sdev_contents
);
190 ASSERT(SDEV_HELD(dv
));
191 rw_exit(&ddv
->sdev_contents
);
192 return (sdev_to_vp(dv
, vpp
));
195 rw_exit(&ddv
->sdev_contents
);
205 devnet_filldir_datalink(datalink_id_t linkid
, void *arg
)
207 struct sdev_node
*ddv
= arg
;
209 struct sdev_node
*dv
;
210 dls_dl_handle_t ddh
= NULL
;
211 char link
[MAXLINKNAMELEN
];
213 ASSERT(RW_WRITE_HELD(&ddv
->sdev_contents
));
215 if (dls_mgmt_get_linkinfo(linkid
, link
, NULL
, NULL
, NULL
) != 0)
218 if ((dv
= sdev_cache_lookup(ddv
, (char *)link
)) != NULL
)
221 if (devnet_create_rvp(link
, &vattr
, &ddh
) != 0)
225 dls_devnet_close(ddh
);
227 if (sdev_mknode(ddv
, (char *)link
, &dv
, &vattr
, NULL
, NULL
, kcred
,
233 * As there is no reference holding the network device, it could be
234 * detached. Set SDEV_ATTR_INVALID so that the 'vattr' will be updated
237 rw_enter(&dv
->sdev_contents
, RW_WRITER
);
238 dv
->sdev_flags
|= SDEV_ATTR_INVALID
;
239 rw_exit(&dv
->sdev_contents
);
242 SDEV_SIMPLE_RELE(dv
);
247 devnet_filldir(struct sdev_node
*ddv
)
249 sdev_node_t
*dv
, *next
;
250 datalink_id_t linkid
;
252 ASSERT(RW_READ_HELD(&ddv
->sdev_contents
));
253 if (rw_tryupgrade(&ddv
->sdev_contents
) == 0) {
254 rw_exit(&ddv
->sdev_contents
);
255 rw_enter(&ddv
->sdev_contents
, RW_WRITER
);
258 for (dv
= SDEV_FIRST_ENTRY(ddv
); dv
; dv
= next
) {
259 next
= SDEV_NEXT_ENTRY(ddv
, dv
);
261 /* validate and prune only ready nodes */
262 if (dv
->sdev_state
!= SDEV_READY
)
265 switch (devnet_validate(dv
)) {
266 case SDEV_VTOR_VALID
:
269 case SDEV_VTOR_INVALID
:
270 case SDEV_VTOR_STALE
:
271 sdcmn_err12(("devnet_filldir: destroy invalid "
272 "node: %s(%p)\n", dv
->sdev_name
, (void *)dv
));
276 if (SDEVTOV(dv
)->v_count
> 0)
279 /* remove the cache node */
280 (void) sdev_cache_update(ddv
, &dv
, dv
->sdev_name
,
285 if (((ddv
->sdev_flags
& SDEV_BUILD
) == 0) && !dls_devnet_rebuild())
288 if (SDEV_IS_GLOBAL(ddv
)) {
289 linkid
= DATALINK_INVALID_LINKID
;
291 linkid
= dls_mgmt_get_next(linkid
, DATALINK_CLASS_ALL
,
292 DATALINK_ANY_MEDIATYPE
, DLMGMT_ACTIVE
);
293 if (linkid
!= DATALINK_INVALID_LINKID
)
294 (void) devnet_filldir_datalink(linkid
, ddv
);
295 } while (linkid
!= DATALINK_INVALID_LINKID
);
297 (void) zone_datalink_walk(getzoneid(),
298 devnet_filldir_datalink
, ddv
);
301 ddv
->sdev_flags
&= ~SDEV_BUILD
;
304 rw_downgrade(&ddv
->sdev_contents
);
308 * Display all instantiated network datalink device nodes.
309 * A /dev/net entry will be created only after the first lookup of
310 * the network datalink device succeeds.
314 devnet_readdir(struct vnode
*dvp
, struct uio
*uiop
, struct cred
*cred
,
315 int *eofp
, caller_context_t
*ct
, int flags
)
317 struct sdev_node
*sdvp
= VTOSDEV(dvp
);
321 if (uiop
->uio_offset
== 0)
322 devnet_filldir(sdvp
);
324 return (devname_readdir_func(dvp
, uiop
, cred
, eofp
, 0));
328 * This callback is invoked from devname_inactive_func() to release
329 * the net entry which was held in devnet_create_rvp().
332 devnet_inactive_callback(struct vnode
*dvp
)
334 struct sdev_node
*sdvp
= VTOSDEV(dvp
);
337 if (dvp
->v_type
== VDIR
)
340 ASSERT(dvp
->v_type
== VCHR
);
341 rw_enter(&sdvp
->sdev_contents
, RW_WRITER
);
342 ddh
= sdvp
->sdev_private
;
343 sdvp
->sdev_private
= NULL
;
344 sdvp
->sdev_flags
|= SDEV_ATTR_INVALID
;
345 rw_exit(&sdvp
->sdev_contents
);
348 * "ddh" (sdev_private) could be NULL if devnet_lookup fails.
351 dls_devnet_close(ddh
);
356 devnet_inactive(struct vnode
*dvp
, struct cred
*cred
, caller_context_t
*ct
)
358 devname_inactive_func(dvp
, cred
, devnet_inactive_callback
);
362 * We override lookup and readdir to build entries based on the
363 * in kernel vanity naming node table.
365 const struct vnodeops devnet_vnodeops
= {
366 .vop_open
= sdev_open
,
367 .vop_close
= sdev_close
,
368 .vop_read
= sdev_read
,
369 .vop_write
= sdev_write
,
370 .vop_ioctl
= sdev_ioctl
,
371 .vop_getattr
= sdev_getattr
,
372 .vop_setattr
= sdev_setattr
,
373 .vop_access
= sdev_access
,
374 .vop_rename
= sdev_rename
,
375 .vop_readlink
= sdev_readlink
,
377 .vop_rwlock
= sdev_rwlock
,
378 .vop_rwunlock
= sdev_rwunlock
,
379 .vop_seek
= sdev_seek
,
380 .vop_frlock
= sdev_frlock
,
381 .vop_pathconf
= sdev_pathconf
,
382 .vop_getsecattr
= sdev_getsecattr
,
385 .vop_readdir
= devnet_readdir
,
386 .vop_lookup
= devnet_lookup
,
387 .vop_inactive
= devnet_inactive
,
388 .vop_create
= fs_nosys
,
389 .vop_remove
= fs_nosys
,
390 .vop_mkdir
= fs_nosys
,
391 .vop_rmdir
= fs_nosys
,
392 .vop_symlink
= fs_nosys
,
393 .vop_setsecattr
= fs_nosys
,