forget about device fd, we don't need it
[arla.git] / nnpfs / bsd / nnpfs_blocks.c
blob8eefe9554dabf632e1da7b3edd906960b8fbfdb8
1 /*
2 * Copyright (c) 2005-2007, Stockholms Universitet
3 * (Stockholm University, Stockholm Sweden)
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the university nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
34 /* $Id$ */
36 #include <nnpfs/nnpfs_locl.h>
37 #include <nnpfs/nnpfs_fs.h>
38 #include <nnpfs/nnpfs_dev.h>
39 #include <nnpfs/nnpfs_deb.h>
40 #include <nnpfs/nnpfs_vnodeops.h>
42 #include <nnpfs/nnpfs_common.h>
43 #include <nnpfs/nnpfs_node.h>
46 * return true if block is in cache
49 int
50 nnpfs_block_have_p(struct nnpfs_node *node, uint64_t offset)
52 struct nnpfs_cache_handle *handle = &node->data;
53 uint32_t index = nnpfs_block_index(offset);
54 uint32_t maskno = nnpfs_block_masknumber(index);
56 nnpfs_assert(nnpfs_offset(offset) == offset);
58 if (handle->nmasks == 0)
59 return 0;
61 if (maskno >= handle->nmasks)
62 return 0;
64 if (handle->nmasks == 1)
65 return (handle->masks.first & nnpfs_block_mask(index));
67 return (handle->masks.list[maskno] & nnpfs_block_mask(index));
71 * mark block at offset as present in cache
73 * XXX assert on the bit being changed?
76 static int
77 nnpfs_block_set_have(struct nnpfs_node *node, uint64_t offset, int val)
79 struct nnpfs_cache_handle *handle = &node->data;
80 uint32_t index = nnpfs_block_index(offset);
81 uint32_t maskno = nnpfs_block_masknumber(index);
82 uint32_t mask = nnpfs_block_mask(index);
83 uint32_t *slot;
85 nnpfs_assert(nnpfs_offset(offset) == offset);
87 if (maskno == 0 && handle->nmasks <= 1) {
88 handle->nmasks = 1;
89 slot = &handle->masks.first;
90 } else {
91 if (maskno >= handle->nmasks) {
92 int n = maskno + NNPFS_NMASKS - (maskno % NNPFS_NMASKS);
93 int size = n * sizeof(uint32_t);
94 uint32_t *new;
96 nnpfs_assert(val);
98 new = nnpfs_alloc(size, M_NNPFS_BLOCKS);
99 nnpfs_debug_assert(new);
100 if (!new)
101 return ENOMEM;
103 if (handle->nmasks == 1) {
104 new[0] = handle->masks.first;
105 } else if (handle->nmasks > 1) {
106 memcpy(new, handle->masks.list,
107 handle->nmasks * sizeof(uint32_t));
108 nnpfs_free(handle->masks.list, handle->nmasks * sizeof(uint32_t),
109 M_NNPFS_BLOCKS);
112 memset(&new[handle->nmasks], 0,
113 (n - handle->nmasks) * sizeof(uint32_t));
114 handle->nmasks = n;
115 handle->masks.list = new;
117 slot = &handle->masks.list[maskno];
120 if (val)
121 *slot |= mask;
122 else
123 *slot &= ~mask;
125 return 0;
129 * mark block at offset as present in cache
133 nnpfs_block_setvalid(struct nnpfs_node *node, uint64_t offset)
135 return nnpfs_block_set_have(node, offset, TRUE);
139 * mark block at offset as not present in cache
142 void
143 nnpfs_block_setinvalid(struct nnpfs_node *node, uint64_t offset)
145 (void)nnpfs_block_set_have(node, offset, FALSE);
148 static void
149 nnpfs_block_foreach_int(struct nnpfs_node *node,
150 nnpfs_block_callback_t fun,
151 void *data,
152 uint64_t base_offset,
153 int32_t mask)
155 uint32_t tmp_mask = 1;
156 int i;
158 if (!mask)
159 return;
161 for (i = 0; i < 32; i++) {
162 if (mask & tmp_mask) {
163 fun(node, base_offset + i * nnpfs_blocksize, data);
164 mask -= tmp_mask;
165 if (!mask)
166 return;
169 tmp_mask = tmp_mask << 1;
174 * call callback for every block present in cache
177 void
178 nnpfs_block_foreach(struct nnpfs_node *node,
179 nnpfs_block_callback_t fun,
180 void *data)
182 struct nnpfs_cache_handle *handle = &node->data;
183 int i;
185 if (handle->nmasks == 0)
186 return;
188 if (handle->nmasks == 1) {
189 nnpfs_block_foreach_int(node, fun, data, 0, handle->masks.first);
190 return;
193 for (i = 0; i < handle->nmasks; i++)
194 nnpfs_block_foreach_int(node, fun, data, i * 32 * nnpfs_blocksize,
195 handle->masks.list[i]);
199 * Foreach callback for nnpfs_block_truncate()
202 static void
203 truncate_callback(struct nnpfs_node *node, uint64_t offset, void *data)
205 uint64_t *size = (uint64_t *)data;
206 if (*size <= offset && offset > 0)
207 (void)nnpfs_block_set_have(node, offset, FALSE);
211 * Forget all blocks beyond `sizeĀ“ for `node'
214 void
215 nnpfs_block_truncate(struct nnpfs_node *node, uint64_t size)
217 nnpfs_block_foreach(node, truncate_callback, &size);
221 * free all handle internal resources
224 void
225 nnpfs_block_free_all(struct nnpfs_node *node)
227 struct nnpfs_cache_handle *handle = &node->data;
228 if (handle->nmasks > 1) {
229 nnpfs_free(handle->masks.list, handle->nmasks * sizeof(uint32_t),
230 M_NNPFS_BLOCKS);
231 handle->masks.list = NULL;
232 } else {
233 handle->masks.first = 0;
236 handle->nmasks = 0;
240 * return true if we have no data
244 nnpfs_block_empty(struct nnpfs_node *node)
246 struct nnpfs_cache_handle *handle = &node->data;
247 int i;
249 if (handle->nmasks == 0)
250 return 1;
252 if (handle->nmasks == 1) {
253 if (handle->masks.first == 0)
254 return 1;
255 return 0;
258 for (i = 0; i < handle->nmasks; i++)
259 if (handle->masks.list[i] != 0)
260 return 0;
262 return 1;
265 static int
266 nnpfs_block_extend_int(struct nnpfs_node *node, struct vnode *vp, d_thread_t *p)
268 struct nnpfs_vfs_vattr va;
269 int ret;
271 VATTR_INIT(&va);
272 nnpfs_set_va_size(&va, nnpfs_blocksize);
273 nnpfs_vfs_writelock(vp, p);
275 /* printf("nnpfs extend_int(%p)\n", vp); */
276 ret = nnpfs_vnode_setattr(vp, &va, NNPFS_FROM_XNODE(node)->ctx);
277 nnpfs_vfs_unlock(vp, p);
278 nnpfs_debug_assert(!ret);
279 return ret;
283 * Extend an existing block to full block size.
286 static int
287 nnpfs_block_extend(struct nnpfs_node *node, uint64_t offset)
289 d_thread_t *p = nnpfs_curproc();
290 struct vnode *vp;
291 int ret;
293 nnpfs_assert(nnpfs_block_have_p(node, offset));
295 ret = nnpfs_block_open(node, offset, FREAD|FWRITE, &vp);
296 if (!ret) {
297 nnpfs_assert(vp);
299 #ifdef __FreeBSD__
301 struct mount *mp;
303 (void)vn_start_write(vp, &mp, V_WAIT);
304 VOP_LEASE(vp, p,
305 nnpfs_vfs_context_ucred(NNPFS_FROM_XNODE(node)->ctx),
306 LEASE_WRITE);
308 ret = nnpfs_block_extend_int(node, vp, p);
310 VOP_UNLOCK(vp, 0, p);
311 vn_finished_write(mp);
313 #else
314 ret = nnpfs_block_extend_int(node, vp, p);
315 #endif
317 nnpfs_block_close(node, vp, 1);
320 if (ret)
321 printf("nnpfs_block_extend: failed at offset 0x%llx: %d\n",
322 (unsigned long long)offset, ret);
324 return ret;
327 #ifndef __APPLE__
329 * namei() compatible alloc/free
332 static long nnpfs_namei_allocs, nnpfs_namei_frees;
333 static void
334 nnpfs_namei_alloc(struct componentname *cnp)
336 void *p = NULL;
338 if (cnp->cn_flags & HASBUF) {
339 printf("nnpfs_namei_alloc: cnp flags 0x%lx\n", cnp->cn_flags);
340 return;
343 #ifdef __FreeBSD__
344 p = uma_zalloc(namei_zone, M_WAITOK);
345 #endif
346 #ifdef __OpenBSD__
347 p = pool_get(&namei_pool, PR_WAITOK);
348 #endif
349 #ifdef __NetBSD__
350 p = PNBUF_GET();
351 #endif
352 if (p) {
353 cnp->cn_pnbuf = p;
354 cnp->cn_flags |= HASBUF;
355 nnpfs_namei_allocs++;
359 static void
360 nnpfs_namei_free(struct componentname *cnp)
362 if ((cnp->cn_flags & HASBUF) == 0)
363 return;
365 #ifdef __FreeBSD__
366 uma_zfree(namei_zone, cnp->cn_pnbuf);
367 #endif
368 #ifdef __NetBSD__
369 PNBUF_PUT(cnp->cn_pnbuf);
370 #endif
371 #ifdef __OpenBSD__
372 pool_put(&namei_pool, cnp->cn_pnbuf);
373 #endif
375 cnp->cn_flags &= ~HASBUF;
376 nnpfs_namei_frees++;
379 #endif /* !__APPLE__ */
383 * a handy implementation of open()
385 * nnpfs_block_close() on success.
388 static int
389 open_file(struct vnode *cachedir, char *name, int fmode,
390 nnpfs_vfs_context ctx, struct vnode **vpp)
392 int error;
394 #ifdef __APPLE__ /* XXX */
395 error = vnode_open(name, fmode, S_IRUSR|S_IWUSR, 0, vpp, ctx);
396 #else
398 d_thread_t *p = nnpfs_curproc();
399 nnpfs_kernel_cred cred = nnpfs_vfs_context_ucred(ctx);
400 /* nnpfs_kernel_cred cred = nnpfs_proc_to_cred(p); */
401 struct nameidata nd;
403 memset(&nd, 0, sizeof(nd));
405 if (fmode & O_CREAT) {
406 NDINIT(&nd, CREATE,
407 FOLLOW | LOCKLEAF | LOCKPARENT | SAVENAME | NNPFS_MPSAFE,
408 UIO_SYSSPACE, name, p);
409 } else {
410 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NNPFS_MPSAFE, UIO_SYSSPACE, name, p);
413 nd.ni_cnd.cn_cred = cred;
414 nd.ni_startdir = cachedir;
416 nnpfs_namei_alloc(&nd.ni_cnd);
417 nd.ni_cnd.cn_nameptr = nd.ni_cnd.cn_pnbuf;
419 error = copystr(name, nd.ni_cnd.cn_pnbuf, MAXPATHLEN, &nd.ni_pathlen);
420 if (error == 0 && nd.ni_pathlen == 1)
421 error = ENOENT;
423 if (error) {
424 nnpfs_namei_free(&nd.ni_cnd);
425 printf("nnpfs open_file(%p, %s) copystr -> %d\n",
426 cachedir, name, error);
427 return error;
430 #ifdef __FreeBSD__
431 if ((fmode & O_ACCMODE) != FREAD)
432 bwillwrite(); /* do this before getting devlock? */
433 #endif
434 /* XXX vn_start_write() etc? */
436 nnpfs_vref(cachedir);
438 error = lookup(&nd);
439 if (error) {
440 nnpfs_namei_free(&nd.ni_cnd);
441 printf("lookup(%s) -> %d\n", name, error);
442 return error;
445 if (fmode & O_CREAT && nd.ni_vp) {
446 fmode &= ~O_CREAT;
448 nnpfs_vfs_unlock(cachedir, p);
451 if (fmode & O_CREAT) {
452 struct vattr vat;
453 struct mount *mp;
455 if ((nd.ni_cnd.cn_flags & SAVENAME) == 0) {
456 nnpfs_namei_free(&nd.ni_cnd);
457 printf("lookup: not SAVENAME, flags 0x%lx\n", nd.ni_cnd.cn_flags);
458 return EINVAL;
461 VATTR_NULL(&vat);
462 vat.va_type = VREG;
463 vat.va_mode = S_IRUSR|S_IWUSR;
464 if ((nd.ni_cnd.cn_flags & HASBUF) == 0)
465 panic("HASBUF was cleared\n");
467 /* nd.ni_cnd.cn_flags |= HASBUF; */
469 #ifdef __FreeBSD__
470 (void)vn_start_write(cachedir, &mp, V_WAIT); /* V_NOWAIT? */
471 #endif
472 VOP_LEASE(cachedir, p, cred, LEASE_WRITE);
473 error = VOP_CREATE(cachedir, vpp, &nd.ni_cnd, &vat);
475 #ifdef __FreeBSD__
476 nnpfs_namei_free(&nd.ni_cnd);
477 nnpfs_vfs_unlock(cachedir, p);
478 vn_finished_write(mp);
479 #else
480 /* NetBSD and OpenBSD releases buf w/o clearing HASBUF */
481 nd.ni_cnd.cn_flags &= ~HASBUF;
482 nnpfs_namei_frees++;
483 #endif
485 if (error) {
486 printf("nnpfs open_file(%p, %s) create -> %d\n",
487 cachedir, name, error);
488 return error;
490 } else {
491 *vpp = nd.ni_vp;
492 nnpfs_namei_free(&nd.ni_cnd);
495 #if defined(__FreeBSD__) && 0
496 if (nd.ni_vp
497 && vn_canvmio(nd.ni_vp) == TRUE
498 && ((nd.ni_cnd.cn_flags & (NOOBJ|LOCKLEAF)) == LOCKLEAF))
499 vfs_object_create(nd.ni_vp, p, cred);
500 #endif
502 #ifdef __FreeBSD__
503 error = VOP_OPEN(*vpp, fmode, cred, p, -1);
504 #else
505 error = VOP_OPEN(*vpp, fmode, cred, p);
506 #endif
508 if (error) {
509 nnpfs_vput(*vpp);
510 } else {
511 if (fmode & FWRITE)
512 (*vpp)->v_writecount++;
514 nnpfs_vfs_unlock(*vpp, p);
518 #endif /* !__APPLE__! */
520 NNPFSDEB(XDEBNODE, ("nnpfs open_file(%p, %s) -> %d (%p)\n",
521 cachedir, name, error, *vpp));
522 return error;
526 * open indicated cache block file. needs to be closed by caller.
530 nnpfs_block_open(struct nnpfs_node *node, uint64_t offset, int flags,
531 struct vnode **vpp)
533 char cachename[NNPFS_CACHE_PATH_SIZE];
534 uint64_t blockindex = nnpfs_block_index(offset);
535 struct nnpfs *nnpfsp = NNPFS_FROM_XNODE(node);
536 off_t eof = nnpfs_vattr_get_size(&node->attr);
537 int ret;
539 NNPFSDEB(XDEBNODE, ("nnpfs_block_open(0x%llx)\n", (unsigned long long)offset));
541 nnpfs_assert(nnpfsp);
543 nnpfs_assert(nnpfs_block_have_p(node, offset)
544 || (flags & O_CREAT));
546 if (nnpfs_vnode_isdir(XNODE_TO_VNODE(node))) {
547 nnpfs_assert((flags & O_CREAT) == 0);
548 *vpp = node->cache_vn;
549 ret = 0;
550 } else {
551 #ifdef __APPLE__
552 ret = snprintf(cachename, sizeof(cachename),
553 NNPFS_CACHE_FILE_PATH,
554 node->index / 0x100, node->index % 0x100,
555 (unsigned long long)blockindex);
556 #else
557 ret = snprintf(cachename, sizeof(cachename),
558 NNPFS_CACHE_FILE_BLOCK_PATH,
559 (unsigned long long)blockindex);
560 #endif
562 nnpfs_assert(ret > 0 && ret < sizeof(cachename)); /* XXX */
564 ret = open_file(node->cache_vn, cachename, flags, nnpfsp->ctx, vpp);
565 nnpfs_debug_assert(!ret);
566 if (ret)
567 return ret;
570 /* blocks in the middle of the file should be of full length */
571 if ((flags & O_CREAT) && offset < nnpfs_offset(eof)) {
572 ret = nnpfs_block_extend_int(node, *vpp, nnpfs_curproc());
573 nnpfs_debug_assert(!ret);
574 if (ret)
575 nnpfs_block_close(node, *vpp,
576 ((flags & FWRITE) == FWRITE) ? 1 : 0);
579 NNPFSDEB(XDEBNODE, ("nnpfs_block_open -> %d\n", ret));
581 #if 0
582 nnpfs_assert(node->cache_vn);
583 if (VOP_ISLOCKED(node->cache_vn, nnpfs_curproc())) {
584 printf("%p is locked at %d\n", node->cache_vn, __LINE__);
585 panic("locked at block_open:exit");
587 #endif
589 return ret;
592 void
593 nnpfs_block_close(struct nnpfs_node *node, struct vnode *vp, int rw)
595 NNPFSDEB(XDEBNODE, ("nnpfs_block_close(%p)\n", vp));
597 if (nnpfs_vnode_isdir(XNODE_TO_VNODE(node)))
598 return;
600 #ifdef __APPLE__
601 vnode_close(vp, 0, NULL);
602 #else
604 d_thread_t *p = nnpfs_curproc();
606 nnpfs_vfs_writelock(vp, p);
608 if (rw)
609 vp->v_writecount--;
611 VOP_CLOSE(vp, rw ? FWRITE : FREAD, NULL, p);
612 nnpfs_vput(vp);
614 #endif /* !__APPLE__ */
616 NNPFSDEB(XDEBNODE, ("nnpfs_block_close done\n"));
620 * Create the indicated block and mark it as present in cache.
622 * Intended for writes beyond EOF.
626 nnpfs_block_create(struct nnpfs_node *node, uint64_t offset)
628 struct nnpfs_message_appenddata msg;
629 struct nnpfs *nnpfsp = NNPFS_FROM_XNODE(node);
630 off_t eof = nnpfs_vattr_get_size(&node->attr);
631 struct vnode *vp;
632 int ret;
634 nnpfs_assert(!nnpfs_block_have_p(node, offset));
635 nnpfs_assert(!nnpfs_vnode_isdir(XNODE_TO_VNODE(node)));
637 /* printf("nnpfs_block_create @0x%llx\n", (unsigned long long)offset);*/
639 NNPFSDEB(XDEBNODE, ("nnpfs_block_create: %lx @0x%llx\n",
640 (unsigned long)node, (unsigned long long )offset));
642 ret = nnpfs_block_setvalid(node, offset);
643 if (ret) {
644 nnpfs_debug_assert(0);
645 return ret;
648 ret = nnpfs_block_open(node, offset, O_CREAT|FWRITE, &vp);
649 if (!ret) {
650 nnpfs_assert(vp);
651 nnpfs_block_close(node, vp, 1);
654 /* extend previously last block to full length */
655 if (!ret && eof < offset) {
656 uint64_t prevoff = nnpfs_offset(eof);
657 if (nnpfs_block_have_p(node, prevoff))
658 ret = nnpfs_block_extend(node, prevoff);
661 nnpfs_debug_assert(!ret);
663 if (ret) {
664 /* XXX roll back file changes? */
665 nnpfs_block_setinvalid(node, offset);
666 return ret;
669 while (nnpfsp->appendquota < nnpfs_blocksize
670 && nnpfsp->status & CHANNEL_OPENED) {
671 int waiting = (nnpfsp->status & NNPFS_QUOTAWAIT);
672 nnpfsp->status |= NNPFS_QUOTAWAIT;
673 /* XXX */
674 (void)nnpfs_dev_msleep(nnpfsp, (caddr_t)&nnpfsp->appendquota,
675 (PZERO + 1), "nnpfsquota");
676 if (!waiting)
677 nnpfsp->status &= ~NNPFS_QUOTAWAIT;
680 if ((nnpfsp->status & CHANNEL_OPENED) == 0)
681 return ENODEV;
683 nnpfsp->appendquota -= nnpfs_blocksize;
684 nnpfs_assert(nnpfsp->appendquota >= 0);
686 msg.header.opcode = NNPFS_MSG_APPENDDATA;
687 msg.handle = node->handle;
688 msg.offset = offset;
690 /* XXX currently no cleanup on failed send, hope it's just a devclose */
691 return nnpfs_message_send(nnpfsp, &msg.header, sizeof(msg));