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