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