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