1 | /* $NetBSD: puffs_node.c,v 1.37 2016/08/20 12:37:08 hannken 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/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: puffs_node.c,v 1.37 2016/08/20 12:37:08 hannken Exp $" ); |
34 | |
35 | #include <sys/param.h> |
36 | #include <sys/hash.h> |
37 | #include <sys/kmem.h> |
38 | #include <sys/mount.h> |
39 | #include <sys/namei.h> |
40 | #include <sys/vnode.h> |
41 | |
42 | #include <uvm/uvm.h> |
43 | |
44 | #include <fs/puffs/puffs_msgif.h> |
45 | #include <fs/puffs/puffs_sys.h> |
46 | |
47 | #include <miscfs/genfs/genfs_node.h> |
48 | #include <miscfs/specfs/specdev.h> |
49 | |
50 | struct pool puffs_pnpool; |
51 | struct pool puffs_vapool; |
52 | |
53 | /* |
54 | * Grab a vnode, intialize all the puffs-dependent stuff. |
55 | */ |
56 | static int |
57 | puffs_getvnode1(struct mount *mp, puffs_cookie_t ck, enum vtype type, |
58 | voff_t vsize, dev_t rdev, bool may_exist, struct vnode **vpp) |
59 | { |
60 | struct puffs_mount *pmp; |
61 | struct vnode *vp; |
62 | struct puffs_node *pnode; |
63 | int error; |
64 | |
65 | pmp = MPTOPUFFSMP(mp); |
66 | |
67 | if (type <= VNON || type >= VBAD) { |
68 | puffs_senderr(pmp, PUFFS_ERR_MAKENODE, EINVAL, |
69 | "bad node type" , ck); |
70 | return EPROTO; |
71 | } |
72 | if (vsize == VSIZENOTSET) { |
73 | puffs_senderr(pmp, PUFFS_ERR_MAKENODE, EINVAL, |
74 | "VSIZENOTSET is not a valid size" , ck); |
75 | return EPROTO; |
76 | } |
77 | |
78 | for (;;) { |
79 | error = vcache_get(mp, &ck, sizeof(ck), &vp); |
80 | if (error) |
81 | return error; |
82 | mutex_enter(vp->v_interlock); |
83 | pnode = VPTOPP(vp); |
84 | if (pnode != NULL) |
85 | break; |
86 | mutex_exit(vp->v_interlock); |
87 | vrele(vp); |
88 | } |
89 | mutex_enter(&pnode->pn_mtx); |
90 | mutex_exit(vp->v_interlock); |
91 | |
92 | /* |
93 | * Release and error out if caller wants a fresh vnode. |
94 | */ |
95 | if (vp->v_type != VNON && ! may_exist) { |
96 | mutex_exit(&pnode->pn_mtx); |
97 | vrele(vp); |
98 | return EEXIST; |
99 | } |
100 | |
101 | *vpp = vp; |
102 | |
103 | /* |
104 | * If fully initialized were done. |
105 | */ |
106 | if (vp->v_type != VNON) { |
107 | mutex_exit(&pnode->pn_mtx); |
108 | return 0; |
109 | } |
110 | |
111 | /* |
112 | * Set type and finalize the initialisation. |
113 | */ |
114 | vp->v_type = type; |
115 | if (type == VCHR || type == VBLK) { |
116 | vp->v_op = puffs_specop_p; |
117 | spec_node_init(vp, rdev); |
118 | } else if (type == VFIFO) { |
119 | vp->v_op = puffs_fifoop_p; |
120 | } else if (vp->v_type == VREG) { |
121 | uvm_vnp_setsize(vp, vsize); |
122 | } |
123 | |
124 | pnode->pn_serversize = vsize; |
125 | |
126 | DPRINTF(("new vnode at %p, pnode %p, cookie %p\n" , vp, |
127 | pnode, pnode->pn_cookie)); |
128 | |
129 | mutex_exit(&pnode->pn_mtx); |
130 | |
131 | return 0; |
132 | } |
133 | |
134 | int |
135 | puffs_getvnode(struct mount *mp, puffs_cookie_t ck, enum vtype type, |
136 | voff_t vsize, dev_t rdev, struct vnode **vpp) |
137 | { |
138 | |
139 | return puffs_getvnode1(mp, ck, type, vsize, rdev, true, vpp); |
140 | } |
141 | |
142 | /* new node creating for creative vop ops (create, symlink, mkdir, mknod) */ |
143 | int |
144 | puffs_newnode(struct mount *mp, struct vnode *dvp, struct vnode **vpp, |
145 | puffs_cookie_t ck, struct componentname *cnp, |
146 | enum vtype type, dev_t rdev) |
147 | { |
148 | struct puffs_mount *pmp = MPTOPUFFSMP(mp); |
149 | int error; |
150 | |
151 | /* userspace probably has this as a NULL op */ |
152 | if (ck == NULL) |
153 | return EOPNOTSUPP; |
154 | |
155 | /* |
156 | * Check for previous node with the same designation. |
157 | * Explicitly check the root node cookie, since it might be |
158 | * reclaimed from the kernel when this check is made. |
159 | */ |
160 | if (ck == pmp->pmp_root_cookie) { |
161 | puffs_senderr(pmp, PUFFS_ERR_MAKENODE, EEXIST, |
162 | "cookie exists" , ck); |
163 | return EPROTO; |
164 | } |
165 | |
166 | KASSERT(curlwp != uvm.pagedaemon_lwp); |
167 | |
168 | error = puffs_getvnode1(dvp->v_mount, ck, type, 0, rdev, false, vpp); |
169 | if (error) { |
170 | if (error == EEXIST) { |
171 | puffs_senderr(pmp, PUFFS_ERR_MAKENODE, EEXIST, |
172 | "cookie exists" , ck); |
173 | error = EPROTO; |
174 | } |
175 | return error; |
176 | } |
177 | |
178 | if (PUFFS_USE_NAMECACHE(pmp)) |
179 | cache_enter(dvp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, |
180 | cnp->cn_flags); |
181 | |
182 | puffs_updatenode(VPTOPP(dvp), PUFFS_UPDATECTIME|PUFFS_UPDATEMTIME, 0); |
183 | |
184 | return 0; |
185 | } |
186 | |
187 | void |
188 | puffs_putvnode(struct vnode *vp) |
189 | { |
190 | struct puffs_node *pnode; |
191 | |
192 | pnode = VPTOPP(vp); |
193 | |
194 | KASSERT(vp->v_tag == VT_PUFFS); |
195 | |
196 | genfs_node_destroy(vp); |
197 | |
198 | /* |
199 | * To interlock with puffs_getvnode1(). |
200 | */ |
201 | mutex_enter(vp->v_interlock); |
202 | vp->v_data = NULL; |
203 | mutex_exit(vp->v_interlock); |
204 | puffs_releasenode(pnode); |
205 | } |
206 | |
207 | /* |
208 | * Make sure root vnode exists and reference it. Does NOT lock. |
209 | */ |
210 | static int |
211 | puffs_makeroot(struct puffs_mount *pmp) |
212 | { |
213 | struct vnode *vp; |
214 | int rv; |
215 | |
216 | /* |
217 | * pmp_lock must be held if vref()'ing or vrele()'ing the |
218 | * root vnode. the latter is controlled by puffs_inactive(). |
219 | * |
220 | * pmp_root is set here and cleared in puffs_reclaim(). |
221 | */ |
222 | |
223 | rv = puffs_getvnode(pmp->pmp_mp, pmp->pmp_root_cookie, |
224 | pmp->pmp_root_vtype, pmp->pmp_root_vsize, pmp->pmp_root_rdev, &vp); |
225 | if (rv != 0) |
226 | return rv; |
227 | |
228 | mutex_enter(&pmp->pmp_lock); |
229 | if (pmp->pmp_root == NULL) |
230 | pmp->pmp_root = vp; |
231 | mutex_exit(&pmp->pmp_lock); |
232 | |
233 | return 0; |
234 | } |
235 | |
236 | /* |
237 | * Locate the in-kernel vnode based on the cookie received given |
238 | * from userspace. |
239 | * |
240 | * returns 0 on success. otherwise returns an errno or PUFFS_NOSUCHCOOKIE. |
241 | * |
242 | * returns PUFFS_NOSUCHCOOKIE if no vnode for the cookie is found. |
243 | */ |
244 | int |
245 | puffs_cookie2vnode(struct puffs_mount *pmp, puffs_cookie_t ck, |
246 | struct vnode **vpp) |
247 | { |
248 | int rv; |
249 | |
250 | /* |
251 | * Handle root in a special manner, since we want to make sure |
252 | * pmp_root is properly set. |
253 | */ |
254 | if (ck == pmp->pmp_root_cookie) { |
255 | if ((rv = puffs_makeroot(pmp))) |
256 | return rv; |
257 | *vpp = pmp->pmp_root; |
258 | return 0; |
259 | } |
260 | |
261 | rv = vcache_get(PMPTOMP(pmp), &ck, sizeof(ck), vpp); |
262 | if (rv != 0) |
263 | return rv; |
264 | mutex_enter((*vpp)->v_interlock); |
265 | if ((*vpp)->v_type == VNON) { |
266 | mutex_exit((*vpp)->v_interlock); |
267 | /* XXX vrele() calls VOP_INACTIVE() with VNON node */ |
268 | vrele(*vpp); |
269 | *vpp = NULL; |
270 | return PUFFS_NOSUCHCOOKIE; |
271 | } |
272 | mutex_exit((*vpp)->v_interlock); |
273 | |
274 | return 0; |
275 | } |
276 | |
277 | void |
278 | puffs_updatenode(struct puffs_node *pn, int flags, voff_t size) |
279 | { |
280 | struct timespec ts; |
281 | |
282 | if (flags == 0) |
283 | return; |
284 | |
285 | nanotime(&ts); |
286 | |
287 | if (flags & PUFFS_UPDATEATIME) { |
288 | pn->pn_mc_atime = ts; |
289 | pn->pn_stat |= PNODE_METACACHE_ATIME; |
290 | } |
291 | if (flags & PUFFS_UPDATECTIME) { |
292 | pn->pn_mc_ctime = ts; |
293 | pn->pn_stat |= PNODE_METACACHE_CTIME; |
294 | } |
295 | if (flags & PUFFS_UPDATEMTIME) { |
296 | pn->pn_mc_mtime = ts; |
297 | pn->pn_stat |= PNODE_METACACHE_MTIME; |
298 | } |
299 | if (flags & PUFFS_UPDATESIZE) { |
300 | pn->pn_mc_size = size; |
301 | pn->pn_stat |= PNODE_METACACHE_SIZE; |
302 | } |
303 | } |
304 | |
305 | /* |
306 | * Add reference to node. |
307 | * mutex held on entry and return |
308 | */ |
309 | void |
310 | puffs_referencenode(struct puffs_node *pn) |
311 | { |
312 | |
313 | KASSERT(mutex_owned(&pn->pn_mtx)); |
314 | pn->pn_refcount++; |
315 | } |
316 | |
317 | /* |
318 | * Release pnode structure which dealing with references to the |
319 | * puffs_node instead of the vnode. Can't use vref()/vrele() on |
320 | * the vnode there, since that causes the lovely VOP_INACTIVE(), |
321 | * which in turn causes the lovely deadlock when called by the one |
322 | * who is supposed to handle it. |
323 | */ |
324 | void |
325 | puffs_releasenode(struct puffs_node *pn) |
326 | { |
327 | |
328 | mutex_enter(&pn->pn_mtx); |
329 | if (--pn->pn_refcount == 0) { |
330 | mutex_exit(&pn->pn_mtx); |
331 | mutex_destroy(&pn->pn_mtx); |
332 | mutex_destroy(&pn->pn_sizemtx); |
333 | seldestroy(&pn->pn_sel); |
334 | if (pn->pn_va_cache != NULL) |
335 | pool_put(&puffs_vapool, pn->pn_va_cache); |
336 | pool_put(&puffs_pnpool, pn); |
337 | } else { |
338 | mutex_exit(&pn->pn_mtx); |
339 | } |
340 | } |
341 | |