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