2 * Copyright (c) 1995 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, 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 Institute 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 INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * 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 INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 #include <sys/types.h>
43 #ifdef HAVE_SYS_MMAN_H
48 #include <fs_errors.h>
52 #include <arla_local.h>
61 static inline FCacheEntry
*
64 return ((struct abuf_data
*)(f
)->data
)->entry
;
71 * mmap implementation for copy{rx2cache,cache2rx}. It's a little
72 * complicated to support reading/writing on non page boundaries, plus
76 #if !defined(MAP_FAILED)
77 #define MAP_FAILED ((void *)(-1))
82 * use mmap for transfer between rx call and cache files
86 cachetransfer(struct rx_call
*call
, FCacheEntry
*entry
,
87 off_t off
, off_t len
, Bool rxwritep
)
92 off_t adjust_off
, adjust_len
;
93 size_t mmap_len
, block_len
;
95 int iosize
= getpagesize();
101 adjust_off
= off
% iosize
;
104 off_t real_off
= off
- adjust_off
;
105 off_t block_off
= block_offset(real_off
);
106 off_t mmap_off
= real_off
- block_off
;
108 block_len
= fcache_getblocksize() - mmap_off
;
109 size
= len
+ adjust_off
;
110 if (size
> block_len
)
113 adjust_len
= iosize
- (size
% iosize
);
114 mmap_len
= size
+ adjust_len
;
116 if (fd
== 0 || mmap_off
== 0) {
121 fd
= fcache_open_block(entry
, block_off
, !rxwritep
);
127 * always truncate to be on the "safe" side.
128 * We assume that we always get a full block or to EOF.
131 ret
= ftruncate(fd
, mmap_len
);
138 buf
= mmap(0, mmap_len
, PROT_READ
, MAP_PRIVATE
, fd
, mmap_off
);
140 buf
= mmap(0, mmap_len
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, mmap_off
);
142 if (buf
== (void *) MAP_FAILED
) {
148 rw_len
= rx_Write(call
, (char *)buf
+ adjust_off
, size
- adjust_off
);
150 rw_len
= rx_Read(call
, (char *)buf
+ adjust_off
, size
- adjust_off
);
152 if (rw_len
!= mmap_len
- adjust_off
)
153 ret
= conv_to_arla_errno(rx_GetCallError(call
));
160 if (msync(buf
, mmap_len
, MS_ASYNC
))
163 if (munmap(buf
, mmap_len
))
175 #else /* !HAVE_MMAP */
178 * use malloc for transfer between rx call and cache files
182 cachetransfer(struct rx_call
*call
, FCacheEntry
*entry
,
183 off_t off
, off_t len
, Bool rxwritep
)
187 size_t io_len
, block_len
;
188 ssize_t nread
, nwrite
;
189 u_long bufsize
= 8192;
195 buf
= malloc(bufsize
);
200 uint64_t block_off
= block_offset(off
);
201 off_t buf_off
= off
- block_off
;
203 if (block_off
== off
) {
204 if ((fd
!= 0 && close(fd
) != 0)
205 || (fd
= fcache_open_block(entry
, block_off
, !rxwritep
)) < 0) {
208 arla_debug_assert(0);
213 io_len
= min(bufsize
, len
);
214 block_len
= fcache_getblocksize() - buf_off
;
216 if (io_len
> block_len
)
220 nread
= pread(fd
, buf
, io_len
, buf_off
);
223 arla_debug_assert(0);
227 nwrite
= rx_Write(call
, buf
, nread
);
228 if (nwrite
!= nread
) {
229 ret
= conv_to_arla_errno(rx_GetCallError(call
));
233 nread
= rx_Read(call
, buf
, io_len
);
234 if (nread
!= io_len
) {
235 ret
= conv_to_arla_errno(rx_GetCallError(call
));
239 nwrite
= pwrite(fd
, buf
, nread
, buf_off
);
240 if (nwrite
!= nread
) {
242 arla_debug_assert(0);
260 #endif /* !HAVE_MMAP */
263 * Copy from a RX_call to a cache node.
264 * The area between offset and len + offset should be present in the cache.
266 * Returns 0 or error.
270 copyrx2cache(struct rx_call
*call
, FCacheEntry
*entry
, off_t off
, off_t len
)
272 return cachetransfer(call
, entry
, off
, len
, FALSE
);
276 * Copy `len' bytes from `entry' to `call'.
277 * Returns 0 or error.
281 copycache2rx(FCacheEntry
*entry
, struct rx_call
*call
, off_t off
, off_t len
)
283 return cachetransfer(call
, entry
, off
, len
, TRUE
);
287 * actually do the malloc + read thing
291 abuf_populate(fbuf
*f
)
293 uint64_t block_off
= 0;
304 arla_warnx(ADEBWARN
, "abuf_populate: malloc(%lu) failed",
306 arla_debug_assert(0);
311 block_off
= block_offset(off
);
312 off_t r_len
= min(len
, fcache_getblocksize());
314 if ((fd
!= 0 && close(fd
) != 0)
315 || (fd
= fcache_open_block(abuf_entry(f
),
316 block_off
, FALSE
)) < 0) {
322 nread
= pread(fd
, buf
+ off
, r_len
, off
- block_off
);
323 if (nread
!= r_len
) {
345 * infrastructure for truncate handling
348 struct truncate_cb_data
{
351 uint64_t prev_last_off
;
355 abuf_truncate_block(FCacheEntry
*entry
, uint64_t offset
, uint64_t blocklen
)
358 int fd
= fcache_open_block(entry
, offset
, TRUE
);
361 arla_warnx(ADEBWARN
, "abuf_truncate_block: "
362 "open failed at offset 0x%" PRIX64
"\n", offset
);
363 arla_debug_assert(0);
367 ret
= ftruncate(fd
, blocklen
);
370 arla_warnx(ADEBWARN
, "abuf_truncate_block: "
371 "truncate failed at offset 0x%" PRIX64
"\n", offset
);
372 arla_debug_assert(0);
381 truncate_callback(struct block
*block
, void *data
)
383 struct truncate_cb_data
*cb_data
= (struct truncate_cb_data
*)data
;
385 if (block
->offset
>= cb_data
->length
&& block
->offset
!= 0) {
386 fcache_throw_block(block
);
387 } else if (cb_data
->last_off
== block
->offset
) {
388 (void)abuf_truncate_block(block
->node
, block
->offset
,
389 cb_data
->length
- block
->offset
);
390 } else if (cb_data
->prev_last_off
== block
->offset
) {
391 uint64_t blocklen
= cb_data
->length
- block
->offset
;
392 uint64_t blocksize
= fcache_getblocksize();
394 if (blocklen
> blocksize
)
395 blocklen
= blocksize
;
397 (void)abuf_truncate_block(block
->node
, block
->offset
, blocklen
);
399 /* block should be ok */
404 * truncate the cache data for real, update 'have' flags
408 abuf_truncate_int(FCacheEntry
*entry
, off_t length
)
410 struct truncate_cb_data data
;
412 data
.length
= length
;
413 data
.last_off
= block_offset(length
);
415 block_offset(fcache_get_status_length(&entry
->status
));
417 block_foreach(entry
, truncate_callback
, &data
);
422 * Change the size of the underlying cache and the fbuf to `new_len'
424 * Returns 0 or error.
428 abuf_truncate_op(fbuf
*f
, size_t new_len
)
436 ret
= abuf_truncate_int(abuf_entry(f
), new_len
);
441 void *buf
= realloc(f
->buf
, new_len
);
460 arla_debug_assert(0);
466 * Change the size of the underlying cache and the fbuf to `new_len'
468 * Returns 0 or error.
472 abuf_truncate(FCacheEntry
*entry
, size_t new_len
)
474 return abuf_truncate_int(entry
, new_len
);
478 purge_callback(struct block
*block
, void *data
)
480 fcache_throw_block(block
);
484 * Throw all data in the node. This is a special case of truncate
485 * that does not leave block zero.
489 abuf_purge(FCacheEntry
*entry
)
491 block_foreach(entry
, purge_callback
, NULL
);
496 * Create a fbuf with (fd, len, flags).
497 * Returns 0 or error.
501 abuf_create(fbuf
*f
, FCacheEntry
*entry
, size_t len
, fbuf_flags flags
)
503 struct abuf_data
*data
= malloc(sizeof(*data
));
514 f
->truncate
= abuf_truncate_op
;
516 return abuf_populate(f
);
520 * Write out the data of `f' to the file.
521 * Returns 0 or error.
528 uint64_t block_off
= 0;
535 if ((f
->flags
& FBUF_WRITE
) != FBUF_WRITE
)
538 while (len
> 0 && ret
== 0) {
539 size_t size
= min(len
, fcache_getblocksize());
541 if ((fd
= fcache_open_block(abuf_entry(f
),
542 block_off
, TRUE
)) < 0) {
547 nwrite
= write(fd
, (char *)f
->buf
+ block_off
, size
);
548 if (nwrite
!= size
) {
555 block_off
+= fcache_getblocksize();
564 * Returns 0 or error.