4f6c35978ef26578a8bee1daaa3590306597524f
[dragonfly.git] / sys / vfs / puffs / puffs_node.c
1 /*      $NetBSD: puffs_node.c,v 1.19 2011/06/30 20:09:41 wiz Exp $      */
2
3 /*
4  * Copyright (c) 2005, 2006, 2007  Antti Kantee.  All Rights Reserved.
5  *
6  * Development of this software was supported by the
7  * Google Summer of Code program, the Ulla Tuominen Foundation
8  * and the Finnish Cultural Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #include <sys/param.h>
33 #include <sys/fnv_hash.h>
34 #include <sys/malloc.h>
35 #include <sys/mount.h>
36 #include <sys/namei.h>
37 #include <sys/vnode.h>
38
39 #include <vfs/puffs/puffs_msgif.h>
40 #include <vfs/puffs/puffs_sys.h>
41
42 static __inline struct puffs_node_hashlist
43         *puffs_cookie2hashlist(struct puffs_mount *, puffs_cookie_t);
44 static struct puffs_node *puffs_cookie2pnode(struct puffs_mount *,
45                                              puffs_cookie_t);
46
47 /*
48  * Grab a vnode, intialize all the puffs-dependent stuff.
49  */
50 int
51 puffs_getvnode(struct mount *mp, puffs_cookie_t ck, enum vtype type,
52         voff_t vsize, struct vnode **vpp)
53 {
54         struct puffs_mount *pmp;
55         struct puffs_newcookie *pnc;
56         struct vnode *vp;
57         struct puffs_node *pnode;
58         struct puffs_node_hashlist *plist;
59         int error;
60
61         pmp = MPTOPUFFSMP(mp);
62
63         error = EPROTO;
64         if (type <= VNON || type >= VBAD) {
65                 puffs_senderr(pmp, PUFFS_ERR_MAKENODE, EINVAL,
66                     "bad node type", ck);
67                 goto bad;
68         }
69         if (type == VBLK || type == VCHR) {
70                 puffs_senderr(pmp, PUFFS_ERR_MAKENODE, EINVAL,
71                     "device nodes are not supported", ck);
72                 goto bad;
73         }
74         if (vsize == VNOVAL) {
75                 puffs_senderr(pmp, PUFFS_ERR_MAKENODE, EINVAL,
76                     "VNOVAL is not a valid size", ck);
77                 goto bad;
78         }
79
80         /* XXX Add VT_PUFFS */
81         error = getnewvnode(VT_SYNTH, mp, &vp, 0, 0);
82         if (error) {
83                 goto bad;
84         }
85         vp->v_type = type;
86
87         /*
88          * Creation should not fail after this point.  Or if it does,
89          * care must be taken so that VOP_INACTIVE() isn't called.
90          */
91
92         /* dances based on vnode type. almost ufs_vinit(), but not quite */
93         switch (type) {
94         case VFIFO:
95                 vp->v_ops = &mp->mnt_vn_fifo_ops;
96                 break;
97
98         case VREG:
99                 if (PUFFS_USE_PAGECACHE(pmp))
100                         vinitvmio(vp, vsize, mp->mnt_stat.f_iosize, -1);
101                 break;
102
103         case VDIR:
104         case VLNK:
105         case VSOCK:
106                 break;
107         default:
108                 panic("puffs_getvnode: invalid vtype %d", type);
109         }
110
111         pnode = kmalloc(sizeof(struct puffs_node), M_PUFFS, M_ZERO | M_WAITOK);
112
113         pnode->pn_cookie = ck;
114         pnode->pn_refcount = 1;
115
116         /* insert cookie on list, take off of interlock list */
117         lockinit(&pnode->pn_mtx, "puffs pn_mtx", 0, 0);
118 #ifdef XXXDF
119         selinit(&pnode->pn_sel);
120         knlist_init();
121 #endif
122         plist = puffs_cookie2hashlist(pmp, ck);
123         lockmgr(&pmp->pmp_lock, LK_EXCLUSIVE);
124         LIST_INSERT_HEAD(plist, pnode, pn_hashent);
125         if (ck != pmp->pmp_root_cookie) {
126                 LIST_FOREACH(pnc, &pmp->pmp_newcookie, pnc_entries) {
127                         if (pnc->pnc_cookie == ck) {
128                                 LIST_REMOVE(pnc, pnc_entries);
129                                 kfree(pnc, M_PUFFS);
130                                 break;
131                         }
132                 }
133                 KKASSERT(pnc != NULL);
134         }
135         lockmgr(&pmp->pmp_lock, LK_RELEASE);
136
137         vp->v_data = pnode;
138         vp->v_type = type;
139         pnode->pn_vp = vp;
140         pnode->pn_serversize = vsize;
141
142         *vpp = vp;
143
144         DPRINTF(("new vnode at %p, pnode %p, cookie %p\n", vp,
145             pnode, pnode->pn_cookie));
146
147         return 0;
148
149  bad:
150         /* remove staging cookie from list */
151         if (ck != pmp->pmp_root_cookie) {
152                 lockmgr(&pmp->pmp_lock, LK_EXCLUSIVE);
153                 LIST_FOREACH(pnc, &pmp->pmp_newcookie, pnc_entries) {
154                         if (pnc->pnc_cookie == ck) {
155                                 LIST_REMOVE(pnc, pnc_entries);
156                                 kfree(pnc, M_PUFFS);
157                                 break;
158                         }
159                 }
160                 KKASSERT(pnc != NULL);
161                 lockmgr(&pmp->pmp_lock, LK_RELEASE);
162         }
163
164         return error;
165 }
166
167 /* new node creating for creative vop ops (create, symlink, mkdir, mknod) */
168 int
169 puffs_newnode(struct mount *mp, struct vnode *dvp, struct vnode **vpp,
170         puffs_cookie_t ck, enum vtype type)
171 {
172         struct puffs_mount *pmp = MPTOPUFFSMP(mp);
173         struct puffs_newcookie *pnc;
174         struct vnode *vp;
175         int error;
176
177         /* userspace probably has this as a NULL op */
178         if (ck == NULL) {
179                 error = EOPNOTSUPP;
180                 return error;
181         }
182
183         /*
184          * Check for previous node with the same designation.
185          * Explicitly check the root node cookie, since it might be
186          * reclaimed from the kernel when this check is made.
187          */
188         lockmgr(&pmp->pmp_lock, LK_EXCLUSIVE);
189         if (ck == pmp->pmp_root_cookie
190             || puffs_cookie2pnode(pmp, ck) != NULL) {
191                 lockmgr(&pmp->pmp_lock, LK_RELEASE);
192                 puffs_senderr(pmp, PUFFS_ERR_MAKENODE, EEXIST,
193                     "cookie exists", ck);
194                 return EPROTO;
195         }
196
197         LIST_FOREACH(pnc, &pmp->pmp_newcookie, pnc_entries) {
198                 if (pnc->pnc_cookie == ck) {
199                         lockmgr(&pmp->pmp_lock, LK_RELEASE);
200                         puffs_senderr(pmp, PUFFS_ERR_MAKENODE, EEXIST,
201                             "newcookie exists", ck);
202                         return EPROTO;
203                 }
204         }
205         pnc = kmalloc(sizeof(struct puffs_newcookie), M_PUFFS, M_WAITOK);
206         pnc->pnc_cookie = ck;
207         LIST_INSERT_HEAD(&pmp->pmp_newcookie, pnc, pnc_entries);
208         lockmgr(&pmp->pmp_lock, LK_RELEASE);
209
210         error = puffs_getvnode(dvp->v_mount, ck, type, 0, &vp);
211         if (error)
212                 return error;
213         *vpp = vp;
214
215         return 0;
216 }
217
218 void
219 puffs_putvnode(struct vnode *vp)
220 {
221         struct puffs_mount *pmp;
222         struct puffs_node *pnode;
223
224         pmp = VPTOPUFFSMP(vp);
225         pnode = VPTOPP(vp);
226
227 #ifdef DIAGNOSTIC
228         if (vp->v_tag != VT_SYNTH)
229                 panic("puffs_putvnode: %p not a puffs vnode", vp);
230 #endif
231
232         puffs_releasenode(pnode);
233         vp->v_data = NULL;
234
235         return;
236 }
237
238 static __inline struct puffs_node_hashlist *
239 puffs_cookie2hashlist(struct puffs_mount *pmp, puffs_cookie_t ck)
240 {
241         uint32_t hash;
242
243         hash = fnv_32_buf(&ck, sizeof(void *), FNV1_32_INIT);
244         return &pmp->pmp_pnodehash[hash % pmp->pmp_npnodehash];
245 }
246
247 /*
248  * Translate cookie to puffs_node.  Caller must hold pmp_lock
249  * and it will be held upon return.
250  */
251 static struct puffs_node *
252 puffs_cookie2pnode(struct puffs_mount *pmp, puffs_cookie_t ck)
253 {
254         struct puffs_node_hashlist *plist;
255         struct puffs_node *pnode;
256
257         plist = puffs_cookie2hashlist(pmp, ck);
258         LIST_FOREACH(pnode, plist, pn_hashent) {
259                 if (pnode->pn_cookie == ck)
260                         break;
261         }
262
263         return pnode;
264 }
265
266 /*
267  * Make sure root vnode exists and reference it.  Does NOT lock.
268  */
269 static int
270 puffs_makeroot(struct puffs_mount *pmp)
271 {
272         struct vnode *vp;
273         int rv;
274
275         /*
276          * pmp_lock must be held if vref()'ing or vrele()'ing the
277          * root vnode.  the latter is controlled by puffs_inactive().
278          *
279          * pmp_root is set here and cleared in puffs_reclaim().
280          */
281  retry:
282         vp = pmp->pmp_root;
283         if (vp) {
284                 if (vget(vp, LK_EXCLUSIVE) == 0)
285                         return 0;
286         }
287
288         /*
289          * So, didn't have the magic root vnode available.
290          * No matter, grab another and stuff it with the cookie.
291          */
292         if ((rv = puffs_getvnode(pmp->pmp_mp, pmp->pmp_root_cookie,
293             pmp->pmp_root_vtype, pmp->pmp_root_vsize, &vp)))
294                 return rv;
295
296         /*
297          * Someone magically managed to race us into puffs_getvnode?
298          * Put our previous new vnode back and retry.
299          */
300         lockmgr(&pmp->pmp_lock, LK_EXCLUSIVE);
301         if (pmp->pmp_root) {
302                 struct puffs_node *pnode = VPTOPP(vp);
303
304                 LIST_REMOVE(pnode, pn_hashent);
305                 lockmgr(&pmp->pmp_lock, LK_RELEASE);
306                 puffs_putvnode(vp);
307                 goto retry;
308         }
309
310         /* store cache */
311         vsetflags(vp, VROOT);
312         pmp->pmp_root = vp;
313         vref(vp);
314         lockmgr(&pmp->pmp_lock, LK_RELEASE);
315
316         return 0;
317 }
318
319 /*
320  * Locate the in-kernel vnode based on the cookie received given
321  * from userspace.  Returns a vnode, if found, NULL otherwise.
322  * The parameter "lock" control whether to lock the possible or
323  * not.  Locking always might cause us to lock against ourselves
324  * in situations where we want the vnode but don't care for the
325  * vnode lock, e.g. file server issued putpages.
326  */
327 int
328 puffs_cookie2vnode(struct puffs_mount *pmp, puffs_cookie_t ck,
329         int willcreate, struct vnode **vpp)
330 {
331         struct puffs_node *pnode;
332         struct puffs_newcookie *pnc;
333         struct vnode *vp;
334         int rv;
335
336         /*
337          * Handle root in a special manner, since we want to make sure
338          * pmp_root is properly set.
339          */
340         if (ck == pmp->pmp_root_cookie) {
341                 if ((rv = puffs_makeroot(pmp)))
342                         return rv;
343                 *vpp = pmp->pmp_root;
344                 return 0;
345         }
346
347         lockmgr(&pmp->pmp_lock, LK_EXCLUSIVE);
348         pnode = puffs_cookie2pnode(pmp, ck);
349         if (pnode == NULL) {
350                 if (willcreate) {
351                         pnc = kmalloc(sizeof(struct puffs_newcookie),
352                             M_PUFFS, M_WAITOK);
353                         pnc->pnc_cookie = ck;
354                         LIST_INSERT_HEAD(&pmp->pmp_newcookie, pnc, pnc_entries);
355                 }
356                 lockmgr(&pmp->pmp_lock, LK_RELEASE);
357                 return PUFFS_NOSUCHCOOKIE;
358         }
359         vp = pnode->pn_vp;
360         vhold(vp);
361         lockmgr(&pmp->pmp_lock, LK_RELEASE);
362
363         rv = vget(vp, LK_EXCLUSIVE);
364         vdrop(vp);
365         if (rv)
366                 return rv;
367
368         *vpp = vp;
369         return 0;
370 }
371
372 void
373 puffs_updatenode(struct puffs_node *pn, int flags)
374 {
375         struct timespec ts;
376
377         if (flags == 0)
378                 return;
379
380         nanotime(&ts);
381
382         if (flags & PUFFS_UPDATEATIME) {
383                 pn->pn_mc_atime = ts;
384                 pn->pn_stat |= PNODE_METACACHE_ATIME;
385         }
386         if (flags & PUFFS_UPDATECTIME) {
387                 pn->pn_mc_ctime = ts;
388                 pn->pn_stat |= PNODE_METACACHE_CTIME;
389         }
390         if (flags & PUFFS_UPDATEMTIME) {
391                 pn->pn_mc_mtime = ts;
392                 pn->pn_stat |= PNODE_METACACHE_MTIME;
393         }
394 }
395
396 int
397 puffs_meta_setsize(struct vnode *vp, off_t nsize, int trivial)
398 {
399         struct puffs_node *pn = VPTOPP(vp);
400         struct puffs_mount *pmp = VPTOPUFFSMP(vp);
401         int biosize = vp->v_mount->mnt_stat.f_iosize;
402         off_t osize;
403         int error;
404
405         osize = puffs_meta_getsize(vp);
406         pn->pn_mc_size = nsize;
407         if (pn->pn_serversize != nsize)
408                 pn->pn_stat |= PNODE_METACACHE_SIZE;
409         else
410                 pn->pn_stat &= ~PNODE_METACACHE_SIZE;
411
412         if (!PUFFS_USE_PAGECACHE(pmp))
413                 return 0;
414
415         if (nsize < osize) {
416                 error = nvtruncbuf(vp, nsize, biosize, -1, 0);
417         } else {
418                 error = nvextendbuf(vp, osize, nsize,
419                                     biosize, biosize, -1, -1,
420                                     trivial);
421         }
422
423         return error;
424 }
425
426 /*
427  * Add reference to node.
428  *  mutex held on entry and return
429  */
430 void
431 puffs_referencenode(struct puffs_node *pn)
432 {
433
434         KKASSERT(lockstatus(&pn->pn_mtx, curthread) == LK_EXCLUSIVE);
435         pn->pn_refcount++;
436 }
437
438 /*
439  * Release pnode structure which dealing with references to the
440  * puffs_node instead of the vnode.  Can't use vref()/vrele() on
441  * the vnode there, since that causes the lovely VOP_INACTIVE(),
442  * which in turn causes the lovely deadlock when called by the one
443  * who is supposed to handle it.
444  */
445 void
446 puffs_releasenode(struct puffs_node *pn)
447 {
448
449         lockmgr(&pn->pn_mtx, LK_EXCLUSIVE);
450         if (--pn->pn_refcount == 0) {
451                 lockmgr(&pn->pn_mtx, LK_RELEASE);
452                 lockuninit(&pn->pn_mtx);
453 #ifdef XXXDF
454                 seldestroy(&pn->pn_sel);
455 #endif
456                 kfree(pn, M_PUFFS);
457         } else {
458                 lockmgr(&pn->pn_mtx, LK_RELEASE);
459         }
460 }