Merge commit '008b34be09d7b9c3e7a18d3ce9ef8b5c4f4ff8b8'
[unleashed.git] / kernel / fs / dev / sdev_netops.c
blob501bc830acfe005a794cf991c353c16ebd739e6e
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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>
41 #include <sys/zone.h>
42 #include <sys/dls.h>
43 #include "sdev_vnops.h"
46 * Check if a net sdev_node is still valid - i.e. it represents a current
47 * network link.
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().
54 int
55 devnet_validate(struct sdev_node *dv)
57 datalink_id_t linkid;
58 zoneid_t zoneid;
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);
66 zoneid = getzoneid();
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.
75 static int
76 devnet_create_rvp(const char *nm, struct vattr *vap, dls_dl_handle_t *ddhp)
78 timestruc_t now;
79 dev_t dev;
80 int error;
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));
85 return (error);
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;
94 vap->va_mode |= 0666;
95 vap->va_rdev = dev;
97 gethrestime(&now);
98 vap->va_atime = now;
99 vap->va_mtime = now;
100 vap->va_ctime = now;
101 return (0);
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.
109 /*ARGSUSED3*/
110 static int
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;
118 struct vattr vattr;
119 int nmlen;
120 int error = ENOENT;
122 if (SDEVTOV(ddv)->v_type != VDIR)
123 return (ENOTDIR);
126 * Empty name or ., return node itself.
128 nmlen = strlen(nm);
129 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
130 *vpp = SDEVTOV(ddv);
131 VN_HOLD(*vpp);
132 return (0);
136 * .., return the parent directory
138 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
139 *vpp = SDEVTOV(ddv->sdev_dotdot);
140 VN_HOLD(*vpp);
141 return (0);
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))
152 goto found;
156 * ZOMBIED parent does not allow new node creation, bail out early.
158 if (ddv->sdev_state == SDEV_ZOMBIE)
159 goto failed;
161 error = devnet_create_rvp(nm, &vattr, &ddh);
162 if (error != 0)
163 goto failed;
165 error = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL, cred, SDEV_READY);
166 if (error != 0) {
167 dls_devnet_close(ddh);
168 goto failed;
171 ASSERT(dv != NULL);
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);
189 found:
190 ASSERT(SDEV_HELD(dv));
191 rw_exit(&ddv->sdev_contents);
192 return (sdev_to_vp(dv, vpp));
194 failed:
195 rw_exit(&ddv->sdev_contents);
197 if (dv != NULL)
198 SDEV_RELE(dv);
200 *vpp = NULL;
201 return (error);
204 static int
205 devnet_filldir_datalink(datalink_id_t linkid, void *arg)
207 struct sdev_node *ddv = arg;
208 struct vattr vattr;
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)
216 return (0);
218 if ((dv = sdev_cache_lookup(ddv, (char *)link)) != NULL)
219 goto found;
221 if (devnet_create_rvp(link, &vattr, &ddh) != 0)
222 return (0);
224 ASSERT(ddh != NULL);
225 dls_devnet_close(ddh);
227 if (sdev_mknode(ddv, (char *)link, &dv, &vattr, NULL, NULL, kcred,
228 SDEV_READY) != 0) {
229 return (0);
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
235 * later.
237 rw_enter(&dv->sdev_contents, RW_WRITER);
238 dv->sdev_flags |= SDEV_ATTR_INVALID;
239 rw_exit(&dv->sdev_contents);
241 found:
242 SDEV_SIMPLE_RELE(dv);
243 return (0);
246 static void
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)
263 continue;
265 switch (devnet_validate(dv)) {
266 case SDEV_VTOR_VALID:
267 case SDEV_VTOR_SKIP:
268 continue;
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));
273 break;
276 if (SDEVTOV(dv)->v_count > 0)
277 continue;
278 SDEV_HOLD(dv);
279 /* remove the cache node */
280 (void) sdev_cache_update(ddv, &dv, dv->sdev_name,
281 SDEV_CACHE_DELETE);
282 SDEV_RELE(dv);
285 if (((ddv->sdev_flags & SDEV_BUILD) == 0) && !dls_devnet_rebuild())
286 goto done;
288 if (SDEV_IS_GLOBAL(ddv)) {
289 linkid = DATALINK_INVALID_LINKID;
290 do {
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);
296 } else {
297 (void) zone_datalink_walk(getzoneid(),
298 devnet_filldir_datalink, ddv);
301 ddv->sdev_flags &= ~SDEV_BUILD;
303 done:
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.
312 /*ARGSUSED4*/
313 static int
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);
319 ASSERT(sdvp);
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().
331 static void
332 devnet_inactive_callback(struct vnode *dvp)
334 struct sdev_node *sdvp = VTOSDEV(dvp);
335 dls_dl_handle_t ddh;
337 if (dvp->v_type == VDIR)
338 return;
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.
350 if (ddh != NULL)
351 dls_devnet_close(ddh);
354 /*ARGSUSED*/
355 static void
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,
376 .vop_fid = sdev_fid,
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,
384 /* overrides */
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,