1 /*-
2 * Copyright (c) 2017-2020 Juniper Networks, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 */
26
27 /*
28 * This file consists of all the VFS interactions of VFS ops which include
29 * mount, unmount, initilaize etc. for p9fs.
30 */
31
32 #include <sys/cdefs.h>
33 #include <sys/systm.h>
34 #include <sys/fnv_hash.h>
35 #include <sys/mount.h>
36 #include <sys/sysctl.h>
37 #include <sys/vnode.h>
38 #include <sys/buf.h>
39 #include <vm/uma.h>
40
41 #include <fs/p9fs/p9fs_proto.h>
42 #include <fs/p9fs/p9_client.h>
43 #include <fs/p9fs/p9_debug.h>
44 #include <fs/p9fs/p9fs.h>
45
46 SYSCTL_NODE(_vfs, OID_AUTO, p9fs, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
47 "Plan 9 filesystem");
48
49 /* This count is static now. Can be made tunable later */
50 #define P9FS_FLUSH_RETRIES 10
51
52 static MALLOC_DEFINE(M_P9MNT, "p9fs_mount", "Mount structures for p9fs");
53 static uma_zone_t p9fs_node_zone;
54 uma_zone_t p9fs_io_buffer_zone;
55 uma_zone_t p9fs_getattr_zone;
56 uma_zone_t p9fs_setattr_zone;
57 uma_zone_t p9fs_pbuf_zone;
58 extern struct vop_vector p9fs_vnops;
59
60 /* option parsing */
61 static const char *p9fs_opts[] = {
62 "from", "trans", "access", NULL
63 };
64
65 /* Dispose p9fs node, freeing it to the UMA zone */
66 void
p9fs_dispose_node(struct p9fs_node ** npp)67 p9fs_dispose_node(struct p9fs_node **npp)
68 {
69 struct p9fs_node *node;
70 struct vnode *vp;
71
72 node = *npp;
73
74 if (node == NULL)
75 return;
76
77 if (node->parent && node->parent != node) {
78 vrele(P9FS_NTOV(node->parent));
79 }
80
81 P9_DEBUG(VOPS, "%s: node: %p\n", __func__, *npp);
82
83 vp = P9FS_NTOV(node);
84 vp->v_data = NULL;
85
86 /* Free our associated memory */
87 if (!(vp->v_vflag & VV_ROOT)) {
88 free(node->inode.i_name, M_TEMP);
89 uma_zfree(p9fs_node_zone, node);
90 }
91
92 *npp = NULL;
93 }
94
95 /* Initialize memory allocation */
96 static int
p9fs_init(struct vfsconf * vfsp)97 p9fs_init(struct vfsconf *vfsp)
98 {
99
100 p9fs_node_zone = uma_zcreate("p9fs node zone",
101 sizeof(struct p9fs_node), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
102
103 /* Create the getattr_dotl zone */
104 p9fs_getattr_zone = uma_zcreate("p9fs getattr zone",
105 sizeof(struct p9_stat_dotl), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
106
107 /* Create the setattr_dotl zone */
108 p9fs_setattr_zone = uma_zcreate("p9fs setattr zone",
109 sizeof(struct p9_iattr_dotl), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
110
111 /* Create the putpages zone */
112 p9fs_pbuf_zone = pbuf_zsecond_create("p9fs pbuf zone", nswbuf / 2);
113
114 /*
115 * Create the io_buffer zone pool to keep things simpler in case of
116 * multiple threads. Each thread works with its own so there is no
117 * contention.
118 */
119 p9fs_io_buffer_zone = uma_zcreate("p9fs io_buffer zone",
120 P9FS_MTU, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
121
122 return (0);
123 }
124
125 /* Destroy all the allocated memory */
126 static int
p9fs_uninit(struct vfsconf * vfsp)127 p9fs_uninit(struct vfsconf *vfsp)
128 {
129
130 uma_zdestroy(p9fs_node_zone);
131 uma_zdestroy(p9fs_io_buffer_zone);
132 uma_zdestroy(p9fs_getattr_zone);
133 uma_zdestroy(p9fs_setattr_zone);
134 uma_zdestroy(p9fs_pbuf_zone);
135
136 return (0);
137 }
138
139 /* Function to umount p9fs */
140 static int
p9fs_unmount(struct mount * mp,int mntflags)141 p9fs_unmount(struct mount *mp, int mntflags)
142 {
143 struct p9fs_mount *vmp;
144 struct p9fs_session *vses;
145 int error, flags, i;
146
147 error = 0;
148 flags = 0;
149 vmp = VFSTOP9(mp);
150 if (vmp == NULL)
151 return (0);
152
153 vses = &vmp->p9fs_session;
154 if (mntflags & MNT_FORCE)
155 flags |= FORCECLOSE;
156
157 p9fs_prepare_to_close(mp);
158 for (i = 0; i < P9FS_FLUSH_RETRIES; i++) {
159
160 /* Flush everything on this mount point.*/
161 error = vflush(mp, 1, flags, curthread);
162
163 if (error == 0 || (mntflags & MNT_FORCE) == 0)
164 break;
165 /* Sleep until interrupted or 1 tick expires. */
166 error = tsleep(&error, PSOCK, "p9unmnt", 1);
167 if (error == EINTR)
168 break;
169 error = EBUSY;
170 }
171
172 if (error != 0)
173 goto out;
174 p9fs_close_session(mp);
175 /* Cleanup the mount structure. */
176 free(vmp, M_P9MNT);
177 mp->mnt_data = NULL;
178 return (error);
179 out:
180 /* Restore the flag in case of error */
181 vses->clnt->trans_status = P9FS_CONNECT;
182 return (error);
183 }
184
185 /*
186 * Compare qid stored in p9fs node
187 * Return 1 if does not match otherwise return 0
188 */
189 int
p9fs_node_cmp(struct vnode * vp,void * arg)190 p9fs_node_cmp(struct vnode *vp, void *arg)
191 {
192 struct p9fs_node *np;
193 struct p9_qid *qid;
194
195 np = vp->v_data;
196 qid = (struct p9_qid *)arg;
197
198 if (np == NULL)
199 return (1);
200
201 if (np->vqid.qid_path == qid->path) {
202 if (vp->v_vflag & VV_ROOT)
203 return (0);
204 else if (np->vqid.qid_mode == qid->type &&
205 np->vqid.qid_version == qid->version)
206 return (0);
207 }
208
209 return (1);
210 }
211
212 /*
213 * Cleanup p9fs node
214 * - Destroy the FID LIST locks
215 * - Dispose all node knowledge
216 */
217 void
p9fs_destroy_node(struct p9fs_node ** npp)218 p9fs_destroy_node(struct p9fs_node **npp)
219 {
220 struct p9fs_node *np;
221
222 np = *npp;
223
224 if (np == NULL)
225 return;
226
227 /* Destroy the FID LIST locks */
228 P9FS_VFID_LOCK_DESTROY(np);
229 P9FS_VOFID_LOCK_DESTROY(np);
230
231 /* Dispose all node knowledge.*/
232 p9fs_dispose_node(&np);
233 }
234
235 /*
236 * Common code used across p9fs to return vnode for the file represented
237 * by the fid.
238 * Lookup for the vnode in hash_list. This lookup is based on the qid path
239 * which is unique to a file. p9fs_node_cmp is called in this lookup process.
240 * I. If the vnode we are looking for is found in the hash list
241 * 1. Check if the vnode is a valid vnode by reloading its stats
242 * a. if the reloading of the vnode stats returns error then remove the
243 * vnode from hash list and return
244 * b. If reloading of vnode stats returns without any error then, clunk the
245 * new fid which was created for the vnode as we know that the vnode
246 * already has a fid associated with it and return the vnode.
247 * This is to avoid fid leaks
248 * II. If vnode is not found in the hash list then, create new vnode, p9fs
249 * node and return the vnode
250 */
251 int
p9fs_vget_common(struct mount * mp,struct p9fs_node * np,int flags,struct p9fs_node * parent,struct p9_fid * fid,struct vnode ** vpp,char * name)252 p9fs_vget_common(struct mount *mp, struct p9fs_node *np, int flags,
253 struct p9fs_node *parent, struct p9_fid *fid, struct vnode **vpp,
254 char *name)
255 {
256 struct p9fs_mount *vmp;
257 struct p9fs_session *vses;
258 struct vnode *vp;
259 struct p9fs_node *node;
260 struct thread *td;
261 uint32_t hash;
262 int error, error_reload = 0;
263 struct p9fs_inode *inode;
264
265 td = curthread;
266 vmp = VFSTOP9(mp);
267 vses = &vmp->p9fs_session;
268
269 /* Look for vp in the hash_list */
270 hash = fnv_32_buf(&fid->qid.path, sizeof(uint64_t), FNV1_32_INIT);
271 error = vfs_hash_get(mp, hash, flags, td, &vp, p9fs_node_cmp,
272 &fid->qid);
273 if (error != 0)
274 return (error);
275 else if (vp != NULL) {
276 if (vp->v_vflag & VV_ROOT) {
277 if (np == NULL)
278 p9_client_clunk(fid);
279 *vpp = vp;
280 return (0);
281 }
282 error = p9fs_reload_stats_dotl(vp, curthread->td_ucred);
283 if (error != 0) {
284 node = vp->v_data;
285 /* Remove stale vnode from hash list */
286 vfs_hash_remove(vp);
287 node->flags |= P9FS_NODE_DELETED;
288
289 vput(vp);
290 *vpp = NULLVP;
291 vp = NULL;
292 } else {
293 *vpp = vp;
294 /* Clunk the new fid if not root */
295 p9_client_clunk(fid);
296 return (0);
297 }
298 }
299
300 /*
301 * We must promote to an exclusive lock for vnode creation. This
302 * can happen if lookup is passed LOCKSHARED.
303 */
304 if ((flags & LK_TYPE_MASK) == LK_SHARED) {
305 flags &= ~LK_TYPE_MASK;
306 flags |= LK_EXCLUSIVE;
307 }
308
309 /* Allocate a new vnode. */
310 if ((error = getnewvnode("p9fs", mp, &p9fs_vnops, &vp)) != 0) {
311 *vpp = NULLVP;
312 P9_DEBUG(ERROR, "%s: getnewvnode failed: %d\n", __func__, error);
313 return (error);
314 }
315
316 /* If we dont have it, create one. */
317 if (np == NULL) {
318 np = uma_zalloc(p9fs_node_zone, M_WAITOK | M_ZERO);
319 /* Initialize the VFID list */
320 P9FS_VFID_LOCK_INIT(np);
321 STAILQ_INIT(&np->vfid_list);
322 p9fs_fid_add(np, fid, VFID);
323
324 /* Initialize the VOFID list */
325 P9FS_VOFID_LOCK_INIT(np);
326 STAILQ_INIT(&np->vofid_list);
327
328 vref(P9FS_NTOV(parent));
329 np->parent = parent;
330 np->p9fs_ses = vses; /* Map the current session */
331 inode = &np->inode;
332 /*Fill the name of the file in inode */
333 inode->i_name = malloc(strlen(name)+1, M_TEMP, M_NOWAIT | M_ZERO);
334 strlcpy(inode->i_name, name, strlen(name)+1);
335 } else {
336 vp->v_type = VDIR; /* root vp is a directory */
337 vp->v_vflag |= VV_ROOT;
338 vref(vp); /* Increment a reference on root vnode during mount */
339 }
340
341 vp->v_data = np;
342 np->v_node = vp;
343 inode = &np->inode;
344 inode->i_qid_path = fid->qid.path;
345 P9FS_SET_LINKS(inode);
346
347 lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL);
348 if (vp->v_type != VFIFO)
349 VN_LOCK_ASHARE(vp);
350 error = insmntque(vp, mp);
351 if (error != 0) {
352 /*
353 * vput(vp) is already called from insmntque_stddtr().
354 * Just goto 'out' to dispose the node.
355 */
356 goto out;
357 }
358
359 /* Init the vnode with the disk info*/
360 error = p9fs_reload_stats_dotl(vp, curthread->td_ucred);
361 if (error != 0) {
362 error_reload = 1;
363 goto out;
364 }
365
366 error = vfs_hash_insert(vp, hash, flags, td, vpp,
367 p9fs_node_cmp, &fid->qid);
368 if (error != 0) {
369 goto out;
370 }
371
372 if (*vpp == NULL) {
373 P9FS_LOCK(vses);
374 STAILQ_INSERT_TAIL(&vses->virt_node_list, np, p9fs_node_next);
375 np->flags |= P9FS_NODE_IN_SESSION;
376 P9FS_UNLOCK(vses);
377
378 *vpp = vp;
379 } else {
380 /*
381 * Returning matching vp found in hashlist.
382 * So cleanup the np allocated above in this context.
383 */
384 if (!IS_ROOT(np)) {
385 p9fs_destroy_node(&np);
386 }
387 }
388
389 return (0);
390 out:
391 /* Something went wrong, dispose the node */
392 if (!IS_ROOT(np)) {
393 p9fs_destroy_node(&np);
394 }
395
396 if (error_reload) {
397 vput(vp);
398 }
399
400 *vpp = NULLVP;
401 return (error);
402 }
403
404 /* Main mount function for 9pfs */
405 static int
p9_mount(struct mount * mp)406 p9_mount(struct mount *mp)
407 {
408 struct p9_fid *fid;
409 struct p9fs_mount *vmp;
410 struct p9fs_session *vses;
411 struct p9fs_node *p9fs_root;
412 int error;
413 char *from;
414 int len;
415
416 /* Verify the validity of mount options */
417 if (vfs_filteropt(mp->mnt_optnew, p9fs_opts))
418 return (EINVAL);
419
420 /* Extract NULL terminated mount tag from mount options */
421 error = vfs_getopt(mp->mnt_optnew, "from", (void **)&from, &len);
422 if (error != 0 || from[len - 1] != '\0')
423 return (EINVAL);
424
425 /* Allocate and initialize the private mount structure. */
426 vmp = malloc(sizeof (struct p9fs_mount), M_P9MNT, M_WAITOK | M_ZERO);
427 mp->mnt_data = vmp;
428 vmp->p9fs_mountp = mp;
429 vmp->mount_tag = from;
430 vmp->mount_tag_len = len;
431 vses = &vmp->p9fs_session;
432 vses->p9fs_mount = mp;
433 p9fs_root = &vses->rnp;
434 /* Hardware iosize from the Qemu */
435 mp->mnt_iosize_max = PAGE_SIZE;
436 /*
437 * Init the session for the p9fs root. This creates a new root fid and
438 * attaches the client and server.
439 */
440 fid = p9fs_init_session(mp, &error);
441 if (fid == NULL) {
442 goto out;
443 }
444
445 P9FS_VFID_LOCK_INIT(p9fs_root);
446 STAILQ_INIT(&p9fs_root->vfid_list);
447 p9fs_fid_add(p9fs_root, fid, VFID);
448 P9FS_VOFID_LOCK_INIT(p9fs_root);
449 STAILQ_INIT(&p9fs_root->vofid_list);
450 p9fs_root->parent = p9fs_root;
451 p9fs_root->flags |= P9FS_ROOT;
452 p9fs_root->p9fs_ses = vses;
453 vfs_getnewfsid(mp);
454 strlcpy(mp->mnt_stat.f_mntfromname, from,
455 sizeof(mp->mnt_stat.f_mntfromname));
456 MNT_ILOCK(mp);
457 mp->mnt_flag |= MNT_LOCAL;
458 mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED;
459 MNT_IUNLOCK(mp);
460 P9_DEBUG(VOPS, "%s: Mount successful\n", __func__);
461 /* Mount structures created. */
462
463 return (0);
464 out:
465 P9_DEBUG(ERROR, "%s: Mount Failed \n", __func__);
466 if (vmp != NULL) {
467 free(vmp, M_P9MNT);
468 mp->mnt_data = NULL;
469 }
470 return (error);
471 }
472
473 /* Mount entry point */
474 static int
p9fs_mount(struct mount * mp)475 p9fs_mount(struct mount *mp)
476 {
477 int error;
478
479 /*
480 * Minimal support for MNT_UPDATE - allow changing from
481 * readonly.
482 */
483 if (mp->mnt_flag & MNT_UPDATE) {
484 if ((mp->mnt_flag & MNT_RDONLY) && !vfs_flagopt(mp->mnt_optnew, "ro", NULL, 0)) {
485 mp->mnt_flag &= ~MNT_RDONLY;
486 }
487 return (0);
488 }
489
490 error = p9_mount(mp);
491 if (error != 0)
492 (void) p9fs_unmount(mp, MNT_FORCE);
493
494 return (error);
495 }
496
497 /*
498 * Retrieve the root vnode of this mount. After filesystem is mounted, the root
499 * vnode is created for the first time. Subsequent calls to p9fs root will
500 * return the same vnode created during mount.
501 */
502 static int
p9fs_root(struct mount * mp,int lkflags,struct vnode ** vpp)503 p9fs_root(struct mount *mp, int lkflags, struct vnode **vpp)
504 {
505 struct p9fs_mount *vmp;
506 struct p9fs_node *np;
507 struct p9_client *clnt;
508 struct p9_fid *vfid;
509 int error;
510
511 vmp = VFSTOP9(mp);
512 np = &vmp->p9fs_session.rnp;
513 clnt = vmp->p9fs_session.clnt;
514 error = 0;
515
516 P9_DEBUG(VOPS, "%s: node=%p name=%s\n",__func__, np, np->inode.i_name);
517
518 vfid = p9fs_get_fid(clnt, np, curthread->td_ucred, VFID, -1, &error);
519
520 if (error != 0) {
521 /* for root use the nobody user's fid as vfid.
522 * This is used while unmounting as root when non-root
523 * user has mounted p9fs
524 */
525 if (vfid == NULL && clnt->trans_status == P9FS_BEGIN_DISCONNECT)
526 vfid = vmp->p9fs_session.mnt_fid;
527 else {
528 *vpp = NULLVP;
529 return (error);
530 }
531 }
532
533 error = p9fs_vget_common(mp, np, lkflags, np, vfid, vpp, NULL);
534 if (error != 0) {
535 *vpp = NULLVP;
536 return (error);
537 }
538 np->v_node = *vpp;
539 return (error);
540 }
541
542 /* Retrieve the file system statistics */
543 static int
p9fs_statfs(struct mount * mp __unused,struct statfs * buf)544 p9fs_statfs(struct mount *mp __unused, struct statfs *buf)
545 {
546 struct p9fs_mount *vmp;
547 struct p9fs_node *np;
548 struct p9_client *clnt;
549 struct p9_fid *vfid;
550 struct p9_statfs statfs;
551 int res, error;
552
553 vmp = VFSTOP9(mp);
554 np = &vmp->p9fs_session.rnp;
555 clnt = vmp->p9fs_session.clnt;
556 error = 0;
557
558 vfid = p9fs_get_fid(clnt, np, curthread->td_ucred, VFID, -1, &error);
559 if (error != 0) {
560 return (error);
561 }
562
563 res = p9_client_statfs(vfid, &statfs);
564
565 if (res == 0) {
566 buf->f_type = statfs.type;
567 /*
568 * We have a limit of 4k irrespective of what the
569 * Qemu server can do.
570 */
571 if (statfs.bsize > PAGE_SIZE)
572 buf->f_bsize = PAGE_SIZE;
573 else
574 buf->f_bsize = statfs.bsize;
575
576 buf->f_iosize = buf->f_bsize;
577 buf->f_blocks = statfs.blocks;
578 buf->f_bfree = statfs.bfree;
579 buf->f_bavail = statfs.bavail;
580 buf->f_files = statfs.files;
581 buf->f_ffree = statfs.ffree;
582 }
583 else {
584 /* Atleast set these if stat fail */
585 buf->f_bsize = PAGE_SIZE;
586 buf->f_iosize = buf->f_bsize; /* XXX */
587 }
588
589 return (0);
590 }
591
592 static int
p9fs_fhtovp(struct mount * mp,struct fid * fhp,int flags,struct vnode ** vpp)593 p9fs_fhtovp(struct mount *mp, struct fid *fhp, int flags, struct vnode **vpp)
594 {
595
596 return (EINVAL);
597 }
598
599 struct vfsops p9fs_vfsops = {
600 .vfs_init = p9fs_init,
601 .vfs_uninit = p9fs_uninit,
602 .vfs_mount = p9fs_mount,
603 .vfs_unmount = p9fs_unmount,
604 .vfs_root = p9fs_root,
605 .vfs_statfs = p9fs_statfs,
606 .vfs_fhtovp = p9fs_fhtovp,
607 };
608
609 VFS_SET(p9fs_vfsops, p9fs, VFCF_JAIL);
610 MODULE_VERSION(p9fs, 1);
611