2 * Copyright (c) 2005-2007, Stockholms Universitet
3 * (Stockholm University, Stockholm Sweden)
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
36 #include <nnpfs/nnpfs_locl.h>
37 #include <nnpfs/nnpfs_fs.h>
38 #include <nnpfs/nnpfs_dev.h>
39 #include <nnpfs/nnpfs_deb.h>
42 * return true if block is in cache
46 nnpfs_block_have_p(struct nnpfs_node
*node
, uint64_t offset
)
48 struct nnpfs_cache_handle
*handle
= &node
->data
;
49 uint32_t index
= nnpfs_block_index(offset
);
50 uint32_t maskno
= nnpfs_block_masknumber(index
);
52 nnpfs_assert(nnpfs_offset(offset
) == offset
);
54 if (handle
->nmasks
== 0)
57 if (maskno
>= handle
->nmasks
)
60 if (handle
->nmasks
== 1)
61 return (handle
->masks
.first
& nnpfs_block_mask(index
));
63 return (handle
->masks
.list
[maskno
] & nnpfs_block_mask(index
));
67 * mark block at offset as present in cache
69 * XXX assert on the bit being changed?
73 nnpfs_block_set_have(struct nnpfs_node
*node
, uint64_t offset
, int val
)
75 struct nnpfs_cache_handle
*handle
= &node
->data
;
76 uint32_t index
= nnpfs_block_index(offset
);
77 uint32_t maskno
= nnpfs_block_masknumber(index
);
78 uint32_t mask
= nnpfs_block_mask(index
);
81 nnpfs_assert(nnpfs_offset(offset
) == offset
);
83 if (maskno
== 0 && handle
->nmasks
<= 1) {
85 slot
= &handle
->masks
.first
;
87 if (maskno
>= handle
->nmasks
) {
88 int n
= maskno
+ NNPFS_NMASKS
- (maskno
% NNPFS_NMASKS
);
89 int size
= n
* sizeof(uint32_t);
94 new = nnpfs_alloc(size
, M_NNPFS_BLOCKS
);
95 nnpfs_debug_assert(new);
99 if (handle
->nmasks
== 1) {
100 new[0] = handle
->masks
.first
;
101 } else if (handle
->nmasks
> 1) {
102 memcpy(new, handle
->masks
.list
,
103 handle
->nmasks
* sizeof(uint32_t));
104 nnpfs_free(handle
->masks
.list
, handle
->nmasks
* sizeof(uint32_t),
108 memset(&new[handle
->nmasks
], 0,
109 (n
- handle
->nmasks
) * sizeof(uint32_t));
111 handle
->masks
.list
= new;
113 slot
= &handle
->masks
.list
[maskno
];
125 * mark block at offset as present in cache
129 nnpfs_block_setvalid(struct nnpfs_node
*node
, uint64_t offset
)
131 return nnpfs_block_set_have(node
, offset
, TRUE
);
135 * mark block at offset as not present in cache
139 nnpfs_block_setinvalid(struct nnpfs_node
*node
, uint64_t offset
)
141 (void)nnpfs_block_set_have(node
, offset
, FALSE
);
145 nnpfs_block_foreach_int(struct nnpfs_node
*node
,
146 nnpfs_block_callback_t fun
,
148 uint64_t base_offset
,
151 uint32_t tmp_mask
= 1;
157 for (i
= 0; i
< 32; i
++) {
158 if (mask
& tmp_mask
) {
159 fun(node
, base_offset
+ i
* nnpfs_blocksize
, data
);
165 tmp_mask
= tmp_mask
<< 1;
170 * call callback for every block present in cache
174 nnpfs_block_foreach(struct nnpfs_node
*node
,
175 nnpfs_block_callback_t fun
,
178 struct nnpfs_cache_handle
*handle
= &node
->data
;
181 if (handle
->nmasks
== 0)
184 if (handle
->nmasks
== 1) {
185 nnpfs_block_foreach_int(node
, fun
, data
, 0, handle
->masks
.first
);
189 for (i
= 0; i
< handle
->nmasks
; i
++)
190 nnpfs_block_foreach_int(node
, fun
, data
, i
* 32 * nnpfs_blocksize
,
191 handle
->masks
.list
[i
]);
195 * Foreach callback for nnpfs_block_truncate()
199 truncate_callback(struct nnpfs_node
*node
, uint64_t offset
, void *data
)
201 uint64_t *size
= (uint64_t *)data
;
202 if (*size
<= offset
&& offset
> 0)
203 (void)nnpfs_block_set_have(node
, offset
, FALSE
);
207 * Forget all blocks beyond `sizeĀ“ for `node'
211 nnpfs_block_truncate(struct nnpfs_node
*node
, uint64_t size
)
213 nnpfs_block_foreach(node
, truncate_callback
, &size
);
217 * free all handle internal resources
221 nnpfs_block_free_all(struct nnpfs_node
*node
)
223 struct nnpfs_cache_handle
*handle
= &node
->data
;
224 if (handle
->nmasks
> 1) {
225 nnpfs_free(handle
->masks
.list
, handle
->nmasks
* sizeof(uint32_t),
227 handle
->masks
.list
= NULL
;
229 handle
->masks
.first
= 0;
236 * return true if we have no data
240 nnpfs_block_empty(struct nnpfs_node
*node
)
242 struct nnpfs_cache_handle
*handle
= &node
->data
;
245 if (handle
->nmasks
== 0)
248 if (handle
->nmasks
== 1) {
249 if (handle
->masks
.first
== 0)
254 for (i
= 0; i
< handle
->nmasks
; i
++)
255 if (handle
->masks
.list
[i
] != 0)
262 nnpfs_block_extend_int(struct nnpfs_node
*node
, struct vnode
*vp
)
264 struct nnpfs_vfs_vattr va
;
268 nnpfs_set_va_size(&va
, nnpfs_blocksize
);
269 ret
= nnpfs_vnode_setattr(vp
, &va
, NNPFS_FROM_XNODE(node
)->ctx
);
270 nnpfs_debug_assert(!ret
);
275 * Extend an existing block to full block size.
279 nnpfs_block_extend(struct nnpfs_node
*node
, uint64_t offset
)
284 nnpfs_assert(nnpfs_block_have_p(node
, offset
));
286 ret
= nnpfs_block_open(node
, offset
, O_RDWR
|FWRITE
, &vp
);
289 ret
= nnpfs_block_extend_int(node
, vp
);
290 nnpfs_block_close(vp
, 1);
294 printf("nnpfs_block_extend: failed at offset 0x%llx: %d\n",
295 (unsigned long long)offset
, ret
);
301 * a handy implementation of open()
303 * nnpfs_block_close() on success.
307 open_file(const char *name
, int fmode
,
308 nnpfs_vfs_context ctx
, struct vnode
**vp
)
312 #ifdef __APPLE__ /* XXX */
313 error
= vnode_open(name
, fmode
, S_IRUSR
|S_IWUSR
, 0, vp
, ctx
);
316 struct nameidata nd
, *ndp
= &nd
;
317 d_thread_t
*p
= nnpfs_curproc();
318 nnpfs_kernel_cred cred
= nnpfs_proc_to_cred(p
);
320 NDINIT(ndp
, LOOKUP
, 0, UIO_SYSSPACE
, name
,
321 nnpfs_vfs_context_proc(ctx
));
329 #if defined(__FreeBSD__) && __FreeBSD_version >= 502000
330 error
= VOP_OPEN(*vp
, fmode
& (FWRITE
|FREAD
), cred
, p
, -1);
332 error
= VOP_OPEN(*vp
, fmode
& (FWRITE
|FREAD
), cred
, p
);
343 * open indicated cache block file. needs to be closed by caller.
347 nnpfs_block_open(struct nnpfs_node
*node
, uint64_t offset
, int flags
,
350 char cachename
[NNPFS_CACHE_PATH_SIZE
];
351 uint64_t blockindex
= nnpfs_block_index(offset
);
352 uint32_t id
= node
->index
;
353 struct nnpfs
*nnpfsp
= NNPFS_FROM_XNODE(node
);
354 off_t eof
= nnpfs_vattr_get_size(&node
->attr
);
357 NNPFSDEB(XDEBNODE
, ("nnpfs_block_open\n"));
359 nnpfs_assert(nnpfsp
);
361 nnpfs_assert(nnpfs_block_have_p(node
, offset
)
362 || (flags
& O_CREAT
));
364 if (nnpfs_vnode_isdir(XNODE_TO_VNODE(node
)))
365 ret
= snprintf(cachename
, sizeof(cachename
),
366 NNPFS_CACHE_DIR_PATH
,
367 id
/ 0x100, id
% 0x100);
369 ret
= snprintf(cachename
, sizeof(cachename
),
370 NNPFS_CACHE_FILE_PATH
,
371 id
/ 0x100, id
% 0x100,
372 (unsigned long long)blockindex
);
374 nnpfs_assert(ret
> 0 && ret
< sizeof(cachename
)); /* XXX */
376 ret
= open_file(cachename
, flags
, nnpfsp
->ctx
, vpp
);
377 nnpfs_debug_assert(!ret
);
381 /* blocks in the middle of the file should be of full length */
382 if ((flags
& O_CREAT
) && offset
< nnpfs_offset(eof
)) {
383 ret
= nnpfs_block_extend_int(node
, *vpp
);
384 nnpfs_debug_assert(!ret
);
386 nnpfs_block_close(*vpp
,
387 ((flags
& FWRITE
) == FWRITE
) ? 1 : 0);
390 NNPFSDEB(XDEBNODE
, ("nnpfs_block_open -> %d\n", ret
));
395 nnpfs_block_close(struct vnode
*vp
, int rw
)
398 vnode_close(vp
, 0, NULL
);
400 d_thread_t
*p
= nnpfs_curproc();
401 struct ucred
*cred
= NULL
;
403 VOP_CLOSE(vp
, rw
? FWRITE
: FREAD
, cred
, p
);
409 * Create the indicated block and mark it as present in cache.
411 * Intended for writes beyond EOF.
415 nnpfs_block_create(struct nnpfs_node
*node
, uint64_t offset
)
417 struct nnpfs_message_appenddata msg
;
418 struct nnpfs
*nnpfsp
= NNPFS_FROM_XNODE(node
);
419 off_t eof
= nnpfs_vattr_get_size(&node
->attr
);
423 nnpfs_assert(!nnpfs_block_have_p(node
, offset
));
424 nnpfs_assert(!nnpfs_vnode_isdir(XNODE_TO_VNODE(node
)));
426 NNPFSDEB(XDEBNODE
, ("nnpfs_block_create: %lx @0x%llx\n",
427 (unsigned long)node
, (unsigned long long )offset
));
429 ret
= nnpfs_block_setvalid(node
, offset
);
431 nnpfs_debug_assert(0);
435 ret
= nnpfs_block_open(node
, offset
, O_CREAT
|FWRITE
, &vp
);
438 nnpfs_block_close(vp
, 1);
441 /* extend previously last block to full length */
442 if (!ret
&& eof
< offset
) {
443 uint64_t prevoff
= nnpfs_offset(eof
);
444 if (nnpfs_block_have_p(node
, prevoff
))
445 ret
= nnpfs_block_extend(node
, prevoff
);
448 nnpfs_debug_assert(!ret
);
451 /* XXX roll back file changes? */
452 nnpfs_block_setinvalid(node
, offset
);
456 while (nnpfsp
->appendquota
< nnpfs_blocksize
457 && nnpfsp
->status
& CHANNEL_OPENED
) {
458 int waiting
= (nnpfsp
->status
& NNPFS_QUOTAWAIT
);
459 nnpfsp
->status
|= NNPFS_QUOTAWAIT
;
461 (void)nnpfs_dev_msleep(nnpfsp
, (caddr_t
)&nnpfsp
->appendquota
,
462 (PZERO
+ 1), "nnpfsquota");
464 nnpfsp
->status
&= ~NNPFS_QUOTAWAIT
;
467 if ((nnpfsp
->status
& CHANNEL_OPENED
) == 0)
470 nnpfsp
->appendquota
-= nnpfs_blocksize
;
471 nnpfs_assert(nnpfsp
->appendquota
>= 0);
473 msg
.header
.opcode
= NNPFS_MSG_APPENDDATA
;
474 msg
.handle
= node
->handle
;
477 /* XXX currently no cleanup on failed send, hope it's just a devclose */
478 return nnpfs_message_send(nnpfsp
->fd
, &msg
.header
, sizeof(msg
));