2 * Copyright (c) 1995 - 2006 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
35 * This is the cache for files.
36 * The hash-table is keyed with (cell, volume, fid).
39 #include "arla_local.h"
42 #include <nnpfs/nnpfs_queue.h>
52 static int get_attr_bulk (FCacheEntry
*parent_entry
,
53 FCacheEntry
*prefered_entry
,
54 VenusFid
*prefered_fid
,
55 const char *prefered_name
,
59 resolve_mp(FCacheEntry
*e
, VenusFid
*ret_fid
, CredCacheEntry
**ce
);
62 fcache_want_bytes(uint64_t wanted
);
65 fcache_need_bytes(uint64_t needed
);
68 fcache_verify_data(FCacheEntry
*e
, CredCacheEntry
*ce
,
69 uint64_t offset
, uint64_t end
);
72 setbusy_block(struct block
*b
, Bool val
);
75 throw_block(struct block
*b
, Bool usagep
);
78 fcache_update_usage(FCacheEntry
*e
, int nblocks
);
81 * Local data for this module.
85 * Hash table for all the nodes known by the cache manager keyed by
86 * (cell, volume, vnode, unique).
89 static Hashtab
*hashtab
;
92 * LRU lists, sorted sorted in LRU-order. The head is the MRU and the
93 * tail the LRU, which is from where we get entries when we have no
98 * List of all nodes known to kernel.
101 static List
*kernel_node_lru
;
104 * List of all nodes in hashtab that are not known to kernel.
107 static List
*node_lru
;
110 * Pool of all nodes not in `hashtab'.
113 static List
*free_nodes
;
116 * List of all data blocks known to kernel.
119 static List
*kernel_block_lru
;
122 * List of all data blocks not known to kernel.
125 static List
*block_lru
;
129 * Heap of entries to be invalidated.
132 static Heap
*invalid_heap
;
134 /* low and high-water marks for vnodes and space */
136 static u_long highvnodes
, lowvnodes
, current_vnodes
;
137 static int64_t highbytes
, lowbytes
;
140 /* block size used in cache */
142 static uint64_t blocksize
;
146 static int64_t usedbytes
, needbytes
, wantbytes
;
147 static u_long usedvnodes
;
149 static int64_t appendquota
, appendquota_used
;
151 /* Map with recovered nodes */
153 static uint32_t maxrecovered
;
155 static char *recovered_map
;
158 /* Handling fair lock queue */
161 FCacheEntry
*entry
; /* NULL for empty slots */
162 NNPQUEUE_ENTRY(lock_waiter
) queue
;
164 unsigned gcp
: 1; /* Accepts gc-flagged entry? */
168 NNPQUEUE_HEAD(locks_head
, lock_waiter
);
169 static struct locks_head
*lockwaiters
;
170 static unsigned num_locks
;
172 static const AFSCallBack broken_callback
= {0, 0, CBDROPPED
};
175 set_recovered(uint32_t index
)
180 if (index
>= maxrecovered
) {
181 oldmax
= maxrecovered
;
182 maxrecovered
= (index
+ 16) * 2;
183 p
= realloc(recovered_map
, maxrecovered
);
185 arla_errx(1, ADEBERROR
, "fcache: realloc %lu recovered_map failed",
186 (unsigned long)maxrecovered
);
189 memset(recovered_map
+ oldmax
, 0, maxrecovered
- oldmax
);
191 recovered_map
[index
] = 1;
194 #define IS_RECOVERED(index) (recovered_map[(index)])
197 * This is how far the cleaner will go to clean out entries.
198 * The higher this is, the higher the risk is that you will
199 * lose any file that you feel is important to disconnected
203 Bool fprioritylevel
= arla_FPRIO_DEFAULT
;
205 static uint32_t node_count
= NNPFS_NO_INDEX
; /* XXX */
208 * This is set to non-zero when we want to use bulkstatus(). 2 means
209 * that the nodes should be installed into the kernel.
212 static int fcache_enable_bulkstatus
= 1;
213 static int fcache_bulkstatus_num
= 14; /* XXX should use the [P]MTU */
215 #define FCHASHSIZE 997
221 #define CLEANER_SLEEP 10
223 static PROCESS cleaner_pid
;
225 #define CLEANER_MARKER (void*)4711
228 * The creator of nodes.
231 static PROCESS create_nodes_pid
;
237 static PROCESS invalidator_pid
;
240 * Smalltalk emulation
244 fcache_highbytes(void)
250 fcache_usedbytes(void)
252 return usedbytes
- appendquota
+ appendquota_used
;
256 fcache_lowbytes(void)
262 fcache_highvnodes(void)
268 fcache_usedvnodes(void)
274 fcache_lowvnodes(void)
281 fcache_getblocksize(void)
287 fcache_setblocksize(uint64_t newsize
)
294 * Fair lock handling:
295 * locked nodes are flagged with `locked'
296 * nodes with lock queue are flagged with `lockwait'
299 static struct locks_head
*
300 find_lock_head(FCacheEntry
*node
)
303 if (node
->flags
.lockwait
) {
304 for (i
= 0; i
< num_locks
; i
++) {
305 struct lock_waiter
*w
= NNPQUEUE_FIRST(&lockwaiters
[i
]);
306 if (w
&& w
->entry
== node
)
307 return &lockwaiters
[i
];
311 for (i
= 0; i
< num_locks
; i
++)
312 if (NNPQUEUE_EMPTY(&lockwaiters
[i
]))
313 return &lockwaiters
[i
];
315 assert(0); /* XXX overflow */
320 take_lock(FCacheEntry
*node
)
322 assert(CheckLock(&node
->lock
) == 0);
323 ObtainWriteLock(&node
->lock
);
324 node
->flags
.locked
= TRUE
;
328 fcache_islocked(FCacheEntry
*e
)
330 return e
->flags
.locked
;
336 * Caller accepts lock on a gcp-flagged node iff `gcp' is TRUE.
340 fcache_lock(FCacheEntry
*node
, Bool gcp
)
342 struct locks_head
*head
;
343 struct lock_waiter me
;
345 /* grab the lock if we can */
346 if (!node
->flags
.locked
&& (!node
->flags
.gcp
|| gcp
))
347 return take_lock(node
);
349 NNPQUEUE_INIT_ENTRY(&me
, queue
);
352 head
= find_lock_head(node
);
354 /* worker_setdebuginfo("lockwait"); */
356 node
->flags
.lockwait
= TRUE
;
357 NNPQUEUE_INSERT_TAIL(head
, &me
, queue
);
358 LWP_WaitProcess(&me
);
360 assert(node
->flags
.locked
);
361 assert(gcp
|| !node
->flags
.gcp
);
363 NNPQUEUE_REMOVE(&me
, head
, queue
);
364 if (NNPQUEUE_EMPTY(head
))
365 node
->flags
.lockwait
= FALSE
;
367 return take_lock(node
);
371 fcache_unlock(FCacheEntry
*node
)
373 struct locks_head
*head
;
374 struct lock_waiter
*next
= NULL
;
375 struct lock_waiter
*w
;
377 assert(node
->flags
.locked
);
378 AssertExclLocked(&node
->lock
);
379 ReleaseWriteLock(&node
->lock
);
381 if (!node
->flags
.lockwait
) {
382 node
->flags
.locked
= FALSE
;
386 head
= find_lock_head(node
);
387 if (node
->flags
.gcp
) {
388 NNPQUEUE_FOREACH(w
, head
, queue
) {
395 next
= NNPQUEUE_FIRST(head
);
400 LWP_NoYieldSignal(next
);
402 node
->flags
.locked
= FALSE
;
406 * Allocate some appropriate append quota for nnpfs and return the
407 * value. Should be called only once.
409 * XXX config option, better defaults.
413 fcache_set_appendquota(void)
415 int64_t newquota
, diff
;
417 assert(num_workers
> 2);
419 newquota
= (num_workers
- 2) * blocksize
;
420 assert(lowbytes
> newquota
);
422 if (newquota
> lowbytes
/2)
423 newquota
= block_offset(lowbytes
/3);
425 (void)fcache_need_bytes(newquota
); /* XXX best effort */
427 diff
= newquota
- appendquota
;
428 appendquota
= newquota
;
431 /* appendquota_used may now be larger than quota. That's ok. */
441 unsigned long fetch_attr
;
442 unsigned long fetch_attr_cached
;
443 unsigned long fetch_attr_bulk
;
444 unsigned long fetch_data
;
445 unsigned long fetch_data_cached
;
446 unsigned long store_attr
;
447 unsigned long store_data
;
451 * Compare two entries. Return 0 if and only if the same.
455 fcachecmp (void *a
, void *b
)
457 FCacheEntry
*f1
= (FCacheEntry
*)a
;
458 FCacheEntry
*f2
= (FCacheEntry
*)b
;
460 return VenusFid_cmp(&f1
->fid
, &f2
->fid
);
464 * Hash the value of an entry.
470 FCacheEntry
*f
= (FCacheEntry
*)e
;
472 return f
->fid
.Cell
+ f
->fid
.fid
.Volume
+ f
->fid
.fid
.Vnode
477 * Compare expiration times.
481 expiration_time_cmp (const void *a
, const void *b
)
483 const FCacheEntry
*f1
= (const FCacheEntry
*)a
;
484 const FCacheEntry
*f2
= (const FCacheEntry
*)b
;
486 return f1
->callback
.ExpirationTime
- f2
->callback
.ExpirationTime
;
490 recon_hashtabadd(FCacheEntry
*entry
)
492 hashtabadd(hashtab
,entry
);
496 recon_hashtabdel(FCacheEntry
*entry
)
498 hashtabdel(hashtab
,entry
);
505 char **sysnamelist
= NULL
;
513 fcache_poller_unref(FCacheEntry
*e
)
515 AssertExclLocked(&e
->lock
);
518 poller_remove(e
->poll
);
524 fcache_poller_reref(FCacheEntry
*e
, ConnCacheEntry
*conn
)
526 PollerEntry
*pe
= e
->poll
;
527 AssertExclLocked(&e
->lock
);
529 e
->poll
= poller_add_conn(conn
);
536 * true if nobody is working on the entry
540 unreferenced(FCacheEntry
*e
)
542 if (!fcache_islocked(e
) && e
->refcount
== 0)
553 fcache_getdefsysname (void)
556 return "fool-dont-remove-all-sysnames";
557 return sysnamelist
[0];
565 fcache_setdefsysname (const char *sysname
)
568 return fcache_addsysname (sysname
);
569 free (sysnamelist
[0]);
570 sysnamelist
[0] = estrdup (sysname
);
579 fcache_addsysname (const char *sysname
)
582 sysnamelist
= erealloc (sysnamelist
,
583 sysnamenum
* sizeof(char *));
584 sysnamelist
[sysnamenum
- 1] = estrdup(sysname
);
593 fcache_removesysname (const char *sysname
)
596 for (i
= 0; i
< sysnamenum
; i
++)
597 if (strcmp (sysnamelist
[i
], sysname
) == 0)
601 free (sysnamelist
[i
]);
602 for (;i
< sysnamenum
; i
++)
603 sysnamelist
[i
] = sysnamelist
[i
+ 1];
605 sysnamelist
= erealloc (sysnamelist
,
606 sysnamenum
* sizeof(char *));
611 * The node is busy, flag it and wait for signal so we can retry our
616 wait_busy(FCacheEntry
*entry
)
618 entry
->flags
.waiters
= TRUE
;
619 fcache_unlock(entry
);
620 LWP_WaitProcess(entry
);
621 fcache_lock(entry
, FALSE
);
625 * Wake any waiters for node and clear flag.
629 wake_waiters(FCacheEntry
*e
)
631 if (e
->flags
.waiters
) {
632 e
->flags
.waiters
= FALSE
;
633 LWP_NoYieldSignal(e
);
638 * Check the wanted data range's presence in cache, return first block
639 * not already cached in *offset and *end
641 * Needed blocks found -> BLOCK_GOT
642 * No additional blocks needed -> BLOCK_NONE
643 * Only busy blocks needed -> BLOCK_BUSY
647 first_wanted_range(FCacheEntry
*entry
,
648 uint64_t wanted_offset
, uint64_t wanted_end
,
649 uint64_t *offset
, uint64_t *end
)
654 AssertExclLocked(&entry
->lock
);
656 assert(wanted_offset
<= wanted_end
);
658 /* find first block that's not in cache */
659 for (i
= block_offset(wanted_offset
); i
< wanted_end
; i
+= blocksize
) {
660 struct block
*block
= block_get(entry
, i
);
665 if (block
->flags
.busy
)
669 if (i
>= wanted_end
) {
673 return BLOCK_NONE
; /* entire wanted range is already in cache */
676 /* find first block present in cache after that one */
677 for (i
+= blocksize
; i
< wanted_end
; i
+= blocksize
) {
678 if (block_get(entry
, i
))
687 * Check the wanted data range's presence in cache.
688 * Only block presence is considered.
692 fcache_have_wanted(FCacheEntry
*entry
, uint64_t offset
, uint64_t end
)
696 AssertExclLocked(&entry
->lock
);
698 assert(offset
<= end
);
700 for (i
= block_offset(offset
); i
< end
; i
+= blocksize
) {
701 struct block
*block
= block_get(entry
, i
);
702 if (!block
|| block
->flags
.busy
)
710 * return the first directory name of the cached file for `entry'
714 fcache_dir_name (FCacheEntry
*entry
, char *s
, size_t len
)
716 return snprintf (s
, len
, "%02X", entry
->index
/ 0x100);
720 * return the second directory name of the cached file for `entry'.
724 fcache_file_name (FCacheEntry
*entry
, char *s
, size_t len
)
726 return snprintf (s
, len
, "%02X/%02X",
727 entry
->index
/ 0x100, entry
->index
% 0x100);
731 * return the file name of the cached file for `entry' and `offset'.
735 fcache_block_name (FCacheEntry
*entry
, uint64_t offset
, char *s
, size_t len
)
737 uint64_t index
= offset
/ blocksize
;
738 return snprintf (s
, len
, NNPFS_CACHE_FILE_PATH
, /* XXX windows */
739 entry
->index
/ 0x100, entry
->index
% 0x100,
740 (unsigned long long)index
);
744 * the filename for the extra (converted) directory
745 * XXX no blocks here for now
749 real_extra_file_name (FCacheEntry
*entry
, char *s
, size_t len
)
751 return snprintf(s
, len
, NNPFS_CACHE_DIR_PATH
, /* XXX windows */
752 entry
->index
/ 0x100, entry
->index
% 0x100);
756 * return the file name of the converted directory for `entry'.
760 fcache_extra_file_name (FCacheEntry
*entry
, char *s
, size_t len
)
762 assert (entry
->flags
.extradirp
&&
763 entry
->status
.FileType
== TYPE_DIR
);
765 return real_extra_file_name (entry
, s
, len
);
770 static int fhopen_working
;
773 * open file by handle
777 fcache_fhopen (fcache_cache_handle
*handle
, int flags
)
779 if (!handle
->valid
) {
784 #if defined(HAVE_GETFH) && defined(HAVE_FHOPEN)
789 memcpy (&fh
, &handle
->nnpfs_handle
, sizeof(fh
));
790 ret
= fhopen (&fh
, flags
);
802 * get the handle of `filename'
806 fcache_fhget (char *filename
, fcache_cache_handle
*handle
)
812 #if 0 /* block cache */
818 ret
= sscanf(filename
, "%02X/%02X", &a
, &b
);
822 GetCurrentDirectory(sizeof(buf
)-1, buf
);
823 buf
[sizeof(buf
) - 1] = '\0';
825 ret
= snprintf((char *)handle
->nnpfs_handle
,
826 sizeof(handle
->nnpfs_handle
),
827 "%s\\%02X\\%02X", buf
, a
, b
);
829 if (ret
> 0 && ret
< sizeof(handle
->nnpfs_handle
))
837 #if defined(HAVE_GETFH) && defined(HAVE_FHOPEN)
842 ret
= getfh (filename
, &fh
);
844 memcpy (&handle
->nnpfs_handle
, &fh
, sizeof(fh
));
855 struct arlaViceIoctl vice_ioctl
;
861 vice_ioctl
.in
= NULL
;
862 vice_ioctl
.in_size
= 0;
864 vice_ioctl
.out
= (caddr_t
)&handle
->nnpfs_handle
;
865 vice_ioctl
.out_size
= sizeof(handle
->nnpfs_handle
);
867 ret
= k_pioctl (filename
, ARLA_VIOC_FHGET
, (void *)&vice_ioctl
, 0);
881 make_dir(FCacheEntry
*entry
, const char *dirname
)
885 (void)unlink(dirname
); /* old implementation used files, remove if so */
887 ret
= mkdir(dirname
, bits
);
888 if (ret
< 0 && errno
!= EEXIST
) {
889 if (errno
== ENOENT
) {
890 char parent
[MAXPATHLEN
];
892 fcache_dir_name(entry
, parent
, sizeof(parent
));
893 ret
= mkdir(parent
, bits
);
895 arla_err (1, ADEBERROR
, errno
, "mkdir %s", parent
);
896 ret
= mkdir(dirname
, bits
);
898 arla_err (1, ADEBERROR
, errno
, "mkdir %s", dirname
);
900 arla_err (1, ADEBERROR
, errno
, "mkdir %s", dirname
);
906 * Get ourselves a clean cache dir, creating it if necessary.
908 * We always keep the first block as arlad/nnpfs traditionally
909 * depended on there being a cache file for every node, for
910 * attributes and data length, maybe more.
912 * XXX It may be time to remove this oddity. Would it add or
913 * remove special cases?
917 make_clean_dir(FCacheEntry
*entry
)
919 char name
[MAXPATHLEN
];
920 char dirname
[MAXPATHLEN
];
925 fcache_file_name (entry
, dirname
, sizeof(dirname
));
927 dir
= opendir(dirname
);
929 make_dir(entry
, dirname
);
933 while ((dp
= readdir(dir
)) != NULL
) {
934 if (strcmp(dp
->d_name
, ".") == 0
935 || strcmp(dp
->d_name
, "..") == 0
936 || strcmp(dp
->d_name
, "00") == 0)
939 ret
= snprintf(name
, sizeof(name
), "%s/%s", dirname
, dp
->d_name
);
940 if (ret
<= 0 || ret
>= sizeof(name
))
941 err (1, "dirname %s/%s", dirname
, dp
->d_name
);
945 err (1, "unlink %s", name
);
951 * create a new cache vnode, assume the entry is locked or private
955 fcache_create_file (FCacheEntry
*entry
, int create
)
957 char bname
[MAXPATHLEN
];
958 char extra_fname
[MAXPATHLEN
];
962 int flags
= O_RDWR
| O_BINARY
| O_CREAT
| O_TRUNC
;
965 flags
|= O_LARGEFILE
;
967 make_clean_dir(entry
);
969 fcache_block_name (entry
, 0, bname
, sizeof(bname
));
970 fd
= open(bname
, flags
, 0600);
972 arla_err (1, ADEBERROR
, errno
, "open %s", bname
);
974 arla_err (1, ADEBERROR
, errno
, "close %s", bname
);
977 real_extra_file_name (entry
, extra_fname
, sizeof(extra_fname
));
978 unlink (extra_fname
);
983 * Transfer used append quota to ordinary usage. `e' will be flagged
984 * with gcp and unlocked temporarily, this is to keep it from being
985 * locked by other operations while we may need to gc blocks from it.
987 * Give back quota to kernel, if any, and return error code.
991 fcache_update_appendquota(FCacheEntry
*e
)
997 AssertExclLocked(&e
->lock
);
1000 if (!appendquota_used
)
1003 worker_setdebuginfo("gc");
1004 assert(!e
->flags
.gcp
);
1006 e
->flags
.gcp
= TRUE
;
1011 diff
= appendquota_used
;
1019 ret
= fcache_want_bytes(diff
);
1021 } while (ret
&& !nthreads
);
1023 fcache_lock(e
, TRUE
);
1024 e
->flags
.gcp
= FALSE
;
1026 LWP_NoYieldSignal(e
);
1028 worker_setdebuginfo("not gc");
1033 assert(appendquota_used
>= 0);
1035 if (appendquota_used
< diff
)
1036 diff
= appendquota_used
;
1037 appendquota_used
-= diff
;
1040 assert(appendquota_used
>= 0);
1041 /* assert(appendquota_used <= appendquota); not on cache size change */
1043 return install_appendquota(diff
);
1047 * Create a block and set kernelp.
1051 fcache_append_block(FCacheEntry
*e
, uint64_t offset
)
1054 assert(block_offset(offset
) == offset
);
1056 appendquota_used
+= blocksize
;
1057 e
->usage
+= blocksize
;
1058 /* assert(appendquota_used <= appendquota); not on cache size change */
1060 b
= block_add(e
, offset
);
1061 b
->flags
.kernelp
= TRUE
;
1062 b
->lru_le
= listaddhead(kernel_block_lru
, b
);
1069 * Create a block, setting kernelp as indicated.
1073 create_block(FCacheEntry
*e
, uint64_t offset
)
1077 assert(block_offset(offset
) == offset
);
1079 worker_setdebuginfo("gc");
1081 ret
= fcache_update_usage(e
, 1);
1083 worker_setdebuginfo("not gc");
1088 b
= block_add(e
, offset
);
1089 b
->flags
.kernelp
= FALSE
;
1090 b
->lru_le
= listaddhead(block_lru
, b
);
1098 * Return true if the node exists.
1102 fcache_block_exists(FCacheEntry
*entry
, uint64_t offset
)
1104 struct block
*block
= block_get(entry
, offset
);
1112 * return a fd to the cache file of `entry' or -1 on failure
1116 fcache_open_block(FCacheEntry
*entry
, uint64_t offset
, Bool writep
)
1118 char fname
[MAXPATHLEN
];
1119 int flags
= O_BINARY
;
1122 if (use_o_largefile
)
1123 flags
|= O_LARGEFILE
;
1130 exists
= fcache_block_exists(entry
, offset
);
1133 int ret
= create_block(entry
, offset
);
1143 arla_warnx (ADEBWARN
, "Tried to read nonexistent block");
1151 if (fhopen_working
) {
1152 ret
= fcache_fhopen (&entry
->handle
, flags
);
1153 if (ret
< 0 && (errno
== EINVAL
|| errno
== EPERM
))
1160 fcache_block_name(entry
, offset
, fname
, sizeof(fname
));
1161 return open(fname
, flags
, 0600);
1165 * Create blocks for a new range without accounting, mark them busy.
1166 * Returns the number of blocks created (for future accounting).
1170 create_busy_blocks(FCacheEntry
*entry
, uint64_t offset
, uint64_t end
)
1172 char fname
[MAXPATHLEN
];
1178 AssertExclLocked(&entry
->lock
);
1180 for (off
= offset
; off
< end
; off
+= blocksize
) {
1181 fcache_block_name(entry
, off
, fname
, sizeof(fname
));
1182 fd
= open(fname
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600);
1185 arla_warnx(ADEBWARN
, "create_busy_blocks: "
1186 "creat failed at offset 0x%llx\n",
1187 (unsigned long long)off
);
1192 b
= block_add(entry
, off
);
1193 b
->lru_le
= listaddhead(block_lru
, b
);
1194 b
->flags
.busy
= TRUE
;
1202 * Clean up after create_busy_blocks(), removing any blocks beyond
1203 * what was actually received. Iff `usagep' is set, update cache
1208 delete_unbusy_blocks(FCacheEntry
*entry
, uint64_t offset
,
1209 uint64_t end
, uint64_t guessed_end
, Bool usagep
)
1213 fcache_data_setbusy(entry
, offset
, end
, FALSE
);
1215 for (off
= end
; off
< guessed_end
; off
+= blocksize
) {
1216 struct block
*b
= block_get(entry
, off
);
1217 setbusy_block(b
, FALSE
);
1218 throw_block(b
, usagep
);
1220 wake_waiters(entry
);
1224 * return a fd to the converted directory for `entry'
1228 fcache_open_extra_dir (FCacheEntry
*entry
, int flag
, mode_t mode
)
1230 char fname
[MAXPATHLEN
];
1232 assert (entry
->flags
.extradirp
&&
1233 entry
->status
.FileType
== TYPE_DIR
);
1235 fcache_extra_file_name (entry
, fname
, sizeof(fname
));
1236 return open (fname
, flag
| O_BINARY
, mode
);
1244 fcache_get_status_length(const AFSFetchStatus
*status
)
1246 return status
->Length
| ((uint64_t)status
->LengthHigh
<< 32);
1250 fcache_set_status_length(AFSFetchStatus
*status
, int64_t length
)
1252 status
->Length
= length
& 0xffffffff;
1253 status
->LengthHigh
= length
>> 32;
1257 * Discard a cached data block for `entry'.
1261 throw_block(struct block
*b
, Bool usagep
)
1263 FCacheEntry
*entry
= b
->node
;
1264 char fname
[MAXPATHLEN
];
1265 uint64_t offset
= b
->offset
;
1267 AssertExclLocked(&entry
->lock
);
1268 assert(!b
->flags
.busy
);
1270 if (b
->flags
.kernelp
)
1271 listdel(kernel_block_lru
, b
->lru_le
);
1273 listdel(block_lru
, b
->lru_le
);
1276 fcache_block_name(entry
, offset
, fname
, sizeof(fname
));
1284 (void)fcache_update_usage(entry
, -1);
1288 * Discard a cached data block for `entry' and update accounting.
1292 fcache_throw_block(struct block
*b
)
1294 throw_block(b
, TRUE
);
1298 * Discard the data cached for `entry'.
1302 throw_data (FCacheEntry
*entry
)
1306 AssertExclLocked(&entry
->lock
);
1307 assert (entry
->flags
.usedp
);
1308 assert(usedbytes
>= entry
->usage
);
1310 entry
->flags
.stale
= FALSE
;
1311 entry
->flags
.dirtied
= FALSE
;
1313 arla_log(ADEBVLOG
, "throw_data(%d, %u, %u, %u): usage %llu",
1314 entry
->fid
.Cell
, entry
->fid
.fid
.Volume
,
1315 entry
->fid
.fid
.Vnode
, entry
->fid
.fid
.Unique
,
1316 (unsigned long long)entry
->usage
);
1318 if (block_emptyp(entry
)) {
1319 assert(entry
->usage
== 0);
1323 ret
= abuf_purge(entry
);
1325 /* XXX set dirtied/stale again? */
1326 arla_warn (ADEBFCACHE
, ret
, "abuf_purge");
1330 assert(block_emptyp(entry
));
1332 if (entry
->flags
.extradirp
) {
1333 char fname
[MAXPATHLEN
];
1335 fcache_extra_file_name (entry
, fname
, sizeof(fname
));
1337 entry
->flags
.extradirp
= FALSE
;
1340 assert(entry
->usage
== 0);
1343 cm_check_consistency();
1347 * A probe function for a file server.
1351 fs_probe (struct rx_connection
*conn
)
1355 return RXAFS_GetTime (conn
, &sec
, &usec
);
1359 * Clean a locked node.
1361 * The caller must not touch the node afterwards.
1362 * This function may yield.
1366 throw_entry (FCacheEntry
*entry
)
1369 ConnCacheEntry
*conn
;
1374 assert (entry
->flags
.usedp
);
1375 assert (!entry
->flags
.kernelp
);
1377 AssertExclLocked(&entry
->lock
);
1378 assert(LockWaiters(&entry
->lock
) == 0);
1379 assert(entry
->refcount
== 0);
1381 hashtabdel (hashtab
, entry
);
1382 listdel(node_lru
, entry
->lru_le
);
1386 if (entry
->invalid_ptr
!= -1) {
1387 heap_remove (invalid_heap
, entry
->invalid_ptr
);
1388 entry
->invalid_ptr
= -1;
1391 fcache_poller_unref(entry
);
1393 if (entry
->flags
.attrp
&& !entry
->flags
.silly
&& entry
->host
) {
1394 ce
= cred_get (entry
->fid
.Cell
, 0, CRED_NONE
);
1395 assert (ce
!= NULL
);
1397 conn
= conn_get (entry
->fid
.Cell
, entry
->host
, afsport
,
1398 FS_SERVICE_ID
, fs_probe
, ce
);
1401 fids
.len
= cbs
.len
= 1;
1402 fids
.val
= &entry
->fid
.fid
;
1403 cbs
.val
= &entry
->callback
;
1405 if (conn_isalivep (conn
)) {
1406 ret
= RXAFS_GiveUpCallBacks(conn
->connection
, &fids
, &cbs
);
1407 if (host_downp(ret
)) {
1415 arla_warn (ADEBFCACHE
, ret
, "RXAFS_GiveUpCallBacks");
1417 if (entry
->volume
) {
1418 volcache_free (entry
->volume
);
1419 entry
->volume
= NULL
;
1421 assert_not_flag(entry
,kernelp
);
1422 entry
->flags
.attrp
= FALSE
;
1423 entry
->flags
.usedp
= FALSE
;
1424 entry
->lru_le
= listaddtail(free_nodes
, entry
);
1425 assert(entry
->lru_le
);
1427 fcache_unlock(entry
);
1428 LWP_NoYieldSignal(free_nodes
);
1432 * Return the next cache node number.
1436 next_cache_index (void)
1440 } while ((node_count
< maxrecovered
)
1441 && IS_RECOVERED(node_count
));
1447 * Pre-create cache nodes up to the limit highvnodes. If you want to
1448 * create more increase highnodes and signal create_nodes.
1452 create_nodes (char *arg
)
1454 FCacheEntry
*entries
;
1459 unsigned int n
, i
, j
;
1461 while (highvnodes
<= current_vnodes
)
1462 LWP_WaitProcess (create_nodes
);
1464 n
= highvnodes
- current_vnodes
;
1468 arla_warnx (ADEBFCACHE
,
1469 "pre-creating nodes");
1471 entries
= calloc (n
, sizeof(FCacheEntry
));
1472 if (n
!= 0 && entries
== NULL
)
1473 arla_errx (1, ADEBERROR
, "fcache: calloc failed");
1475 for (i
= 0; i
< n
; ++i
) {
1476 entries
[i
].invalid_ptr
= -1;
1477 entries
[i
].volume
= NULL
;
1478 entries
[i
].refcount
= 0;
1479 entries
[i
].anonaccess
= 0;
1480 entries
[i
].poll
= NULL
;
1481 for (j
= 0; j
< NACCESS
; j
++) {
1482 entries
[i
].acccache
[j
].cred
= ARLA_NO_AUTH_CRED
;
1483 entries
[i
].acccache
[j
].access
= 0;
1485 entries
[i
].usage
= 0;
1486 entries
[i
].blocks
= listnew();
1487 assert(entries
[i
].blocks
);
1489 Lock_Init(&entries
[i
].lock
);
1490 entries
[i
].index
= next_cache_index ();
1491 fcache_create_file (&entries
[i
], 1);
1499 entries
[i
].lru_le
= listaddhead(free_nodes
, &entries
[i
]);
1500 assert (entries
[i
].lru_le
);
1502 LWP_NoYieldSignal (free_nodes
);
1503 IOMGR_Select(0, NULL
, NULL
, NULL
, &tv
);
1506 arla_warnx (ADEBFCACHE
,
1507 "pre-created %u nodes", count
);
1512 * This is the almighty cleaner loop
1515 static Bool cleaner_working
= FALSE
;
1516 static Bool cleaner_force
= FALSE
;
1517 static int cleaner_npending
;
1520 * manage count of pending gc ops.
1524 fcache_cleaner_ref(void)
1530 fcache_cleaner_deref(void)
1533 assert(cleaner_npending
>= 0);
1534 if (!cleaner_npending
)
1535 LWP_NoYieldSignal(&cleaner_npending
);
1539 * make sure we wait until all ops triggered by the gc message have
1540 * been processed. putdata does take some time, should be batched.
1544 cleaner_gc_wait(void)
1547 while (cleaner_npending
)
1548 LWP_WaitProcess(&cleaner_npending
);
1552 * Try to trim nodes off the node LRU:s until we reach the low water
1557 cleaner_gc_nodes(void)
1559 struct nnpfs_message_gc msg
;
1560 Listitem
*item
, *prev
;
1564 int numnodes
= NNPFS_GC_MAX_HANDLE
;
1566 Bool found_kmarker
= FALSE
;
1568 kmarker
= listaddhead(kernel_node_lru
, CLEANER_MARKER
);
1572 * XXX it would be better if kernel handled this
1574 for (item
= listtail(kernel_node_lru
);
1575 item
&& item
!= kmarker
;
1577 prev
= listprev(kernel_node_lru
, item
);
1578 entry
= (FCacheEntry
*)listdata(item
);
1580 if (entry
->flags
.silly
&& unreferenced(entry
)) {
1581 memcpy(&msg
.handle
[cnt
].node
, &entry
->fid
,
1582 sizeof(msg
.handle
[0].node
));
1583 msg
.handle
[cnt
].offset
= NNPFS_NO_OFFSET
;
1586 if (cnt
>= numnodes
) {
1587 nnpfs_send_message_gc(kernel_fd
, &msg
, cnt
);
1594 nnpfs_send_message_gc(kernel_fd
, &msg
, cnt
);
1600 while (usedvnodes
> lowvnodes
) {
1601 Listitem
*nmarker
= listaddhead(node_lru
, CLEANER_MARKER
);
1604 while ((item
= listtail(node_lru
)) != NULL
1606 && usedvnodes
> lowvnodes
) {
1607 entry
= (FCacheEntry
*)listdata(item
);
1609 if (unreferenced(entry
)) {
1610 /* if (this_is_a_good_node_to_gc(entry,state)) */
1613 fcache_lock(entry
, FALSE
); /* should be clean */
1614 throw_entry(entry
); /* releases lock and sleeps */
1616 fcache_node_lru(entry
);
1619 listdel(node_lru
, nmarker
);
1624 for (item
= listtail(kernel_node_lru
);
1625 item
&& item
!= kmarker
&& usedvnodes
> lowvnodes
;
1627 prev
= listprev(kernel_node_lru
, item
);
1628 entry
= (FCacheEntry
*)listdata(item
);
1630 if (unreferenced(entry
)) {
1631 /* if (this_is_a_good_node_to_gc(entry,state)) */
1634 fcache_node_lru(entry
);
1636 memcpy(&msg
.handle
[cnt
].node
, &entry
->fid
,
1637 sizeof(msg
.handle
[0].node
));
1638 msg
.handle
[cnt
].offset
= NNPFS_NO_OFFSET
;
1641 if (cnt
>= numnodes
)
1647 nnpfs_send_message_gc(kernel_fd
, &msg
, cnt
);
1652 /* We've traversed the entire kernel node list, time to bail out. */
1653 if (item
== kmarker
)
1654 found_kmarker
= TRUE
;
1657 listdel(kernel_node_lru
, kmarker
);
1661 * Return true if this looks like a good block to gc.
1665 good_block_to_gc(struct block
*b
)
1667 FCacheEntry
*entry
= b
->node
;
1669 if (fcache_islocked(entry
))
1672 if (b
->flags
.busy
) {
1674 arla_warnx(ADEBWARN
, "busy block (%ld.%lu.%lu.%lu) @%llu",
1675 (long)entry
->fid
.Cell
,
1676 (unsigned long)entry
->fid
.fid
.Volume
,
1677 (unsigned long)entry
->fid
.fid
.Vnode
,
1678 (unsigned long)entry
->fid
.fid
.Unique
,
1679 (unsigned long long)b
->offset
);
1684 if (entry
->flags
.silly
&& entry
->flags
.kernelp
)
1687 if (entry
->status
.FileType
!= TYPE_DIR
)
1690 if (!entry
->flags
.datausedp
)
1700 * GC block if it's possible and looks like a good idea.
1702 * Return true if the block is gone.
1706 gc_block_maybe(struct block
*b
)
1708 if (good_block_to_gc(b
)) {
1709 FCacheEntry
*entry
= b
->node
;
1711 /* For directories, throw all or nothing */
1712 if (entry
->status
.FileType
== TYPE_DIR
) {
1713 if (unreferenced(entry
)) {
1714 fcache_lock(entry
, FALSE
); /* should be clean */
1716 fcache_unlock(entry
);
1719 Bool locked
= fcache_islocked(entry
);
1721 fcache_lock(entry
, TRUE
);
1723 throw_block(b
, TRUE
);
1726 fcache_unlock(entry
);
1734 * Return target value for usedbytes.
1738 target_usedbytes(void) {
1739 return min(lowbytes
, highbytes
- needbytes
- wantbytes
);
1743 * Get more space by evicting blocks off of the block LRU lists.
1745 * XXX don't loop forever
1749 cleaner_gc_blocks(void)
1751 struct nnpfs_message_gc msg
;
1752 struct block
*entry
;
1753 Listitem
*item
, *prev
;
1755 int64_t usedtarget
= target_usedbytes();
1757 int numblocks
= NNPFS_GC_MAX_HANDLE
;
1759 Bool found_marker
= FALSE
;
1761 marker
= listaddhead(kernel_block_lru
, CLEANER_MARKER
);
1764 while (usedbytes
> usedtarget
) {
1765 Listitem
*last
= NULL
;
1768 * First, release non-kernel blocks. No context switching in
1769 * this loop. Directory blocks may be thrown several at a
1770 * time, so we need to take extra care. Fortunately, blocks
1771 * we've already passed and not thrown can be trusted as long
1772 * as we avoid context switches.
1774 while (usedbytes
> usedtarget
) {
1776 item
= listprev(block_lru
, last
);
1778 item
= listtail(block_lru
);
1783 entry
= (struct block
*)listdata(item
);
1785 if (!gc_block_maybe(entry
))
1792 for (item
= listtail(kernel_block_lru
);
1793 item
&& item
!= marker
&& usedbytes
> usedtarget
;
1795 prev
= listprev(kernel_block_lru
, item
);
1796 entry
= (struct block
*)listdata(item
);
1798 if (good_block_to_gc(entry
)) {
1799 listdel(kernel_block_lru
, item
);
1800 entry
->lru_le
= listaddhead(kernel_block_lru
, entry
);
1802 memcpy(&msg
.handle
[cnt
].node
, &entry
->node
->fid
,
1803 sizeof(msg
.handle
[0].node
));
1806 * XXX overkill to gc entire node for dirs. invalidnode?
1808 if (entry
->node
->status
.FileType
== TYPE_DIR
)
1809 msg
.handle
[cnt
].offset
= NNPFS_NO_OFFSET
;
1811 msg
.handle
[cnt
].offset
= entry
->offset
;
1814 if (cnt
>= numblocks
)
1820 nnpfs_send_message_gc(kernel_fd
, &msg
, cnt
);
1822 usedtarget
= target_usedbytes();
1826 /* We've traversed the entire kernel node list, time to bail out. */
1828 found_marker
= TRUE
;
1831 listdel(kernel_block_lru
, marker
);
1839 arla_warnx (ADEBCLEANER
,
1841 "%lu (%lu-(%lu)-%lu) files, "
1842 "%lu (%lu-%lu) bytes "
1845 usedvnodes
, lowvnodes
, current_vnodes
, highvnodes
,
1846 (long)usedbytes
, (long)lowbytes
, (long)highbytes
,
1847 (long)needbytes
, (long)wantbytes
);
1848 cleaner_working
= TRUE
;
1850 for (i
= 0; i
< 3; i
++) {
1852 * Releasing nodes may give us data space as a side effect, so we
1853 * check the nodes first.
1855 cm_check_consistency();
1858 cleaner_force
= TRUE
;
1860 cleaner_force
= FALSE
;
1863 cm_check_consistency();
1864 cleaner_gc_blocks();
1866 if (target_usedbytes() >= usedbytes
)
1870 arla_warnx(ADEBCLEANER
,
1872 "%lu (%lu-(%lu)-%lu) files, "
1873 "%ld (%ld-%ld) bytes "
1876 usedvnodes
, lowvnodes
, current_vnodes
, highvnodes
,
1877 (long)usedbytes
, (long)lowbytes
, (long)highbytes
,
1878 (long)needbytes
, (long)wantbytes
);
1880 cm_check_consistency();
1881 if (needbytes
|| wantbytes
)
1882 LWP_NoYieldSignal(fcache_need_bytes
);
1883 cleaner_working
= FALSE
;
1884 IOMGR_Sleep (CLEANER_SLEEP
);
1889 fcache_wakeup_cleaner (void *wait
)
1891 worker_setdebuginfo("wake cleaner");
1892 if (cleaner_working
== FALSE
)
1893 IOMGR_Cancel (cleaner_pid
);
1894 worker_setdebuginfo("wait cleaner");
1895 LWP_WaitProcess (wait
);
1896 worker_setdebuginfo("waited cleaner");
1900 * Try to allocate 'wanted' bytes of space. May trigger GC.
1904 fcache_want_bytes(uint64_t wanted
)
1907 wantbytes
+= wanted
;
1909 assert(wantbytes
>= 0);
1911 if (usedbytes
+ needbytes
+ wantbytes
> highbytes
)
1912 fcache_wakeup_cleaner(fcache_need_bytes
);
1914 if (usedbytes
+ needbytes
+ wantbytes
> highbytes
)
1917 wantbytes
-= wanted
;
1922 * Reserve 'needed' bytes of space. May trigger GC.
1926 fcache_need_bytes(uint64_t needed
)
1929 needbytes
+= needed
;
1931 assert(needbytes
>= 0);
1933 if (usedbytes
+ needbytes
> highbytes
)
1934 fcache_wakeup_cleaner(fcache_need_bytes
);
1936 if (usedbytes
+ needbytes
> highbytes
) {
1937 arla_warnx(ADEBWARN
,
1938 "Out of space, couldn't get needed bytes after cleaner "
1939 "(%lu bytes missing, %lu used, %lu highbytes)",
1940 (long)(needbytes
- (highbytes
- usedbytes
)),
1941 (long)usedbytes
, (long)highbytes
);
1945 needbytes
-= needed
;
1951 * Run through the heap of objects to be invalidated and throw them away
1952 * when their expirationtime arrive.
1956 invalidator (char *arg
)
1962 arla_warnx(ADEBINVALIDATOR
,
1963 "running invalidator");
1965 while ((head
= heap_head (invalid_heap
)) == NULL
)
1966 LWP_WaitProcess (invalid_heap
);
1968 gettimeofday (&tv
, NULL
);
1970 while ((head
= heap_head (invalid_heap
)) != NULL
) {
1971 FCacheEntry
*entry
= (FCacheEntry
*)head
;
1973 if (tv
.tv_sec
< entry
->callback
.ExpirationTime
) {
1974 unsigned long t
= entry
->callback
.ExpirationTime
- tv
.tv_sec
;
1976 arla_warnx (ADEBINVALIDATOR
,
1977 "invalidator: sleeping for %lu second(s)", t
);
1982 fcache_lock(entry
, FALSE
);
1983 if (head
== heap_head (invalid_heap
)) {
1984 heap_remove_head (invalid_heap
);
1985 entry
->invalid_ptr
= -1;
1986 if (entry
->flags
.kernelp
)
1987 break_callback (entry
);
1988 fcache_poller_unref(entry
);
1990 fcache_unlock(entry
);
1996 * Add `entry' to the list of entries to invalidate when its time is
2001 add_to_invalidate (FCacheEntry
*e
)
2003 if (e
->invalid_ptr
!= -1)
2004 heap_remove (invalid_heap
, e
->invalid_ptr
);
2005 heap_insert (invalid_heap
, (const void *)e
, &e
->invalid_ptr
);
2006 LWP_NoYieldSignal (invalid_heap
);
2007 IOMGR_Cancel(invalidator_pid
);
2011 * Return a usable locked entry.
2012 * If there are no free entries, sleep until there is.
2015 static FCacheEntry
*
2016 find_free_entry (void)
2018 FCacheEntry
*entry
= NULL
;
2021 while ((item
= listtail(free_nodes
)) == NULL
) {
2022 arla_warnx (ADEBFCACHE
, "find_free_entry: sleeping");
2023 fcache_wakeup_cleaner(free_nodes
);
2026 /* Entries on the freelist should not be locked. */
2027 entry
= (FCacheEntry
*)listdata(item
);
2028 assert_not_flag(entry
,usedp
);
2029 fcache_lock(entry
, FALSE
); /* should be clean */
2030 listdel(free_nodes
, entry
->lru_le
);
2031 entry
->lru_le
= NULL
;
2042 struct fstore_context
{
2048 fcache_store_entry (struct fcache_store
*st
, void *ptr
)
2050 struct fstore_context
*c
;
2053 c
= (struct fstore_context
*)ptr
;
2054 if (c
->item
== NULL
) /* check if done ? */
2057 entry
= (FCacheEntry
*)listdata (c
->item
);
2058 c
->item
= listprev (node_lru
, c
->item
);
2060 if (!entry
->flags
.usedp
)
2063 strlcpy(st
->cell
, cell_num2name(entry
->fid
.Cell
), sizeof(st
->cell
));
2064 st
->fid
= entry
->fid
.fid
;
2065 st
->refcount
= entry
->refcount
;
2066 st
->length
= entry
->usage
;
2067 st
->fetched_length
= 0; /* XXX */
2068 st
->volsync
= entry
->volsync
;
2069 st
->status
= entry
->status
;
2070 st
->anonaccess
= entry
->anonaccess
;
2071 st
->index
= entry
->index
;
2072 st
->flags
.attrp
= entry
->flags
.attrp
;
2073 st
->flags
.datap
= entry
->usage
? TRUE
: FALSE
;
2074 st
->flags
.extradirp
= entry
->flags
.extradirp
;
2075 st
->flags
.mountp
= entry
->flags
.mountp
;
2076 st
->flags
.fake_mp
= entry
->flags
.fake_mp
;
2077 st
->flags
.vol_root
= entry
->flags
.vol_root
;
2078 strlcpy(st
->parentcell
, cell_num2name(entry
->parent
.Cell
),
2079 sizeof(st
->parentcell
));
2080 st
->parent
= entry
->parent
.fid
;
2081 st
->priority
= entry
->priority
;
2092 fcache_store_state (void)
2094 struct fstore_context c
;
2097 if (node_lru
== NULL
) {
2098 arla_warnx (ADEBFCACHE
, "store_state: node_lru is NULL\n");
2102 c
.item
= listtail(node_lru
);
2105 ret
= state_store_fcache("fcache", fcache_store_entry
, &c
);
2107 arla_warn(ADEBWARN
, ret
, "failed to write fcache state");
2109 arla_warnx (ADEBFCACHE
, "wrote %u entries to fcache", c
.n
);
2119 fcache_recover_entry (struct fcache_store
*st
, void *ptr
)
2121 unsigned *n
= (unsigned *)ptr
;
2130 cellid
= cell_name2num(st
->cell
);
2131 assert (cellid
!= -1);
2133 ce
= cred_get (cellid
, 0, 0);
2134 assert (ce
!= NULL
);
2136 res
= volcache_getbyid (st
->fid
.Volume
, cellid
, ce
, &vol
, NULL
);
2142 e
= calloc(1, sizeof(FCacheEntry
));
2143 e
->invalid_ptr
= -1;
2144 Lock_Init(&e
->lock
);
2145 fcache_lock(e
, FALSE
);
2148 e
->fid
.Cell
= cellid
;
2149 e
->fid
.fid
= st
->fid
;
2151 e
->status
= st
->status
;
2152 e
->usage
= st
->length
;
2153 /* e->fetched_length = st->fetched_length; */
2154 e
->callback
= broken_callback
;
2155 e
->volsync
= st
->volsync
;
2156 e
->refcount
= st
->refcount
;
2158 /* Better not restore the rights. pags don't have to be the same */
2159 for (i
= 0; i
< NACCESS
; ++i
) {
2160 e
->acccache
[i
].cred
= ARLA_NO_AUTH_CRED
;
2161 e
->acccache
[i
].access
= ANONE
;
2164 e
->anonaccess
= st
->anonaccess
;
2165 e
->index
= st
->index
;
2166 fcache_create_file(e
, 0);
2167 set_recovered(e
->index
);
2168 e
->flags
.usedp
= TRUE
;
2169 e
->flags
.attrp
= st
->flags
.attrp
;
2170 /* st->flags.datap */
2171 e
->flags
.attrusedp
= FALSE
;
2172 e
->flags
.datausedp
= FALSE
;
2173 e
->flags
.kernelp
= FALSE
;
2174 e
->flags
.extradirp
= st
->flags
.extradirp
;
2175 e
->flags
.mountp
= st
->flags
.mountp
;
2176 e
->flags
.fake_mp
= st
->flags
.fake_mp
;
2177 e
->flags
.vol_root
= st
->flags
.vol_root
;
2178 e
->flags
.sentenced
= FALSE
;
2179 e
->flags
.stale
= FALSE
;
2180 e
->flags
.dirtied
= FALSE
;
2181 e
->flags
.silly
= FALSE
;
2182 e
->flags
.waiters
= FALSE
;
2183 e
->flags
.gcp
= FALSE
;
2184 e
->flags
.appended
= FALSE
;
2186 e
->parent
.Cell
= cell_name2num(st
->parentcell
);
2187 assert(e
->parent
.Cell
!= -1);
2188 e
->parent
.fid
= st
->parent
;
2189 e
->priority
= st
->priority
;
2191 e
->lru_le
= listaddhead (node_lru
, e
);
2194 hashtabadd (hashtab
, e
);
2196 usedbytes
+= e
->usage
;
2209 fcache_recover_state (void)
2214 state_recover_fcache("fcache", fcache_recover_entry
, &n
);
2216 arla_warnx (ADEBFCACHE
, "recovered %u entries to fcache", n
);
2221 * Search for `cred' in `ae' and return a pointer in `pos'. If it
2222 * already exists return TRUE, else return FALSE and set pos to a
2227 findaccess (nnpfs_pag_t cred
, AccessEntry
*ae
, AccessEntry
**pos
)
2231 for(i
= 0; i
< NACCESS
; ++i
)
2232 if(ae
[i
].cred
== cred
) {
2237 i
= rand() % NACCESS
;
2248 fs_rtt_cmp (const void *v1
, const void *v2
)
2250 struct fs_server_entry
*e1
= (struct fs_server_entry
*)v1
;
2251 struct fs_server_entry
*e2
= (struct fs_server_entry
*)v2
;
2253 return conn_rtt_cmp(&e1
->conn
, &e2
->conn
);
2257 * Initialize a `fs_server_context'.
2261 init_fs_server_context (fs_server_context
*context
)
2263 context
->num_conns
= 0;
2267 find_partition (fs_server_context
*context
)
2269 int i
= context
->conns
[context
->i
- 1].ve_ent
;
2271 if (i
< 0 || i
>= context
->ve
->entry
.nServers
)
2273 return context
->ve
->entry
.serverPartition
[i
];
2277 * Find the next fileserver for the request in `context'.
2278 * Returns a ConnCacheEntry or NULL.
2282 find_next_fs (fs_server_context
*context
,
2283 ConnCacheEntry
*prev_conn
,
2287 if (host_downp(error
))
2288 conn_dead (prev_conn
);
2289 if (volume_downp(error
))
2290 volcache_mark_down (context
->ve
,
2291 context
->conns
[context
->i
- 1].ve_ent
,
2293 } else if (prev_conn
) {
2294 assert(prev_conn
== context
->conns
[context
->i
- 1].conn
);
2295 volcache_reliable_el(context
->ve
, context
->conns
[context
->i
- 1].ve_ent
);
2298 if (context
->i
< context
->num_conns
)
2299 return context
->conns
[context
->i
++].conn
;
2305 * Clean up a `fs_server_context'
2309 free_fs_server_context (fs_server_context
*context
)
2313 for (i
= 0; i
< context
->num_conns
; ++i
)
2314 conn_free (context
->conns
[i
].conn
);
2317 volcache_process_marks(context
->ve
);
2321 * Find the the file servers housing the volume for `e' and store it
2326 init_fs_context (FCacheEntry
*e
,
2328 fs_server_context
*context
)
2330 VolCacheEntry
*ve
= e
->volume
;
2334 int cell
= e
->fid
.Cell
;
2337 memset(context
, 0, sizeof(*context
));
2340 ret
= volcache_getbyid (e
->fid
.fid
.Volume
, e
->fid
.Cell
,
2341 ce
, &e
->volume
, NULL
);
2347 ret
= volume_make_uptodate (ve
, ce
);
2351 bit
= volcache_volid2bit (ve
, e
->fid
.fid
.Volume
);
2354 /* the volume entry is inconsistent. */
2355 volcache_invalidate_ve (ve
);
2360 for (i
= 0; i
< min(NMAXNSERVERS
,ve
->entry
.nServers
); ++i
) {
2361 u_long addr
= htonl(ve
->entry
.serverNumber
[i
]);
2363 if (ve
->entry
.serverFlags
[i
] & bit
2365 && (ve
->entry
.serverFlags
[i
] & VLSF_DONTUSE
) == 0) {
2366 ConnCacheEntry
*conn
;
2368 conn
= conn_get (cell
, addr
, afsport
,
2369 FS_SERVICE_ID
, fs_probe
, ce
);
2370 if (!conn_isalivep (conn
))
2371 conn
->rtt
= INT_MAX
/2 ;
2372 else if (!volcache_reliablep_el(ve
, i
))
2373 conn
->rtt
= INT_MAX
/4;
2375 conn
->rtt
= rx_PeerOf(conn
->connection
)->srtt
2376 + rand() % RTT_FUZZ
- RTT_FUZZ
/ 2;
2377 context
->conns
[num_clones
].conn
= conn
;
2378 context
->conns
[num_clones
].ve_ent
= i
;
2383 if (num_clones
== 0)
2388 qsort (context
->conns
, num_clones
, sizeof(*context
->conns
),
2391 context
->num_conns
= num_clones
;
2398 * Find the first file server housing the volume for `e'.
2402 find_first_fs (fs_server_context
*context
)
2404 return find_next_fs (context
, NULL
, 0);
2408 * Initialize the file cache in `cachedir', with these values for high
2409 * and low-water marks.
2413 fcache_init (u_long alowvnodes
,
2417 uint64_t ablocksize
,
2421 * Initialize all variables.
2428 fhopen_working
= k_hasafs ();
2434 collectstats_init ();
2437 lowvnodes
= alowvnodes
;
2438 highvnodes
= ahighvnodes
;
2439 lowbytes
= alowbytes
;
2440 highbytes
= ahighbytes
;
2441 highbytes
= ahighbytes
;
2442 blocksize
= ablocksize
;
2444 if (cache_dir
== NULL
)
2445 cache_dir
= getcwd(NULL
, 0);
2447 if (cache_dir
== NULL
)
2448 arla_errx (1, ADEBERROR
, "fcache: getcwd failed");
2450 hashtab
= hashtabnew (FCHASHSIZE
, fcachecmp
, fcachehash
);
2451 if (hashtab
== NULL
)
2452 arla_errx (1, ADEBERROR
, "fcache: hashtabnew failed");
2454 kernel_node_lru
= listnew();
2455 node_lru
= listnew();
2456 free_nodes
= listnew();
2457 kernel_block_lru
= listnew();
2458 block_lru
= listnew();
2459 if (kernel_node_lru
== NULL
2461 || free_nodes
== NULL
2462 || kernel_block_lru
== NULL
2463 || block_lru
== NULL
)
2464 arla_errx (1, ADEBERROR
, "fcache: listnew failed");
2466 invalid_heap
= heap_new (ahighvnodes
, expiration_time_cmp
);
2467 if (invalid_heap
== NULL
)
2468 arla_errx (1, ADEBERROR
, "fcache: heap_new failed");
2470 num_locks
= num_workers
+ 8; /* a few for other threads */
2471 lockwaiters
= malloc(num_locks
* sizeof(*lockwaiters
));
2472 if (lockwaiters
== NULL
)
2473 arla_errx(1, ADEBERROR
, "fcache: malloc failed");
2475 for (i
= 0; i
< num_locks
; i
++)
2476 NNPQUEUE_INIT(&lockwaiters
[i
]);
2479 fcache_recover_state ();
2481 if (LWP_CreateProcess (create_nodes
, 0, 1, NULL
, "fcache-create-nodes",
2483 arla_errx (1, ADEBERROR
,
2484 "fcache: cannot create create-nodes thread");
2486 if (LWP_CreateProcess (cleaner
, 0, 1, NULL
, "fcache-cleaner",
2488 arla_errx (1, ADEBERROR
,
2489 "fcache: cannot create cleaner thread");
2491 if (LWP_CreateProcess (invalidator
, 0, 1, NULL
, "fcache-invalidator",
2493 arla_errx (1, ADEBERROR
,
2494 "fcache: cannot create invalidator thread");
2498 * set new values for those of lowvnodes, highvnodes that are not zero.
2499 * do some sanity checks
2500 * return 0 or an error code
2504 fcache_setvnodes(u_long alowvnodes
,
2507 int64_t high
= highvnodes
;
2508 int64_t low
= lowvnodes
;
2510 arla_warnx (ADEBFCACHE
, "fcache_setvnodes");
2512 if (ahighvnodes
!= 0)
2515 if (alowvnodes
!= 0)
2521 if (high
> highvnodes
)
2522 LWP_NoYieldSignal (create_nodes
);
2531 * set new values for those of lowvnodes, highvnodes that are not zero.
2532 * do some sanity checks
2533 * return 0 or an error code
2537 fcache_setbytes(int64_t alowbytes
,
2540 int64_t high
= highbytes
;
2541 int64_t low
= lowbytes
;
2544 arla_warnx (ADEBFCACHE
, "fcache_setbytes");
2549 if (ahighbytes
!= 0)
2558 quotadiff
= fcache_set_appendquota();
2559 return install_appendquota(quotadiff
);
2563 * set new high/low values for vnodes and bytes.
2564 * return 0 or an error code
2568 fcache_reinit(u_long alowvnodes
,
2573 int error
= fcache_setvnodes(alowvnodes
, ahighvnodes
);
2577 return fcache_setbytes(alowbytes
, ahighbytes
);
2581 * Node has been touched, move to head of LRU list.
2585 fcache_node_lru(FCacheEntry
*e
)
2587 if (e
->flags
.kernelp
) {
2588 listdel(kernel_node_lru
, e
->lru_le
);
2589 e
->lru_le
= listaddhead(kernel_node_lru
, e
);
2591 listdel(node_lru
, e
->lru_le
);
2592 e
->lru_le
= listaddhead(node_lru
, e
);
2599 * Block has been touched, move it to head of LRU list.
2603 fcache_block_lru(struct block
*b
)
2605 if (b
->flags
.kernelp
) {
2606 listdel(kernel_block_lru
, b
->lru_le
);
2607 b
->lru_le
= listaddhead(kernel_block_lru
, b
);
2609 listdel(block_lru
, b
->lru_le
);
2610 b
->lru_le
= listaddhead(block_lru
, b
);
2615 /* fcache_node_lru(b->node); XXX hopefully happens anyway */
2619 data_unkernel_callback(struct block
*block
, void *data
)
2621 if (!block
->flags
.kernelp
)
2624 block
->flags
.kernelp
= FALSE
;
2625 listdel(kernel_block_lru
, block
->lru_le
);
2626 block
->lru_le
= listaddhead(block_lru
, block
);
2628 assert(block
->lru_le
);
2632 * Node's kernelp is to be set to indicated value.
2634 * XXX kernelp implies things about attrused, dataused, etc which we
2635 * should take care of here, too... but what?
2639 fcache_node_setkernelp(FCacheEntry
*e
, Bool val
)
2641 assert(val
== TRUE
|| val
== FALSE
);
2643 if (e
->flags
.kernelp
== val
)
2647 block_foreach(e
, data_unkernel_callback
, NULL
);
2649 e
->flags
.kernelp
= val
;
2652 listdel(node_lru
, e
->lru_le
);
2653 e
->lru_le
= listaddhead(kernel_node_lru
, e
);
2655 listdel(kernel_node_lru
, e
->lru_le
);
2656 e
->lru_le
= listaddhead(node_lru
, e
);
2663 * Set a block's busy flag to `val'.
2667 setbusy_block(struct block
*b
, Bool val
)
2669 assert(b
->flags
.busy
!= val
);
2670 b
->flags
.busy
= val
;
2674 * Set busy flag to `val' for a range of blocks, wake any waiters.
2678 fcache_data_setbusy(FCacheEntry
*e
, uint64_t offset
, uint64_t end
,
2683 assert(block_offset(offset
) == offset
);
2685 for (off
= offset
; off
< end
; off
+= blocksize
) {
2686 struct block
*b
= block_get(e
, off
);
2687 setbusy_block(b
, val
);
2693 * Block's kernelp is to be set to indicated value.
2697 fcache_data_setkernelp(FCacheEntry
*e
, uint64_t offset
, Bool val
, Bool unbusy
)
2701 assert(block_offset(offset
) == offset
);
2703 AssertExclLocked(&e
->lock
);
2705 b
= block_get(e
, offset
);
2713 setbusy_block(b
, FALSE
);
2717 if (b
->flags
.kernelp
== val
)
2720 b
->flags
.kernelp
= val
;
2723 listdel(block_lru
, b
->lru_le
);
2724 b
->lru_le
= listaddhead(kernel_block_lru
, b
);
2726 listdel(kernel_block_lru
, b
->lru_le
);
2727 b
->lru_le
= listaddhead(block_lru
, b
);
2734 * Find the entry for `fid' in the hash table.
2735 * If it's found, move it to the front of `node_lru' as well.
2738 static FCacheEntry
*
2739 find_entry_nolock (VenusFid fid
)
2744 if (hashtab
== NULL
)
2748 e
= (FCacheEntry
*)hashtabsearch (hashtab
, (void *)&key
);
2756 * Mark `e' as having `callback' and notify the kernel.
2757 * This might be overly harsh to opened files.
2761 stale (FCacheEntry
*e
, AFSCallBack callback
)
2763 if (callback
.CallBackType
== CBDROPPED
&&
2764 e
->callback
.CallBackType
== CBDROPPED
)
2767 if (fcache_islocked(e
) || e
->refcount
> 0)
2768 e
->flags
.sentenced
= TRUE
;
2770 fcache_lock(e
, FALSE
);
2771 fcache_poller_unref(e
);
2772 e
->callback
= callback
;
2774 if (e
->flags
.kernelp
)
2779 if (e
->status
.FileType
== TYPE_DIR
)
2787 AFSCallBack callback
;
2791 * Iterate over all entries until we find an entry that matches in
2792 * only fid (without cell) and stale it.
2796 stale_unknown_cell (void *ptr
, void *arg
)
2798 FCacheEntry
*e
= (FCacheEntry
*)ptr
;
2799 struct stale_arg
*sa
= (struct stale_arg
*)arg
;
2801 if (e
->fid
.fid
.Volume
== sa
->fid
.fid
.Volume
2802 && e
->fid
.fid
.Vnode
== sa
->fid
.fid
.Vnode
2803 && e
->fid
.fid
.Unique
== sa
->fid
.fid
.Unique
)
2804 stale (e
, sa
->callback
);
2810 * Call stale on the entry corresponding to `fid', if any.
2814 fcache_stale_entry (VenusFid fid
, AFSCallBack callback
)
2818 if (fid
.Cell
== -1) {
2819 struct stale_arg arg
;
2822 arg
.callback
= callback
;
2824 hashtabforeach (hashtab
, stale_unknown_cell
, &arg
);
2828 e
= find_entry_nolock (fid
);
2830 arla_warnx (ADEBFCACHE
,
2831 "callback for non-existing file (%d, %u, %u, %u)",
2832 fid
.Cell
, fid
.fid
.Volume
, fid
.fid
.Vnode
, fid
.fid
.Unique
);
2835 stale (e
, callback
);
2844 * If ptr has cred arg, set it invalid
2848 purge_cred (void *ptr
, void *arg
)
2850 FCacheEntry
*e
= (FCacheEntry
*)ptr
;
2851 fc_purgecred
*cred
= (fc_purgecred
*) arg
;
2852 AccessEntry
*ae
= e
->acccache
;
2855 if (e
->fid
.Cell
== cred
->cell
|| cred
->cell
== -1) {
2857 for(i
= 0; i
< NACCESS
; ++i
)
2858 if(ae
[i
].cred
== cred
->pag
) {
2859 ae
[i
].cred
= ARLA_NO_AUTH_CRED
;
2860 ae
[i
].access
= ANONE
;
2861 if (e
->flags
.kernelp
)
2862 install_attr (e
, FCACHE2NNPFSNODE_NO_LENGTH
);
2871 * Mark cred as stale in kernel and all fcache-entries,
2872 * When cell == -1, flush all creds in this pag.
2876 fcache_purge_cred (nnpfs_pag_t pag
, int32_t cell
)
2883 hashtabforeach(hashtab
, purge_cred
, &cred
);
2887 * If ptr was retrieved from cell - volume , try to mark stale
2891 purge_volume (void *ptr
, void *arg
)
2893 FCacheEntry
*e
= (FCacheEntry
*)ptr
;
2894 VenusFid
*fid
= (VenusFid
*) arg
;
2896 if ((e
->fid
.Cell
== fid
->Cell
|| fid
->Cell
== -1)
2897 && e
->fid
.fid
.Volume
== fid
->fid
.Volume
) {
2898 stale (e
, broken_callback
);
2904 * Mark all entries from cell.volume as stale
2908 fcache_purge_volume (VenusFid fid
)
2910 hashtabforeach(hashtab
, purge_volume
, &fid
);
2914 * If `ptr' was retrieved from `host', mark it as stale.
2918 purge_host (void *ptr
, void *arg
)
2920 FCacheEntry
*e
= (FCacheEntry
*)ptr
;
2921 u_long
*host
= (u_long
*)arg
;
2924 if (e
->host
== *host
)
2925 stale (e
, broken_callback
);
2930 * Mark all entries from the host `host' as stale.
2934 fcache_purge_host (u_long host
)
2936 hashtabforeach (hashtab
, purge_host
, &host
);
2941 * If `ptr' is a mountpoint, mark it as stale.
2945 invalidate_mp (void *ptr
, void *arg
)
2947 FCacheEntry
*e
= (FCacheEntry
*)ptr
;
2948 if (e
->flags
.mountp
)
2949 stale (e
, broken_callback
);
2954 * Invalidate all mountpoints to force them to be reread.
2958 fcache_invalidate_mp (void)
2960 hashtabforeach (hashtab
, invalidate_mp
, NULL
);
2964 * Mark `entry' as not being used.
2968 fcache_unused (FCacheEntry
*entry
)
2970 AssertExclLocked(&entry
->lock
);
2972 assert(!entry
->flags
.appended
|| entry
->flags
.dirtied
);
2973 assert(!entry
->flags
.kernelp
);
2975 entry
->flags
.datausedp
= entry
->flags
.attrusedp
= FALSE
;
2976 entry
->tokens
&= ~NNPFS_DATA_MASK
;
2979 * we don't signal free_nodes here since we never
2980 * free the node (usedvnode--);
2983 /* throw stale and deleted-and-unreachable data */
2984 if (entry
->flags
.stale
|| entry
->flags
.dirtied
|| entry
->flags
.silly
) {
2985 throw_data(entry
); /* XXX overkill for volume callbacks etc */
2987 /* make sure we get fresh attrs. unnecessary for "stale" entries? */
2988 entry
->flags
.attrp
= FALSE
;
2993 * make up some status that might be valid for a mount-point
2997 fake_mp_status (FCacheEntry
*e
)
2999 AFSFetchStatus
*status
= &e
->status
;
3001 status
->FileType
= TYPE_DIR
;
3002 status
->LinkCount
= 100;
3003 status
->UnixModeBits
= 0777;
3004 status
->ClientModTime
= 0;
3005 status
->ServerModTime
= 0;
3011 * Return true if `entry' is a mountpoint
3015 mountpointp (FCacheEntry
*entry
)
3017 if (entry
->status
.FileType
== TYPE_LINK
3018 && fcache_get_status_length(&entry
->status
) != 0
3019 && entry
->status
.UnixModeBits
== 0644)
3025 * Mark `entry' as mountpoint or a fake mountpoint depending on
3026 * fake_mp is used or not.
3030 fcache_mark_as_mountpoint (FCacheEntry
*entry
)
3033 entry
->flags
.fake_mp
= TRUE
;
3034 fake_mp_status (entry
);
3036 entry
->flags
.mountp
= TRUE
;
3041 * Update all the relevant parts of `entry' after having received new
3042 * data from the file server.
3046 update_entry (FCacheEntry
*entry
,
3047 AFSFetchStatus
*status
,
3048 AFSCallBack
*callback
,
3049 AFSVolSync
*volsync
,
3050 ConnCacheEntry
*conn
,
3055 unsigned long bitmask
= 0141777; /* REG, DIR, STICKY, USR, GRP, OTH */
3057 if (entry
->volume
&& cell_issuid_by_num (entry
->volume
->cell
))
3058 bitmask
|= 0006000; /* SUID, SGID */
3060 gettimeofday (&tv
, NULL
);
3062 entry
->status
= *status
;
3063 entry
->status
.UnixModeBits
&= bitmask
;
3065 entry
->callback
= *callback
;
3066 entry
->callback
.ExpirationTime
+= tv
.tv_sec
;
3067 add_to_invalidate (entry
);
3070 entry
->volsync
= *volsync
;
3072 volcache_update_volsync (entry
->volume
, *volsync
);
3076 fcache_poller_reref(entry
, conn
);
3077 entry
->host
= rx_HostOf(rx_PeerOf(conn
->connection
));
3079 fcache_poller_unref(entry
);
3083 entry
->anonaccess
= status
->AnonymousAccess
;
3084 findaccess (cred
, entry
->acccache
, &ae
);
3086 ae
->access
= status
->CallerAccess
;
3087 if (!entry
->flags
.mountp
&& mountpointp (entry
))
3088 fcache_mark_as_mountpoint (entry
);
3092 * Update entry, common code for do_read_attr and get_attr_bulk
3096 update_attr_entry (FCacheEntry
*entry
,
3097 AFSFetchStatus
*status
,
3098 AFSCallBack
*callback
,
3099 AFSVolSync
*volsync
,
3100 ConnCacheEntry
*conn
,
3103 if (block_any(entry
) != BLOCK_NONE
3104 && entry
->status
.DataVersion
!= status
->DataVersion
) {
3106 if (entry
->flags
.datausedp
) {
3107 /* we need to mark entry as stale, or we won't be able to
3108 * detect that once DataVersion has been updated
3111 /* actually, what we do need is to get rid of old
3112 * data. there are two ways to do that. for us to get rid
3113 * of all data we need kernel to drop the node. the other
3114 * way is to send gc messages until the all data is out of
3115 * kernel, but that's probably tricky to get right.
3118 entry
->flags
.stale
= TRUE
;
3119 arla_log(ADEBVLOG
, "update_attr_entry(%d, %u, %u, %u): usage %llu",
3120 entry
->fid
.Cell
, entry
->fid
.fid
.Volume
,
3121 entry
->fid
.fid
.Vnode
, entry
->fid
.fid
.Unique
,
3122 (unsigned long long)entry
->usage
);
3123 stale(entry
, broken_callback
);
3125 entry
->tokens
&= ~NNPFS_DATA_MASK
;
3130 update_entry (entry
, status
, callback
, volsync
,
3133 entry
->tokens
|= NNPFS_ATTR_R
;
3134 entry
->flags
.attrp
= TRUE
;
3138 * We're about to update entry, if we have valid data we'll update
3139 * length ourselves when modifying the local cache. If data is stale,
3140 * throw it so we can get fresh.
3143 * 1. Following code to do the actual update if all is well
3144 * 2. Following code to get new data if we have stale data
3145 * (maybe we should do that here)
3149 update_modify_dir(FCacheEntry
*entry
,
3150 AFSFetchStatus
*status
,
3151 AFSCallBack
*callback
,
3152 AFSVolSync
*volsync
,
3153 ConnCacheEntry
*conn
,
3156 entry
->status
.DataVersion
++;
3158 if (block_any(entry
) != BLOCK_NONE
3159 && entry
->status
.DataVersion
!= status
->DataVersion
)
3161 arla_warnx(ADEBWARN
, "DataVersion mismatch, refreshing directory");
3163 throw_data(entry
); /* XXX installed in kernel, but should be locked */
3164 entry
->tokens
&= ~NNPFS_DATA_MASK
;
3166 /* XXX entry->flags.datausedp = FALSE; perhaps? */
3168 /* keep server's length so we know how much data to fetch */
3170 uint64_t len
= fcache_get_status_length(&entry
->status
);
3171 fcache_set_status_length(status
, len
);
3174 update_entry(entry
, status
, callback
, volsync
, conn
, cred
);
3179 * Give up all callbacks.
3183 giveup_all_callbacks (uint32_t cell
, uint32_t host
, void *arg
)
3186 ConnCacheEntry
*conn
;
3189 ce
= cred_get (cell
, 0, CRED_ANY
);
3190 assert (ce
!= NULL
);
3192 conn
= conn_get (cell
, host
, afsport
, FS_SERVICE_ID
, fs_probe
, ce
);
3195 if (conn_isalivep (conn
)) {
3197 ret
= RXAFS_GiveUpAllCallBacks(conn
->connection
);
3198 if (ret
!= 0 && ret
!= RXGEN_OPCODE
) {
3199 struct in_addr in_addr
;
3201 in_addr
.s_addr
= rx_HostOf(rx_PeerOf(conn
->connection
));
3202 arla_warn (ADEBWARN
, ret
, "GiveUpAllCallBacks %s",
3203 inet_ntoa (in_addr
));
3204 if (host_downp(ret
)) {
3217 fcache_giveup_all_callbacks (void)
3221 poller_foreach(giveup_all_callbacks
, NULL
);
3223 for (item
= listtail(node_lru
);
3225 item
= listprev(node_lru
, item
)) {
3226 FCacheEntry
*entry
= (FCacheEntry
*)listdata(item
);
3228 if (entry
->flags
.attrp
&&
3229 entry
->flags
.silly
== FALSE
&&
3233 ConnCacheEntry
*conn
;
3238 ce
= cred_get (entry
->fid
.Cell
, 0, CRED_ANY
);
3239 assert (ce
!= NULL
);
3241 conn
= conn_get (entry
->fid
.Cell
, entry
->host
, afsport
,
3242 FS_SERVICE_ID
, fs_probe
, ce
);
3245 fids
.len
= cbs
.len
= 1;
3246 fids
.val
= &entry
->fid
.fid
;
3247 cbs
.val
= &entry
->callback
;
3249 if (conn_isalivep (conn
)) {
3250 ret
= RXAFS_GiveUpCallBacks (conn
->connection
, &fids
, &cbs
);
3252 struct in_addr in_addr
;
3254 in_addr
.s_addr
= rx_HostOf(rx_PeerOf(conn
->connection
));
3255 arla_warn (ADEBFCACHE
, ret
, "RXAFS_GiveUpCallBacks %s",
3256 inet_ntoa (in_addr
));
3266 * discard all cached attrs to force revalidation of entries
3267 * intended for reconnect after disconnected mode.
3271 fcache_discard_attrs(void)
3275 for (item
= listtail(node_lru
);
3277 item
= listprev(node_lru
, item
)) {
3278 FCacheEntry
*entry
= (FCacheEntry
*)listdata(item
);
3280 if (entry
->flags
.attrp
&&
3281 entry
->flags
.silly
== FALSE
)
3282 entry
->flags
.attrp
= FALSE
;
3287 * Obtain new callbacks for all entries in the cache.
3291 fcache_reobtain_callbacks (struct nnpfs_cred
*cred
)
3296 for (item
= listtail(node_lru
);
3298 item
= listprev(node_lru
, item
)) {
3299 FCacheEntry
*entry
= (FCacheEntry
*)listdata(item
);
3301 fcache_lock(entry
, FALSE
);
3302 if (entry
->flags
.usedp
&&
3303 entry
->flags
.silly
== FALSE
&&
3307 ConnCacheEntry
*conn
;
3308 AFSFetchStatus status
;
3309 AFSCallBack callback
;
3313 ce
= cred_get (entry
->fid
.Cell
, cred
->pag
, CRED_ANY
);
3314 assert (ce
!= NULL
);
3316 conn
= conn_get (entry
->fid
.Cell
, entry
->host
, afsport
,
3317 FS_SERVICE_ID
, fs_probe
, ce
);
3318 if (!conn_isalivep(conn
))
3321 * does this belong here?
3324 ret
= volcache_getbyid (entry
->fid
.fid
.Volume
,
3325 entry
->fid
.Cell
, ce
, &vol
, NULL
);
3327 entry
->volume
= vol
;
3329 ret
= RXAFS_FetchStatus (conn
->connection
,
3335 arla_warn (ADEBFCACHE
, ret
, "RXAFS_FetchStatus");
3337 update_attr_entry (entry
, &status
, &callback
, &volsync
,
3339 if (entry
->flags
.kernelp
)
3340 break_callback (entry
);
3342 fcache_counter
.fetch_attr
++;
3348 fcache_unlock(entry
);
3354 * Return true iff there's any point in trying the next fs.
3358 try_next_fs (int error
, const VenusFid
*fid
)
3362 case RXKADUNKNOWNKEY
:
3364 case ARLA_CALL_DEAD
:
3365 case ARLA_INVALID_OPERATION
:
3366 case ARLA_CALL_TIMEOUT
:
3368 case ARLA_PROTOCOL_ERROR
:
3369 case ARLA_USER_ABORT
:
3370 case ARLA_ADDRINUSE
:
3372 case ARLA_VSALVAGE
:
3373 case ARLA_VNOSERVICE
:
3374 case ARLA_VOFFLINE
:
3380 if (fid
&& !volcache_reliablep (fid
->fid
.Volume
, fid
->Cell
))
3381 volcache_invalidate (fid
->fid
.Volume
, fid
->Cell
);
3391 * Fetch the attributes for the file in `entry' from the file_server,
3392 * using the credentials in `ce' and returning the connection in
3395 * `entry' must be write-locked.
3397 * If an error code is returned `fs_server_context' is already freed.
3398 * If everything is ok, `fs_server_context' must be freed by the caller.
3402 do_read_attr (FCacheEntry
*entry
,
3404 ConnCacheEntry
**ret_conn
,
3405 fs_server_context
*ret_context
)
3407 ConnCacheEntry
*conn
;
3408 AFSFetchStatus status
;
3409 AFSCallBack callback
;
3411 struct collect_stat collectstat
;
3414 AssertExclLocked(&entry
->lock
);
3418 ret
= init_fs_context(entry
, ce
, ret_context
);
3422 for (conn
= find_first_fs (ret_context
);
3424 conn
= find_next_fs (ret_context
, conn
, ret
)) {
3426 collectstats_start(&collectstat
);
3427 ret
= RXAFS_FetchStatus (conn
->connection
,
3432 collectstats_stop(&collectstat
, entry
, conn
,
3433 find_partition(ret_context
),
3434 arla_STATISTICS_REQTYPE_FETCHSTATUS
, 1);
3435 arla_warnx (ADEBFCACHE
, "trying to fetch status: %d", ret
);
3436 if (!try_next_fs (ret
, &entry
->fid
))
3440 arla_warn (ADEBFCACHE
, ret
, "fetch-status");
3441 if (host_downp(ret
))
3443 free_fs_server_context (ret_context
);
3447 fcache_counter
.fetch_attr
++;
3449 update_attr_entry (entry
, &status
, &callback
, &volsync
,
3452 AssertExclLocked(&entry
->lock
);
3460 * Read the attributes of `entry' from the file server and store them.
3461 * `e' must be write-locked.
3465 read_attr (FCacheEntry
*entry
, CredCacheEntry
*ce
)
3468 ConnCacheEntry
*conn
;
3469 fs_server_context context
;
3471 AssertExclLocked(&entry
->lock
);
3473 init_fs_server_context (&context
);
3474 ret
= do_read_attr (entry
, ce
, &conn
, &context
);
3477 free_fs_server_context (&context
);
3482 * Read the contents of `entry' from the file server and store it.
3484 * If the wanted data is busy, try waiting for it.
3488 read_data (FCacheEntry
*entry
, ConnCacheEntry
*conn
, CredCacheEntry
*ce
,
3489 long partition
, uint64_t wanted_offset
, uint64_t wanted_end
)
3491 struct rx_call
*call
;
3494 uint64_t end
, nbytes
= 0;
3496 AFSFetchStatus status
;
3497 AFSCallBack callback
;
3499 struct collect_stat collectstat
;
3503 Bool unlocked
= FALSE
;
3505 arla_warnx (ADEBMISC
, "read_data");
3507 AssertExclLocked(&entry
->lock
);
3509 if (connected_mode
== DISCONNECTED
)
3512 state
= first_wanted_range(entry
, wanted_offset
, wanted_end
,
3515 case BLOCK_GOT
: /* got block to fetch, process below */
3517 case BLOCK_BUSY
: /* already fetching block, wait for them and retry */
3521 return 0; /* already cached, no blocks to fetch */
3524 /* figure out how much more then we need we want to fetch */
3525 /* XXX end = stats_fetch_round(conn, partition, end); */
3526 if (end
> fcache_get_status_length(&entry
->status
))
3527 end
= fcache_get_status_length(&entry
->status
);
3529 nbytes
= end
- offset
;
3531 nblocks
= create_busy_blocks(entry
, offset
, end
);
3534 arla_warn(ADEBFCACHE
, ret
, "read_data blocks");
3535 delete_unbusy_blocks(entry
, offset
, offset
, end
, FALSE
);
3540 * Release lock on ordinary nodes during rpc, it's still
3544 fcache_unlock(entry
);
3547 ret
= fcache_update_usage(entry
, nblocks
);
3549 arla_warn(ADEBFCACHE
, ret
, "read_data usage");
3550 fcache_lock(entry
, FALSE
);
3551 delete_unbusy_blocks(entry
, offset
, offset
, end
, FALSE
);
3556 /* now go talk to the world */
3557 call
= rx_NewCall (conn
->connection
);
3559 arla_warnx (ADEBMISC
, "rx_NewCall failed");
3564 arla_warnx(ADEBFCACHE
, "read_data: from %#llx to %#llx",
3567 collectstats_start(&collectstat
);
3568 if (conn_get_fs_support64(conn
)) {
3569 ret
= StartRXAFS_FetchData64 (call
,
3573 if (ret
== RXGEN_OPCODE
) {
3574 rx_EndCall(call
,ret
);
3575 conn_set_fs_support64(conn
, FALSE
);
3578 } else if (end
>> 32)
3581 ret
= StartRXAFS_FetchData (call
,
3587 arla_warn (ADEBFCACHE
, ret
, "fetch-data");
3588 rx_EndCall(call
,ret
);
3592 if (conn_get_fs_support64(conn
)) {
3593 ret
= rx_Read (call
, &sizefs4
, sizeof(sizefs4
));
3594 if (ret
!= sizeof(sizefs4
)) {
3595 ret
= rx_GetCallError(call
);
3596 if (ret
== RXGEN_OPCODE
&& conn_get_fs_support64(conn
)) {
3598 conn_set_fs_support64(conn
, FALSE
);
3601 ret
= conv_to_arla_errno(ret
);
3602 arla_warn (ADEBFCACHE
, ret
, "Error reading length");
3603 rx_EndCall(call
, 0);
3606 sizefs
= (int64_t)ntohl(sizefs4
) << 32;
3610 ret
= rx_Read (call
, &sizefs4
, sizeof(sizefs4
));
3611 if (ret
!= sizeof(sizefs4
)) {
3612 ret
= conv_to_arla_errno(rx_GetCallError(call
));
3613 arla_warn (ADEBFCACHE
, ret
, "Error reading length");
3614 rx_EndCall(call
, 0);
3618 sizefs
|= ntohl (sizefs4
);
3622 /* get node lock again, now that we're about to change things */
3623 fcache_lock(entry
, FALSE
);
3626 ret
= copyrx2cache(call
, entry
, offset
, sizefs
);
3628 arla_warn (ADEBFCACHE
, ret
, "copyrx2cache");
3629 rx_EndCall(call
, ret
);
3633 if (conn_get_fs_support64(conn
)) {
3634 ret
= EndRXAFS_FetchData64 (call
,
3639 ret
= EndRXAFS_FetchData (call
,
3645 arla_warn (ADEBWARN
, ret
, "EndRXAFS_FetchData");
3646 ret
= rx_EndCall (call
, ret
);
3648 arla_warn (ADEBFCACHE
, ret
, "rx_EndCall");
3651 collectstats_stop(&collectstat
, entry
, conn
,
3652 partition
, arla_STATISTICS_REQTYPE_FETCHDATA
, sizefs
);
3654 fcache_counter
.fetch_data
++;
3656 update_entry (entry
, &status
, &callback
, &volsync
, conn
, ce
->cred
);
3660 fcache_lock(entry
, FALSE
);
3663 * Unbusy blocks. If we didn't get all created blocks, the rest
3664 * should be removed.
3668 delete_unbusy_blocks(entry
, offset
,
3669 block_next_offset(offset
+ sizefs
), end
,
3672 AssertExclLocked(&entry
->lock
);
3678 * Write the contents of the cache file back to the file server.
3682 write_data(FCacheEntry
*entry
, FCacheEntry
*data_entry
,
3683 uint64_t offset
, uint64_t length
,
3684 AFSStoreStatus
*storestatus
, CredCacheEntry
*ce
)
3686 FCacheEntry
*fd_entry
= entry
;
3687 ConnCacheEntry
*conn
;
3688 struct rx_call
*call
;
3691 AFSFetchStatus status
;
3693 fs_server_context context
;
3694 struct collect_stat collectstat
;
3696 AssertExclLocked(&entry
->lock
);
3699 AssertExclLocked(&data_entry
->lock
);
3700 fd_entry
= data_entry
;
3703 if (connected_mode
!= CONNECTED
|| entry
->flags
.silly
)
3706 sizefs
= fcache_get_status_length(&fd_entry
->status
);
3709 fcache_data_setbusy(fd_entry
, offset
, offset
+ length
, TRUE
);
3711 /* keep node lock for now, perhaps unnecessary */
3713 ret
= init_fs_context(entry
, ce
, &context
);
3715 fcache_data_setbusy(fd_entry
, offset
, offset
+ length
, FALSE
);
3719 for (conn
= find_first_fs (&context
);
3721 conn
= find_next_fs (&context
, conn
, ret
)) {
3724 call
= rx_NewCall (conn
->connection
);
3726 arla_warnx (ADEBMISC
, "rx_NewCall failed");
3731 collectstats_start(&collectstat
);
3732 if (conn_get_fs_support64(conn
)) {
3733 ret
= StartRXAFS_StoreData64 (call
, &entry
->fid
.fid
,
3738 if (ret
== RXGEN_OPCODE
) {
3739 rx_EndCall(call
,ret
);
3740 conn_set_fs_support64(conn
, FALSE
);
3743 } else if ((uint64_t)sizefs
>> 32) {
3746 ret
= StartRXAFS_StoreData (call
, &entry
->fid
.fid
,
3753 if (host_downp(ret
)) {
3754 rx_EndCall(call
, ret
);
3757 arla_warn (ADEBFCACHE
, ret
, "store-data");
3758 rx_EndCall(call
, 0);
3762 ret
= copycache2rx(fd_entry
, call
, offset
, length
);
3763 if (ret
== RXGEN_OPCODE
&& conn_get_fs_support64(conn
)) {
3764 rx_EndCall(call
,ret
);
3765 conn_set_fs_support64(conn
, FALSE
);
3768 rx_EndCall(call
, ret
);
3769 arla_warn (ADEBFCACHE
, ret
, "copycache2rx");
3773 if (conn_get_fs_support64(conn
)) {
3774 ret
= EndRXAFS_StoreData64 (call
,
3777 if (ret
== RXGEN_OPCODE
) {
3778 rx_EndCall(call
, 0);
3779 conn_set_fs_support64(conn
, FALSE
);
3783 ret
= EndRXAFS_StoreData (call
,
3788 rx_EndCall (call
, ret
);
3789 arla_warnx (ADEBFCACHE
, "EndRXAFS_StoreData");
3793 ret
= rx_EndCall (call
, 0);
3795 arla_warn (ADEBFCACHE
, ret
, "rx_EndCall");
3797 collectstats_stop(&collectstat
, entry
, conn
,
3798 find_partition(&context
),
3799 arla_STATISTICS_REQTYPE_STOREDATA
, sizefs
);
3805 fcache_counter
.store_data
++;
3806 update_entry (entry
, &status
, NULL
, &volsync
,
3811 * We can't do this, it will corrupt the cache since nnpfs
3812 * will still think it have the data, and then when we
3813 * write back the file to the fileserver, it will be
3814 * filled with zeros. Happens if you are unlucky so store
3815 * a file at the same moment as your credentials expire.
3818 usedbytes
-= entry
->usage
;
3823 fcache_data_setbusy(fd_entry
, offset
, offset
+ length
, FALSE
);
3825 if (host_downp(ret
))
3828 free_fs_server_context (&context
);
3829 AssertExclLocked(&entry
->lock
);
3831 AssertExclLocked(&data_entry
->lock
);
3833 /* make sure data is up to date, copy it or just throw */
3841 * Truncate the file in `entry' to `size' bytes.
3845 truncate_file (FCacheEntry
*entry
, uint64_t size
,
3846 AFSStoreStatus
*storestatus
, CredCacheEntry
*ce
)
3848 fs_server_context context
;
3849 ConnCacheEntry
*conn
;
3850 struct rx_call
*call
;
3851 AFSFetchStatus status
;
3855 AssertExclLocked(&entry
->lock
);
3857 if (connected_mode
!= CONNECTED
)
3862 ret
= fcache_verify_data(entry
, ce
, 0, 0);
3867 ret
= init_fs_context(entry
, ce
, &context
);
3871 for (conn
= find_first_fs (&context
);
3873 conn
= find_next_fs (&context
, conn
, ret
)) {
3876 call
= rx_NewCall (conn
->connection
);
3878 arla_warnx (ADEBMISC
, "rx_NewCall failed");
3883 if (conn_get_fs_support64(conn
)) {
3884 ret
= StartRXAFS_StoreData64 (call
,
3890 if (ret
== RXGEN_OPCODE
) {
3891 rx_EndCall(call
, ret
);
3892 conn_set_fs_support64(conn
, FALSE
);
3895 } else if (size
>> 32)
3898 ret
= StartRXAFS_StoreData (call
,
3904 if (host_downp(ret
)) {
3905 rx_EndCall(call
, ret
);
3908 arla_warn (ADEBFCACHE
, ret
, "store-data");
3909 rx_EndCall(call
, 0);
3913 if (conn_get_fs_support64(conn
)) {
3914 ret
= EndRXAFS_StoreData64 (call
,
3917 if (ret
== RXGEN_OPCODE
) {
3918 rx_EndCall(call
, 0);
3919 conn_set_fs_support64(conn
, FALSE
);
3923 ret
= EndRXAFS_StoreData (call
,
3928 rx_EndCall (call
, ret
);
3929 arla_warnx (ADEBFCACHE
, "EndRXAFS_StoreData");
3933 ret
= rx_EndCall (call
, 0);
3935 arla_warn (ADEBFCACHE
, ret
, "rx_EndCall");
3941 ret
= abuf_truncate(entry
, size
);
3943 arla_warn (ADEBFCACHE
, ret
, "abuf_truncate %ld", (long)size
);
3944 free_fs_server_context (&context
);
3948 fcache_counter
.store_data
++;
3949 update_entry (entry
, &status
, NULL
, &volsync
,
3953 free_fs_server_context (&context
);
3955 if (host_downp(ret
))
3958 AssertExclLocked(&entry
->lock
);
3963 * Set the attributes of the file in `entry' to `status'.
3967 write_attr (FCacheEntry
*entry
,
3968 const AFSStoreStatus
*store_status
,
3971 ConnCacheEntry
*conn
= NULL
;
3973 AFSFetchStatus status
;
3976 AssertExclLocked(&entry
->lock
);
3978 /* Don't write attributes to deleted files */
3979 if (entry
->flags
.silly
)
3982 if (connected_mode
== CONNECTED
) {
3983 fs_server_context context
;
3984 struct collect_stat collectstat
;
3986 ret
= init_fs_context(entry
, ce
, &context
);
3990 for (conn
= find_first_fs (&context
);
3992 conn
= find_next_fs (&context
, conn
, ret
)) {
3994 collectstats_start(&collectstat
);
3995 ret
= RXAFS_StoreStatus (conn
->connection
,
4000 if (host_downp(ret
)) {
4003 arla_warn (ADEBFCACHE
, ret
, "store-status");
4004 free_fs_server_context (&context
);
4013 collectstats_stop(&collectstat
, entry
, conn
,
4014 find_partition(&context
),
4015 arla_STATISTICS_REQTYPE_STORESTATUS
, 1);
4018 free_fs_server_context (&context
);
4020 if (host_downp(ret
)) {
4024 update_entry (entry
, &status
, NULL
, &volsync
, conn
, ce
->cred
);
4027 assert (conn
== NULL
);
4029 fcache_counter
.store_attr
++;
4030 if (store_status
->Mask
& SS_MODTIME
) {
4031 entry
->status
.ClientModTime
= store_status
->ClientModTime
;
4032 entry
->status
.ServerModTime
= store_status
->ClientModTime
;
4034 if (store_status
->Mask
& SS_OWNER
)
4035 entry
->status
.Owner
= store_status
->Owner
;
4036 if (store_status
->Mask
& SS_GROUP
)
4037 entry
->status
.Group
= store_status
->Group
;
4038 if (store_status
->Mask
& SS_MODEBITS
)
4039 entry
->status
.UnixModeBits
= store_status
->UnixModeBits
;
4046 AssertExclLocked(&entry
->lock
);
4052 * Create a file. The new node is returned locked in `ret_entry'.
4056 create_file (FCacheEntry
*dir_entry
,
4057 const char *name
, AFSStoreStatus
*store_attr
,
4058 FCacheEntry
**ret_entry
, CredCacheEntry
*ce
)
4060 ConnCacheEntry
*conn
= NULL
;
4062 FCacheEntry
*child_entry
;
4063 AFSFetchStatus fetch_attr
;
4065 AFSFetchStatus status
;
4066 AFSCallBack callback
;
4069 AssertExclLocked(&dir_entry
->lock
);
4073 if (connected_mode
== CONNECTED
) {
4074 fs_server_context context
;
4076 ret
= init_fs_context(dir_entry
, ce
, &context
);
4080 for (conn
= find_first_fs (&context
);
4082 conn
= find_next_fs (&context
, conn
, ret
)) {
4084 ret
= RXAFS_CreateFile (conn
->connection
,
4085 &dir_entry
->fid
.fid
,
4093 if (host_downp(ret
)) {
4096 free_fs_server_context (&context
);
4097 arla_warn (ADEBFCACHE
, ret
, "CreateFile");
4105 free_fs_server_context (&context
);
4107 if (host_downp(ret
)) {
4112 fetch_attr
.CallerAccess
|= AADMIN
;
4114 update_modify_dir(dir_entry
, &status
, &callback
,
4115 &volsync
, conn
, ce
->cred
);
4117 static int fakefid
= 1001;
4119 assert(conn
== NULL
);
4123 child_fid
.fid
.Volume
= dir_entry
->fid
.fid
.Volume
;
4124 child_fid
.fid
.Vnode
= fakefid
;
4125 child_fid
.fid
.Unique
= fakefid
;
4128 fetch_attr
.InterfaceVersion
= 1;
4129 fetch_attr
.FileType
= TYPE_FILE
;
4130 fetch_attr
.LinkCount
= 1;
4131 fetch_attr
.Length
= 0;
4132 fetch_attr
.DataVersion
= 1;
4133 fetch_attr
.Author
= store_attr
->Owner
;
4134 fetch_attr
.Owner
= store_attr
->Owner
;
4135 fetch_attr
.CallerAccess
= dir_entry
->status
.CallerAccess
;
4136 fetch_attr
.AnonymousAccess
= dir_entry
->status
.AnonymousAccess
;
4137 fetch_attr
.UnixModeBits
= store_attr
->UnixModeBits
;
4138 fetch_attr
.ParentVnode
= dir_entry
->fid
.fid
.Vnode
;
4139 fetch_attr
.ParentUnique
= dir_entry
->fid
.fid
.Unique
;
4140 fetch_attr
.ResidencyMask
= 1;
4141 fetch_attr
.ClientModTime
= store_attr
->ClientModTime
;
4142 fetch_attr
.ServerModTime
= store_attr
->ClientModTime
;
4143 fetch_attr
.Group
= store_attr
->Group
;
4144 fetch_attr
.SyncCounter
= 0;
4145 fetch_attr
.DataVersionHigh
= 0;
4146 fetch_attr
.LockCount
= 0;
4147 fetch_attr
.LengthHigh
= 0;
4148 fetch_attr
.ErrorCode
= 0;
4151 child_fid
.Cell
= dir_entry
->fid
.Cell
;
4153 ret
= fcache_get(&child_entry
, child_fid
, ce
);
4155 arla_warn (ADEBFCACHE
, ret
, "fcache_get");
4159 update_entry(child_entry
, &fetch_attr
, NULL
, NULL
,
4162 throw_data(child_entry
);
4163 ret
= create_block(child_entry
, 0);
4165 arla_warn(ADEBFCACHE
, ret
, "create cache file %u",
4166 (unsigned)child_entry
->index
);
4167 fcache_release(child_entry
);
4171 child_entry
->tokens
|= NNPFS_ATTR_R
| NNPFS_DATA_R
| NNPFS_DATA_W
;
4172 child_entry
->flags
.attrp
= TRUE
;
4174 *ret_entry
= child_entry
;
4180 AssertExclLocked(&dir_entry
->lock
);
4186 * Create a directory.
4190 create_directory (FCacheEntry
*dir_entry
,
4191 const char *name
, AFSStoreStatus
*store_attr
,
4192 VenusFid
*child_fid
, AFSFetchStatus
*fetch_attr
,
4195 ConnCacheEntry
*conn
= NULL
;
4198 FCacheEntry
*child_entry
;
4199 AFSFetchStatus status
;
4200 AFSCallBack callback
;
4204 AssertExclLocked(&dir_entry
->lock
);
4206 if (connected_mode
== CONNECTED
) {
4207 fs_server_context context
;
4209 ret
= init_fs_context(dir_entry
, ce
, &context
);
4213 for (conn
= find_first_fs (&context
);
4215 conn
= find_next_fs (&context
, conn
, ret
)) {
4217 ret
= RXAFS_MakeDir (conn
->connection
,
4218 &dir_entry
->fid
.fid
,
4227 if (host_downp(ret
)) {
4230 free_fs_server_context (&context
);
4231 arla_warn (ADEBFCACHE
, ret
, "MakeDir");
4238 free_fs_server_context (&context
);
4240 if (host_downp(ret
)) {
4245 update_modify_dir(dir_entry
, &status
, &callback
, &volsync
,
4248 static int fakedir
= 1000;
4252 assert(conn
== NULL
);
4254 OutFid
.Volume
= dir_entry
->fid
.fid
.Volume
;
4255 OutFid
.Vnode
= fakedir
;
4256 OutFid
.Unique
= fakedir
;
4259 fetch_attr
->InterfaceVersion
= 1;
4260 fetch_attr
->FileType
= TYPE_DIR
;
4261 fetch_attr
->LinkCount
= 2;
4262 fetch_attr
->Length
= AFSDIR_PAGESIZE
;
4263 fetch_attr
->DataVersion
= 1;
4264 fetch_attr
->Author
= store_attr
->Owner
;
4265 fetch_attr
->Owner
= store_attr
->Owner
;
4266 fetch_attr
->CallerAccess
= dir_entry
->status
.CallerAccess
;
4267 fetch_attr
->AnonymousAccess
= dir_entry
->status
.AnonymousAccess
;
4268 fetch_attr
->UnixModeBits
= store_attr
->UnixModeBits
;
4269 fetch_attr
->ParentVnode
= dir_entry
->fid
.fid
.Vnode
;
4270 fetch_attr
->ParentUnique
= dir_entry
->fid
.fid
.Unique
;
4271 fetch_attr
->ResidencyMask
= 1;
4272 fetch_attr
->ClientModTime
= store_attr
->ClientModTime
;
4273 fetch_attr
->ServerModTime
= store_attr
->ClientModTime
;
4274 fetch_attr
->Group
= store_attr
->Group
;
4275 fetch_attr
->SyncCounter
= 0;
4276 fetch_attr
->DataVersionHigh
= 0;
4277 fetch_attr
->LockCount
= 0;
4278 fetch_attr
->LengthHigh
= 0;
4279 fetch_attr
->ErrorCode
= 0;
4282 child_fid
->Cell
= dir_entry
->fid
.Cell
;
4283 child_fid
->fid
= OutFid
;
4285 ret
= fcache_get (&child_entry
, *child_fid
, ce
);
4287 arla_warn (ADEBFCACHE
, ret
, "fcache_get");
4291 assert(child_entry
->usage
== 0);
4293 update_entry (child_entry
, fetch_attr
, NULL
, NULL
,
4296 child_entry
->flags
.attrp
= TRUE
;
4298 ret
= adir_mkdir (child_entry
, child_fid
->fid
, dir_entry
->fid
.fid
);
4300 arla_warn (ADEBFCACHE
, ret
, "adir_mkdir");
4301 fcache_release(child_entry
);
4305 child_entry
->tokens
|= NNPFS_ATTR_R
| NNPFS_DATA_R
| NNPFS_DATA_W
;
4307 fcache_release(child_entry
);
4312 AssertExclLocked(&dir_entry
->lock
);
4317 * Create a symbolic link.
4319 * Note: create_symlink->flags.kernelp is not set on success
4320 * and that must be done by the caller.
4324 create_symlink (FCacheEntry
*dir_entry
,
4325 const char *name
, AFSStoreStatus
*store_attr
,
4326 VenusFid
*child_fid
, AFSFetchStatus
*fetch_attr
,
4327 const char *contents
,
4331 ConnCacheEntry
*conn
;
4333 FCacheEntry
*child_entry
;
4335 AFSFetchStatus new_status
;
4336 fs_server_context context
;
4338 AssertExclLocked(&dir_entry
->lock
);
4340 if (connected_mode
!= CONNECTED
)
4343 ret
= init_fs_context(dir_entry
, ce
, &context
);
4347 for (conn
= find_first_fs (&context
);
4349 conn
= find_next_fs (&context
, conn
, ret
)) {
4351 ret
= RXAFS_Symlink (conn
->connection
,
4352 &dir_entry
->fid
.fid
,
4360 if (host_downp(ret
)) {
4363 arla_warn (ADEBFCACHE
, ret
, "Symlink");
4364 free_fs_server_context (&context
);
4371 free_fs_server_context (&context
);
4373 if (host_downp(ret
)) {
4378 update_modify_dir(dir_entry
, &new_status
, NULL
, &volsync
,
4381 child_fid
->Cell
= dir_entry
->fid
.Cell
;
4382 child_fid
->fid
= OutFid
;
4384 ret
= fcache_get (&child_entry
, *child_fid
, ce
);
4386 arla_warn (ADEBFCACHE
, ret
, "fcache_get");
4390 update_entry (child_entry
, fetch_attr
, NULL
, NULL
,
4394 * flags.kernelp is set in cm_symlink since the symlink
4395 * might be a mountpoint and this entry is never install
4399 child_entry
->flags
.attrp
= TRUE
;
4400 child_entry
->tokens
|= NNPFS_ATTR_R
;
4402 fcache_release(child_entry
);
4407 AssertExclLocked(&dir_entry
->lock
);
4412 * Create a hard link.
4416 create_link (FCacheEntry
*dir_entry
,
4418 FCacheEntry
*existing_entry
,
4421 ConnCacheEntry
*conn
= NULL
;
4423 AFSFetchStatus new_status
;
4424 AFSFetchStatus status
;
4426 fs_server_context context
;
4428 AssertExclLocked(&dir_entry
->lock
);
4430 if (connected_mode
!= CONNECTED
)
4433 ret
= init_fs_context(dir_entry
, ce
, &context
);
4437 for (conn
= find_first_fs (&context
);
4439 conn
= find_next_fs (&context
, conn
, ret
)) {
4441 ret
= RXAFS_Link (conn
->connection
,
4442 &dir_entry
->fid
.fid
,
4444 &existing_entry
->fid
.fid
,
4448 if (host_downp(ret
)) {
4451 free_fs_server_context (&context
);
4452 arla_warn (ADEBFCACHE
, ret
, "Link");
4459 free_fs_server_context (&context
);
4461 if (host_downp(ret
)) {
4466 update_modify_dir(dir_entry
, &status
, NULL
, &volsync
,
4469 update_entry (existing_entry
, &new_status
, NULL
, NULL
,
4475 AssertExclLocked(&dir_entry
->lock
);
4480 * Remove a file from a directory.
4484 remove_file(FCacheEntry
*dir_entry
, const char *name
,
4485 FCacheEntry
*child_entry
, CredCacheEntry
*ce
)
4488 ConnCacheEntry
*conn
;
4489 AFSFetchStatus status
;
4491 fs_server_context context
;
4493 AssertExclLocked(&dir_entry
->lock
);
4494 AssertExclLocked(&child_entry
->lock
);
4496 if (connected_mode
== CONNECTED
) {
4498 ret
= init_fs_context(dir_entry
, ce
, &context
);
4502 for (conn
= find_first_fs (&context
);
4504 conn
= find_next_fs (&context
, conn
, ret
)) {
4506 ret
= RXAFS_RemoveFile (conn
->connection
,
4507 &dir_entry
->fid
.fid
,
4511 if (host_downp(ret
)) {
4514 free_fs_server_context (&context
);
4515 arla_warn (ADEBFCACHE
, ret
, "RemoveFile");
4522 free_fs_server_context (&context
);
4524 if (host_downp(ret
))
4532 status
= dir_entry
->status
;
4536 ret
= fcache_get_fbuf(dir_entry
, &the_fbuf
, FBUF_READ
);
4540 ret
= fdir_lookup(&the_fbuf
, &dir_entry
->fid
, name
, &child_fid
);
4542 FCacheEntry
*child_entry
= NULL
;
4543 uint32_t disco_id
= 0;
4545 child_entry
= fcache_find(child_fid
);
4547 disco_id
= child_entry
->disco_id
;
4549 disco_id
= disco_unlink(&dir_entry
->fid
, &child_fid
,
4553 child_entry
->disco_id
= disco_id
;
4554 fcache_release(child_entry
);
4558 abuf_end (&the_fbuf
);
4560 ret
= EINVAL
; /* XXX */
4565 update_modify_dir(dir_entry
, &status
, NULL
, &volsync
,
4567 child_entry
->status
.LinkCount
--;
4568 if (child_entry
->status
.LinkCount
== 0)
4569 child_entry
->flags
.silly
= TRUE
;
4575 AssertExclLocked(&dir_entry
->lock
);
4580 * Remove a directory from a directory.
4584 remove_directory(FCacheEntry
*dir_entry
,
4586 FCacheEntry
*child_entry
,
4590 ConnCacheEntry
*conn
;
4591 AFSFetchStatus status
;
4593 fs_server_context context
;
4595 AssertExclLocked(&dir_entry
->lock
);
4597 if (connected_mode
!= CONNECTED
)
4600 ret
= init_fs_context(dir_entry
, ce
, &context
);
4604 for (conn
= find_first_fs (&context
);
4606 conn
= find_next_fs (&context
, conn
, ret
)) {
4608 ret
= RXAFS_RemoveDir (conn
->connection
,
4609 &dir_entry
->fid
.fid
,
4613 if (host_downp(ret
)) {
4616 free_fs_server_context (&context
);
4617 arla_warn (ADEBFCACHE
, ret
, "RemoveDir");
4624 free_fs_server_context (&context
);
4626 if (host_downp(ret
)) {
4631 update_modify_dir(dir_entry
, &status
, NULL
, &volsync
,
4634 if (child_entry
->status
.LinkCount
== 2) {
4635 child_entry
->status
.LinkCount
= 0;
4636 child_entry
->flags
.silly
= TRUE
;
4642 AssertExclLocked(&dir_entry
->lock
);
4651 rename_file (FCacheEntry
*old_dir
,
4652 const char *old_name
,
4653 FCacheEntry
*new_dir
,
4654 const char *new_name
,
4657 int ret
= ARLA_CALL_DEAD
;
4658 ConnCacheEntry
*conn
;
4659 AFSFetchStatus orig_status
, new_status
;
4661 fs_server_context context
;
4663 AssertExclLocked(&old_dir
->lock
);
4664 AssertExclLocked(&new_dir
->lock
);
4666 if (connected_mode
!= CONNECTED
)
4669 ret
= init_fs_context(old_dir
, ce
, &context
);
4673 for (conn
= find_first_fs (&context
);
4675 conn
= find_next_fs (&context
, conn
, ret
)) {
4677 ret
= RXAFS_Rename (conn
->connection
,
4685 if (host_downp(ret
)) {
4688 free_fs_server_context (&context
);
4689 arla_warn (ADEBFCACHE
, ret
, "Rename");
4696 free_fs_server_context (&context
);
4698 if (host_downp(ret
)) {
4704 if (old_dir
!= new_dir
)
4705 update_modify_dir(old_dir
, &orig_status
, NULL
, &volsync
,
4708 update_modify_dir(new_dir
, &new_status
, NULL
, &volsync
,
4714 AssertExclLocked(&old_dir
->lock
);
4715 AssertExclLocked(&new_dir
->lock
);
4720 * Return the fid to the root.
4724 getroot (VenusFid
*res
, CredCacheEntry
*ce
)
4728 const char *root_volume
= volcache_get_rootvolume ();
4730 const char *this_cell
= cell_getthiscell ();
4731 int32_t this_cell_id
;
4733 if (dynroot_enablep()) {
4734 this_cell
= "dynroot";
4735 this_cell_id
= dynroot_cellid();
4737 this_cell_id
= cell_name2num (this_cell
);
4738 if (this_cell_id
== -1)
4739 arla_errx (1, ADEBERROR
, "cell %s does not exist", this_cell
);
4742 ret
= volcache_getbyname (root_volume
, this_cell_id
, ce
, &ve
, NULL
);
4744 arla_warn (ADEBWARN
, ret
,
4745 "Cannot find the root volume (%s) in cell %s",
4746 root_volume
, this_cell
);
4750 fid
.Cell
= this_cell_id
;
4751 if (ve
->entry
.flags
& VLF_ROEXISTS
) {
4752 fid
.fid
.Volume
= ve
->entry
.volumeId
[ROVOL
];
4753 } else if (ve
->entry
.flags
& VLF_RWEXISTS
) {
4754 arla_warnx(ADEBERROR
,
4755 "getroot: %s in cell %s is missing a RO clone, not good",
4756 root_volume
, this_cell
);
4757 fid
.fid
.Volume
= ve
->entry
.volumeId
[RWVOL
];
4759 arla_errx(1, ADEBERROR
,
4760 "getroot: %s in cell %s has no RW or RO clone?",
4761 root_volume
, this_cell
);
4763 fid
.fid
.Vnode
= fid
.fid
.Unique
= 1;
4772 * Return the type for this volume.
4776 getvoltype(int32_t volid
, const VolCacheEntry
*ve
)
4780 for (i
= RWVOL
; i
<= BACKVOL
; ++i
)
4781 if (ve
->entry
.volumeId
[i
] == volid
)
4784 return -1; /* NOT REACHED */
4788 * Return locked entry for `fid' or NULL. If `gcp' is set the
4789 * returned node may be under gc.
4793 fcache_find_gcp(VenusFid fid
, Bool gcp
)
4795 FCacheEntry
*res
= find_entry_nolock(fid
);
4800 assert(res
->refcount
> 0);
4802 fcache_lock(res
, gcp
);
4804 assert(!res
->flags
.gcp
);
4810 * Return locked entry for `fid' or NULL.
4814 fcache_find(VenusFid fid
)
4816 return fcache_find_gcp(fid
, FALSE
);
4820 * Return the entry for `fid'. If it's not cached, add it.
4824 fcache_get_int(FCacheEntry
**res
, VenusFid fid
, CredCacheEntry
*ce
, Bool gcp
)
4833 old
= fcache_find_gcp(fid
, gcp
);
4835 assert (old
->flags
.usedp
);
4840 error
= volcache_getbyid (fid
.fid
.Volume
, fid
.Cell
, ce
, &vol
, NULL
);
4842 if (connected_mode
== DISCONNECTED
&& error
== ENOENT
)
4847 e
= find_free_entry ();
4850 old
= fcache_find_gcp(fid
, gcp
);
4852 AssertExclLocked(&e
->lock
);
4855 e
->lru_le
= listaddtail(free_nodes
, e
);
4858 assert (old
->flags
.usedp
);
4865 assert(listemptyp(e
->blocks
));
4871 memset (&e
->status
, 0, sizeof(e
->status
));
4872 memset (&e
->callback
, 0, sizeof(e
->callback
));
4873 memset (&e
->volsync
, 0, sizeof(e
->volsync
));
4874 for (i
= 0; i
< NACCESS
; i
++) {
4875 e
->acccache
[i
].cred
= ARLA_NO_AUTH_CRED
;
4876 e
->acccache
[i
].access
= 0;
4879 e
->flags
.usedp
= TRUE
;
4880 e
->flags
.attrp
= FALSE
;
4881 e
->flags
.attrusedp
= FALSE
;
4882 e
->flags
.datausedp
= FALSE
;
4883 e
->flags
.extradirp
= FALSE
;
4884 e
->flags
.mountp
= FALSE
;
4885 e
->flags
.fake_mp
= FALSE
;
4886 e
->flags
.vol_root
= FALSE
;
4887 e
->flags
.kernelp
= FALSE
;
4888 e
->flags
.sentenced
= FALSE
;
4889 e
->flags
.stale
= FALSE
;
4890 e
->flags
.dirtied
= FALSE
;
4891 e
->flags
.silly
= FALSE
;
4892 e
->flags
.waiters
= FALSE
;
4893 e
->flags
.gcp
= FALSE
;
4894 e
->flags
.appended
= FALSE
;
4896 memset (&e
->parent
, 0, sizeof(e
->parent
));
4897 e
->lru_le
= listaddhead (node_lru
, e
);
4899 e
->invalid_ptr
= -1;
4901 e
->priority
= fprio_get(fid
);
4904 hashtabadd (hashtab
, e
);
4911 * Return the entry for `fid'. If it's not cached, add it.
4915 fcache_get(FCacheEntry
**res
, VenusFid fid
, CredCacheEntry
*ce
)
4917 return fcache_get_int(res
, fid
, ce
, FALSE
);
4921 * Return the entry for `fid'. If it's not cached, add it.
4925 fcache_get_gc(FCacheEntry
**res
, VenusFid fid
, CredCacheEntry
*ce
)
4927 return fcache_get_int(res
, fid
, ce
, TRUE
);
4931 * Release the lock on `e' and mark it as stale if it has been sentenced.
4935 fcache_release(FCacheEntry
*e
)
4937 AssertExclLocked(&e
->lock
);
4941 assert(e
->refcount
>= 0);
4945 if (e
->flags
.sentenced
) {
4946 e
->flags
.sentenced
= FALSE
;
4947 stale(e
, broken_callback
);
4956 uptodatep (FCacheEntry
*e
)
4959 assert (e
->flags
.usedp
);
4961 if (connected_mode
!= CONNECTED
&&
4962 connected_mode
!= FETCH_ONLY
)
4965 gettimeofday(&tv
, NULL
);
4967 if (tv
.tv_sec
< e
->callback
.ExpirationTime
&&
4968 e
->callback
.CallBackType
!= CBDROPPED
&&
4969 (e
->callback
.CallBackType
!= 0
4970 || e
->volume
->volsync
.spare1
!= e
->volsync
.spare1
))
4977 * The idea is that we start to stat everything after the prefered
4978 * entry, everything before that is probably not useful to get, the
4979 * user is probably trying to stat() everything _after_ that node.
4980 * This might be somewhat bogus, but we dont care (for now).
4984 int len
; /* used entries in fids and names */
4985 AFSFid fids
[AFSCBMAX
]; /* fids to fetch */
4986 char *names
[AFSCBMAX
]; /* names it install */
4987 AFSFid
*used
; /* do we have a prefered node */
4988 CredCacheEntry
*ce
; /* cred to use */
4992 struct nnpfs_message_installnode node
;
4993 struct nnpfs_message_installattr attr
;
4994 } nnpfs_message_install_node_attr
;
4997 bulkstat_help_func (VenusFid
*fid
, const char *name
, void *ptr
)
4999 struct bulkstat
*bs
= (struct bulkstat
*) ptr
;
5005 if (bs
->len
> fcache_bulkstatus_num
)
5008 /* Ignore . and .. */
5009 if (strcmp(name
, ".") == 0 || strcmp(name
, "..") == 0)
5013 * Do we have a prefered node, and is this the one. If we don't know
5014 * the name of the node (ie bs.names[0] == NULL), fill it in.
5015 * Set bs->used to NULL it indicate that we should start stat stuff
5016 * from here, remeber that bs->len == 1 if bs->used is set.
5019 if (memcmp(bs
->used
, &fid
->fid
, sizeof(fid
->fid
)) == 0) {
5020 if (bs
->names
[0] == NULL
)
5021 bs
->names
[0] = strdup (name
);
5022 bs
->used
= NULL
; /* stat everything after this */
5028 * Already cached for this pag ?
5031 e
= (FCacheEntry
*)hashtabsearch (hashtab
, (void *)&key
);
5036 && findaccess (bs
->ce
->cred
, e
->acccache
, &ae
) == TRUE
) {
5037 arla_warnx (ADEBFCACHE
,
5038 "bulkstat_help_func: already cached "
5039 "(%d.%d.%d.%d) name: %s",
5040 fid
->Cell
, fid
->fid
.Volume
, fid
->fid
.Vnode
,
5041 fid
->fid
.Unique
, name
);
5045 if (fcache_enable_bulkstatus
== 2) {
5046 /* cache the name for the installnode */
5047 bs
->names
[bs
->len
] = strdup (name
);
5048 if (bs
->names
[bs
->len
] == NULL
)
5051 bs
->names
[bs
->len
] = NULL
;
5055 bs
->fids
[bs
->len
] = fid
->fid
;
5062 * Do bulkstat for ``parent_entry''. Make sure that ``prefered_entry''
5063 * is in the list of fids it not NULL, and it ``prefered_name'' is NULL
5064 * try to find it in the list files in the directory.
5066 * Entry Success Failure
5067 * parent_entry locked locked locked
5068 * prefered_entry locked locked locked
5069 * or if NULL if set to NULL must not be locked
5070 * prefered_fid related fcache-entry must not be locked
5075 get_attr_bulk (FCacheEntry
*parent_entry
,
5076 FCacheEntry
*prefered_entry
,
5077 VenusFid
*prefered_fid
,
5078 const char *prefered_name
,
5081 fs_server_context context
;
5082 ConnCacheEntry
*conn
= NULL
;
5092 struct collect_stat collectstat
;
5094 arla_warnx (ADEBFCACHE
, "get_attr_bulk");
5096 AssertExclLocked(&parent_entry
->lock
);
5098 AssertExclLocked(&prefered_entry
->lock
);
5100 if (fcache_enable_bulkstatus
== 0)
5103 if (parent_entry
->usage
== 0) {
5104 arla_warnx (ADEBFCACHE
, "get_attr_bulk: parent doesn't have data");
5110 memset (bs
.names
, 0, sizeof(bs
.names
));
5111 memset (bs
.fids
, 0, sizeof(bs
.fids
));
5117 * If we have a prefered_entry, and that to the first entry in the
5118 * array. This is used later. If we find the prefered_entry in the
5119 * directory-structure its ignored.
5123 arla_warnx (ADEBFCACHE
, "get_attr_bulk: using prefered_entry");
5124 bs
.used
= &prefered_fid
->fid
;
5125 fids
.val
[bs
.len
] = prefered_fid
->fid
;
5126 if (prefered_name
!= NULL
) {
5127 bs
.names
[bs
.len
] = strdup(prefered_name
);
5128 if (bs
.names
[bs
.len
] == NULL
)
5131 bs
.names
[bs
.len
] = NULL
;
5136 ret
= fcache_get_fbuf (parent_entry
, &the_fbuf
, FBUF_READ
);
5140 ret
= fdir_readdir (&the_fbuf
,
5145 abuf_end (&the_fbuf
);
5152 * Don't do BulkStatus when fids.len == 0 since we should never do it.
5153 * There should at least be the node that we want in the BulkStatus.
5156 if (fids
.len
== 0) {
5158 arla_warnx (ADEBERROR
,
5160 "prefered_fid not found in dir");
5161 /* XXX MAGIC send it back so we don't do it again soon */
5162 parent_entry
->hits
-= 64;
5168 * XXX if there is a prefered fid, and and we didn't find the name for it
5172 if (prefered_fid
&& bs
.names
[0] == NULL
) {
5173 arla_warnx (ADEBFCACHE
,
5174 "get_attr_bulk: didn't find prefered_fid's name");
5179 ret
= ARLA_CALL_DEAD
;
5181 ret
= init_fs_context(parent_entry
, ce
, &context
);
5185 for (conn
= find_first_fs (&context
);
5187 conn
= find_next_fs (&context
, conn
, ret
)) {
5191 stats
.len
= cbs
.len
= 0;
5193 collectstats_start(&collectstat
);
5194 ret
= RXAFS_BulkStatus (conn
->connection
, &fids
, &stats
, &cbs
, &sync
);
5195 collectstats_stop(&collectstat
, parent_entry
, conn
,
5196 find_partition(&context
),
5197 arla_STATISTICS_REQTYPE_BULKSTATUS
, fids
.len
);
5203 if (host_downp(ret
)) {
5206 free_fs_server_context(&context
);
5207 arla_warn(ADEBFCACHE
, ret
, "BulkStatus");
5215 free_fs_server_context (&context
);
5222 arla_warnx (ADEBFCACHE
,"get_attr_bulk: BulkStatus returned %d",ret
);
5224 len
= min(fids
.len
, min(stats
.len
, cbs
.len
));
5227 * Save results of bulkstatus
5234 fcache_counter
.fetch_attr_bulk
+= len
;
5236 fid
.Cell
= parent_entry
->fid
.Cell
;
5237 for (i
= 0; i
< len
&& ret
== 0; i
++) {
5239 fid
.fid
= fids
.val
[i
];
5241 if (VenusFid_cmp(prefered_fid
, &fid
) == 0) {
5244 e
= find_entry_nolock (fid
);
5245 if (e
!= NULL
&& fcache_islocked(e
))
5248 ret
= fcache_get (&e
, fid
, ce
);
5252 update_attr_entry (e
,
5258 e
->parent
= parent_entry
->fid
;
5259 if (prefered_entry
!= e
) {
5266 * Insert result into kernel
5269 if (fcache_enable_bulkstatus
== 2 && ret
== 0) {
5270 nnpfs_message_install_node_attr msg
[AFSCBMAX
];
5271 struct nnpfs_msg_node
*node
;
5272 nnpfs_handle
*parent
;
5277 fid
.Cell
= parent_entry
->fid
.Cell
;
5278 for (i
= 0 , j
= 0; i
< len
&& ret
== 0; i
++) {
5281 fid
.fid
= fids
.val
[i
];
5283 if (VenusFid_cmp(prefered_fid
, &fid
) == 0) {
5286 e
= find_entry_nolock (fid
);
5287 if (e
!= NULL
&& fcache_islocked(e
))
5290 ret
= fcache_get (&e
, fid
, ce
);
5296 arla_warnx (ADEBFCACHE
, "installing %d.%d.%d\n",
5300 assert_flag(e
,kernelp
);
5301 e
->flags
.attrusedp
= TRUE
;
5304 * Its its already installed, just update with installattr
5307 e
->tokens
|= NNPFS_ATTR_R
;
5309 if (!e
->flags
.kernelp
|| !e
->flags
.datausedp
)
5310 tokens
&= ~NNPFS_DATA_MASK
;
5312 if (e
->flags
.kernelp
) {
5313 msg
[j
].attr
.header
.opcode
= NNPFS_MSG_INSTALLATTR
;
5314 node
= &msg
[j
].attr
.node
;
5317 msg
[j
].node
.header
.opcode
= NNPFS_MSG_INSTALLNODE
;
5318 node
= &msg
[j
].node
.node
;
5319 parent
= &msg
[j
].node
.parent_handle
;
5320 e
->flags
.kernelp
= TRUE
;
5321 strlcpy (msg
[j
].node
.name
, bs
.names
[i
],
5322 sizeof(msg
[j
].node
.name
));
5324 node
->tokens
= tokens
;
5327 * Don't install symlink since they might be
5331 if (e
->status
.FileType
!= TYPE_LINK
) {
5332 fcacheentry2nnpfsnode (&e
->fid
,
5336 parent_entry
->acccache
,
5337 FCACHE2NNPFSNODE_ALL
);
5340 *parent
= *(struct nnpfs_handle
*) &parent_entry
->fid
;
5341 msg
[j
].attr
.flag
= 0;
5344 if (prefered_entry
!= e
)
5349 * Install if there is no error and we have something to install
5352 if (ret
== 0 && j
!= 0)
5353 ret
= nnpfs_send_message_multiple_list (kernel_fd
,
5354 (struct nnpfs_message_header
*) msg
,
5357 /* We have what we wanted, ignore errors */
5358 if (ret
&& i
> 0 && prefered_entry
)
5366 for (i
= 0 ; i
< bs
.len
&& ret
== 0; i
++)
5372 arla_warnx (ADEBFCACHE
, "get_attr_bulk: returned %d", ret
);
5379 * fetch attributes for the note `entry' with the rights `ce'. If
5380 * `parent_entry' is not NULL, it is used for doing bulkstatus when
5381 * guess is necessary. If there is a named associated with `entry' it
5382 * should be filled into `prefered_name' as that will be used for
5383 * guessing that nodes should be bulkstat:ed.
5385 * If there is no bulkstatus done, a plain FetchStatus is done.
5389 fcache_verify_attr (FCacheEntry
*entry
, FCacheEntry
*parent
,
5390 const char *prefered_name
, CredCacheEntry
* ce
)
5394 if (dynroot_is_dynrootp (entry
))
5395 return dynroot_get_attr (entry
, ce
);
5397 if (entry
->flags
.usedp
5398 && entry
->flags
.attrp
5400 && findaccess (ce
->cred
, entry
->acccache
, &ae
) == TRUE
)
5402 arla_warnx (ADEBFCACHE
, "fcache_get_attr: have attr");
5403 fcache_counter
.fetch_attr_cached
++;
5408 * XXX is this right ?
5409 * Dont ask fileserver if this file is deleted
5411 if (entry
->flags
.silly
) {
5412 entry
->tokens
|= NNPFS_ATTR_R
;
5413 entry
->flags
.attrp
= TRUE
;
5417 if (connected_mode
== DISCONNECTED
) {
5418 if (entry
->flags
.attrp
) {
5420 findaccess(ce
->cred
, entry
->acccache
, &ae
);
5421 ae
->cred
= ce
->cred
;
5422 ae
->access
= 0x7f; /* XXXDISCO */
5430 * If there is no parent, `entry' is a root-node, or the parent is
5431 * un-initialized, don't bother bulkstatus.
5434 && entry
->fid
.fid
.Vnode
!= 1
5435 && entry
->fid
.fid
.Unique
!= 1
5436 && !entry
->flags
.mountp
5437 && !entry
->flags
.fake_mp
5438 && entry
->parent
.Cell
!= 0
5439 && entry
->parent
.fid
.Volume
!= 0
5440 && entry
->parent
.fid
.Vnode
!= 0
5441 && entry
->parent
.fid
.Unique
!= 0)
5444 * Check if the entry is used, that means that
5445 * there is greater chance that we we'll succeed
5446 * when doing bulkstatus.
5449 if (parent
->hits
++ > fcache_bulkstatus_num
&&
5450 parent
->flags
.datausedp
) {
5453 arla_warnx (ADEBFCACHE
, "fcache_get_attr: doing bulk get_attr");
5455 error
= get_attr_bulk (parent
,
5458 /* magic calculation when we are going to do next bulkstat */
5467 * We got here because the bulkstatus failed, didn't want to do a
5468 * bulkstatus or we didn't get a parent for the entry
5471 arla_warnx (ADEBFCACHE
, "fcache_get_attr: doing read_attr");
5473 return read_attr (entry
, ce
);
5479 * Make sure that `e' has attributes and that they are up-to-date.
5480 * `e' must be write-locked.
5484 do_read_data(FCacheEntry
*e
, CredCacheEntry
*ce
,
5485 uint64_t offset
, uint64_t end
)
5487 int ret
= ARLA_CALL_DEAD
;
5488 fs_server_context context
;
5489 ConnCacheEntry
*conn
;
5491 if (connected_mode
== DISCONNECTED
)
5494 ret
= init_fs_context(e
, ce
, &context
);
5498 for (conn
= find_first_fs (&context
);
5500 conn
= find_next_fs (&context
, conn
, ret
)) {
5502 ret
= read_data(e
, conn
, ce
, find_partition(&context
),
5504 } while (!ret
&& !fcache_have_wanted(e
, offset
, end
));
5505 if (!try_next_fs (ret
, &e
->fid
))
5508 free_fs_server_context (&context
);
5510 if (host_downp(ret
))
5516 * Make sure that `e' has file data and is up-to-date.
5520 fcache_verify_data(FCacheEntry
*e
, CredCacheEntry
*ce
,
5521 uint64_t offset
, uint64_t end
)
5523 ConnCacheEntry
*conn
= NULL
;
5525 fs_server_context context
;
5527 assert (e
->flags
.usedp
);
5528 AssertExclLocked(&e
->lock
);
5530 if (dynroot_is_dynrootp (e
))
5531 return dynroot_get_data (e
, ce
);
5533 /* Don't get data for deleted files */
5534 if (e
->flags
.silly
) {
5535 if (fcache_have_wanted(e
, offset
, end
))
5541 if (e
->flags
.attrp
&& uptodatep(e
)) {
5543 /* For directories we have all data or no data at all */
5544 if (e
->status
.FileType
== TYPE_DIR
5545 && block_any(e
) != BLOCK_NONE
) /* XXX */
5548 if (fcache_have_wanted(e
, offset
, end
)) {
5549 fcache_counter
.fetch_data_cached
++;
5552 return do_read_data(e
, ce
, offset
, end
);
5555 ret
= do_read_attr (e
, ce
, &conn
, &context
);
5559 if (fcache_have_wanted(e
, offset
, end
)) {
5560 fcache_counter
.fetch_data_cached
++;
5561 free_fs_server_context (&context
);
5566 ret
= read_data(e
, conn
, ce
, find_partition(&context
),
5568 } while (!ret
&& !fcache_have_wanted(e
, offset
, end
));
5570 free_fs_server_context (&context
);
5575 * Fetch `fid' with data, returning the cache entry in `res'.
5576 * note that `fid' might change.
5580 fcache_get_data(FCacheEntry
**e
, CredCacheEntry
**ce
,
5581 uint64_t wanted_offset
, uint64_t wanted_end
)
5585 if ((*e
)->flags
.fake_mp
) {
5587 FCacheEntry
*new_root
;
5589 ret
= resolve_mp(*e
, &new_fid
, ce
);
5593 ret
= fcache_get (&new_root
, new_fid
, *ce
);
5597 ret
= fcache_verify_attr (new_root
, NULL
, NULL
, *ce
);
5599 fcache_release (new_root
);
5602 (*e
)->flags
.fake_mp
= FALSE
;
5603 (*e
)->flags
.mountp
= TRUE
;
5604 (*e
)->status
.FileType
= TYPE_LINK
;
5605 update_fid ((*e
)->fid
, *e
, new_fid
, new_root
);
5606 fcache_release (*e
);
5608 install_attr (*e
, FCACHE2NNPFSNODE_ALL
);
5611 if (wanted_end
== 0) {
5613 * XXX remove this case, attr should either be known already
5614 * here, or we should just fetch `whole file'/next block.
5617 ret
= fcache_verify_attr (*e
, NULL
, NULL
, *ce
);
5621 /* if ((*e)->usage == 0 || !uptodatep(*e)) */
5622 wanted_end
= fcache_get_status_length(&(*e
)->status
);
5625 ret
= fcache_verify_data(*e
, *ce
, wanted_offset
, wanted_end
);
5630 * Helper function for followmountpoint.
5631 * Given the contents of a mount-point, figure out the cell and volume name.
5633 * ``mp'' must be writeable and should not be used afterwards.
5634 * ``*volname'' is a pointer to somewhere in the mp string.
5635 * ``cell'' should be set before function is called to default cell.
5639 parse_mountpoint (char *mp
, size_t len
, int32_t *cell
, char **volname
)
5644 colon
= strchr (mp
, ':');
5645 if (colon
!= NULL
) {
5647 *cell
= cell_name2num (mp
+ 1);
5658 * Used by followmountpoint to figure out what clone of a volume
5661 * Given a `volname', `cell', it uses the given `ce', `mount_symbol'
5662 * and `parent_type' to return a volume id in `volume'.
5667 * BK + "backup" -> fail
5673 * this_type = "" | "readonly" | "backup"
5674 * parent_type = RW | RO | BK
5675 * mount_symbol = "#" | "%"
5679 find_volume (const char *volname
, int32_t cell
,
5680 CredCacheEntry
*ce
, char mount_symbol
, int parent_type
,
5681 uint32_t *volid
, VolCacheEntry
**ve
)
5687 res
= volcache_getbyname (volname
, cell
, ce
, ve
, &this_type
);
5691 assert (this_type
== RWVOL
||
5692 this_type
== ROVOL
||
5693 this_type
== BACKVOL
);
5695 if (this_type
== ROVOL
) {
5696 if (!((*ve
)->entry
.flags
& VLF_ROEXISTS
)) {
5697 volcache_free (*ve
);
5700 result_type
= ROVOL
;
5701 } else if (this_type
== BACKVOL
&& parent_type
== BACKVOL
) {
5702 volcache_free (*ve
);
5704 } else if (this_type
== BACKVOL
) {
5705 if (!((*ve
)->entry
.flags
& VLF_BOEXISTS
)) {
5706 volcache_free (*ve
);
5709 result_type
= BACKVOL
;
5710 } else if (this_type
== RWVOL
&&
5711 parent_type
!= RWVOL
&&
5712 mount_symbol
== '#') {
5713 if ((*ve
)->entry
.flags
& VLF_ROEXISTS
)
5714 result_type
= ROVOL
;
5715 else if ((*ve
)->entry
.flags
& VLF_RWEXISTS
)
5716 result_type
= RWVOL
;
5718 volcache_free (*ve
);
5722 if ((*ve
)->entry
.flags
& VLF_RWEXISTS
)
5723 result_type
= RWVOL
;
5724 else if ((*ve
)->entry
.flags
& VLF_ROEXISTS
)
5725 result_type
= ROVOL
;
5727 volcache_free (*ve
);
5731 *volid
= (*ve
)->entry
.volumeId
[result_type
];
5736 * Set `fid' to point to the root of the volume pointed to by the
5737 * mount-point in (buf, len).
5739 * If succesful, `fid' will be updated to the root of the volume, and
5740 * `ce' will point to a cred in the new cell.
5744 get_root_of_volume (VenusFid
*fid
, const VenusFid
*parent
,
5745 VolCacheEntry
*volume
,
5746 CredCacheEntry
**ce
,
5747 char *buf
, size_t len
)
5749 VenusFid oldfid
= *fid
;
5754 long parent_type
, voltype
;
5761 res
= parse_mountpoint (buf
, len
, &cell
, &volname
);
5766 * If this is a cross-cell mountpoint we need new credentials.
5769 if ((*ce
)->cell
!= cell
) {
5770 CredCacheEntry
*new_ce
;
5772 new_ce
= cred_get(cell
, (*ce
)->cred
, CRED_ANY
);
5779 parent_type
= getvoltype (fid
->fid
.Volume
, volume
);
5780 mount_symbol
= *buf
;
5782 res
= find_volume (volname
, cell
, *ce
, mount_symbol
,
5783 parent_type
, &volid
, &ve
);
5788 * Create the new fid. The root of a volume always has
5789 * (Vnode, Unique) = (1,1)
5793 fid
->fid
.Volume
= volid
;
5794 fid
->fid
.Vnode
= fid
->fid
.Unique
= 1;
5797 * Check if we are looking up ourself, if we are, just return.
5800 if (VenusFid_cmp(fid
, parent
) == 0) {
5805 res
= fcache_get (&e
, *fid
, *ce
);
5812 * Root nodes are a little bit special. We keep track of
5813 * their parent in `parent' so that `..' can be handled
5817 e
->flags
.vol_root
= TRUE
;
5818 e
->parent
= *parent
;
5820 voltype
= getvoltype (fid
->fid
.Volume
, ve
);
5821 if (ve
->parent
[voltype
].volume
== NULL
) {
5822 ve
->parent
[voltype
].fid
= *parent
;
5823 ve
->parent
[voltype
].mp_fid
= oldfid
;
5825 volcache_volref (ve
, volume
, voltype
);
5832 * If this entry is a mount point, set the fid data to
5833 * the root directory of the volume it's pointing at,
5834 * otherwise just leave it.
5836 * Mount points are symbol links with the following contents:
5838 * '#' | '%' [ cell ':' ] volume-name [ '.' ]
5840 * This function tries to do a minimal amount of work. It always has
5841 * to fetch the attributes of `fid' and if it's a symbolic link, the
5846 followmountpoint (VenusFid
*fid
, const VenusFid
*parent
, FCacheEntry
*parent_e
,
5847 CredCacheEntry
**ce
)
5853 * Get the node for `fid' and verify that it's a symbolic link
5854 * with the correct bits. Otherwise, just return the old
5855 * `fid' without any change.
5858 ret
= fcache_get (&e
, *fid
, *ce
);
5862 e
->parent
= *parent
;
5863 ret
= fcache_verify_attr (e
, parent_e
, NULL
, *ce
);
5869 if (e
->flags
.mountp
)
5870 ret
= resolve_mp(e
, fid
, ce
);
5877 * actually resolve a mount-point
5881 resolve_mp(FCacheEntry
*e
, VenusFid
*ret_fid
, CredCacheEntry
**ce
)
5883 VenusFid fid
= e
->fid
;
5889 assert(e
->flags
.fake_mp
|| e
->flags
.mountp
);
5890 AssertExclLocked(&e
->lock
);
5892 ret
= fcache_verify_data(e
, *ce
, 0,
5893 fcache_get_status_length(&e
->status
));
5897 length
= fcache_get_status_length(&e
->status
);
5899 ret
= abuf_create (&the_fbuf
, e
, length
, FBUF_READ
);
5903 buf
= fbuf_buf (&the_fbuf
);
5905 ret
= get_root_of_volume (&fid
, &e
->parent
, e
->volume
,
5908 abuf_end (&the_fbuf
);
5920 print_entry (void *ptr
, void *arg
)
5922 FCacheEntry
*e
= (FCacheEntry
*)ptr
;
5924 arla_log(ADEBVLOG
, "(%d, %u, %u, %u)" "%s%s%s%s"
5925 "%s%s%s%s" "%s%s%s%s" "%s%s%s%s" "%s%s%s usage: %llu",
5927 e
->fid
.fid
.Volume
, e
->fid
.fid
.Vnode
, e
->fid
.fid
.Unique
,
5929 e
->flags
.usedp
?" used":"",
5930 e
->flags
.attrp
?" attr":"",
5931 e
->usage
!= 0 ?" data":"",
5932 e
->flags
.attrusedp
?" attrused":"",
5934 e
->flags
.datausedp
?" dataused":"",
5935 e
->flags
.extradirp
?" extradir":"",
5936 e
->flags
.mountp
?" mount":"",
5937 e
->flags
.kernelp
?" kernel":"",
5939 e
->flags
.sentenced
?" sentenced":"",
5940 e
->flags
.stale
?" stale":"",
5941 e
->flags
.dirtied
?" dirtied":"",
5942 e
->flags
.silly
?" silly":"",
5944 e
->flags
.fake_mp
? " fake mp" : "",
5945 e
->flags
.vol_root
? " vol root" : "",
5946 e
->flags
.waiters
? " waiters" : "",
5947 e
->flags
.gcp
? " gc" : "",
5949 e
->flags
.locked
? " locked" : "",
5950 e
->flags
.lockwait
? " lockwait" : "",
5951 e
->flags
.appended
? " appended" : "",
5952 (unsigned long long)e
->usage
);
5962 fcache_status (void)
5964 arla_log(ADEBVLOG
, "%lu (%lu-/%lu)-%lu) files"
5965 "%lu (%lu-%lu) bytes\n",
5966 usedvnodes
, lowvnodes
, current_vnodes
, highvnodes
,
5967 (long)usedbytes
, (long)lowbytes
, (long)highbytes
);
5968 hashtabforeach (hashtab
, print_entry
, NULL
);
5972 * Update cache usage of entry by adding `nblocks' times blocksize and
5973 * update accounting accordingly.
5977 fcache_update_usage(FCacheEntry
*e
, int nblocks
)
5979 int64_t diff
= ((int64_t)blocksize
) * nblocks
;
5982 /* AssertExclLocked(&e->lock); */
5985 ret
= fcache_need_bytes(diff
);
5990 assert(e
->usage
<= usedbytes
);
5991 assert(e
->usage
>= 0);
5998 * Mark the data range's presence in cache according to 'have'
5999 * Cache usage is updated, this may cause gc.
6003 fcache_set_have(FCacheEntry
*entry
, uint64_t offset
, uint64_t end
)
6007 AssertExclLocked(&entry
->lock
);
6009 assert(offset
<= end
);
6011 off
= block_offset(offset
);
6013 if (!fcache_block_exists(entry
, off
)) {
6014 int ret
= create_block(entry
, off
);
6019 } while (off
< end
);
6025 * Set new length of entry, and note it's all in cache
6029 fcache_set_have_all(FCacheEntry
*e
, uint64_t len
)
6031 AssertExclLocked(&e
->lock
);
6032 fcache_set_status_length(&e
->status
, len
);
6036 * Request an ACL and put it in opaque
6040 getacl(VenusFid fid
,
6045 ConnCacheEntry
*conn
;
6046 AFSFetchStatus status
;
6049 fs_server_context context
;
6054 if (connected_mode
!= CONNECTED
)
6057 ret
= fcache_get (&dire
, fid
, ce
);
6059 arla_warn (ADEBFCACHE
, ret
, "fcache_get");
6063 ret
= init_fs_context(dire
, ce
, &context
);
6067 for (conn
= find_first_fs (&context
);
6069 conn
= find_next_fs (&context
, conn
, ret
)) {
6071 ret
= RXAFS_FetchACL (conn
->connection
, &fid
.fid
,
6072 opaque
, &status
, &volsync
);
6079 if (!try_next_fs (ret
, &fid
))
6083 arla_warn (ADEBFCACHE
, ret
, "FetchACL");
6086 update_entry (dire
, &status
, NULL
, &volsync
,
6088 else if (host_downp(ret
))
6091 free_fs_server_context (&context
);
6092 fcache_release (dire
);
6097 * Store the ACL read from opaque
6099 * If the function return 0, ret_e is set to the dir-entry and must
6100 * be fcache_released().
6104 setacl(VenusFid fid
,
6107 FCacheEntry
**ret_e
)
6110 ConnCacheEntry
*conn
;
6111 AFSFetchStatus status
;
6114 fs_server_context context
;
6116 if (connected_mode
!= CONNECTED
)
6119 ret
= fcache_get (&dire
, fid
, ce
);
6121 arla_warn (ADEBFCACHE
, ret
, "fcache_get");
6125 ret
= init_fs_context(dire
, ce
, &context
);
6129 for (conn
= find_first_fs (&context
);
6131 conn
= find_next_fs (&context
, conn
, ret
)) {
6132 ret
= RXAFS_StoreACL (conn
->connection
, &fid
.fid
,
6133 opaque
, &status
, &volsync
);
6134 if (!try_next_fs (ret
, &fid
))
6138 arla_warn (ADEBFCACHE
, ret
, "StoreACL");
6141 update_entry (dire
, &status
, NULL
, &volsync
,
6143 else if (host_downp(ret
))
6146 free_fs_server_context (&context
);
6152 fcache_release (dire
);
6158 * Request volume status
6162 getvolstat(VenusFid fid
, CredCacheEntry
*ce
,
6163 AFSFetchVolumeStatus
*volstat
,
6164 char *volumename
, size_t volumenamesz
,
6169 ConnCacheEntry
*conn
;
6171 fs_server_context context
;
6173 if (connected_mode
!= CONNECTED
)
6176 ret
= fcache_get (&dire
, fid
, ce
);
6178 arla_warn (ADEBFCACHE
, ret
, "fcache_get");
6182 ret
= init_fs_context(dire
, ce
, &context
);
6186 for (conn
= find_first_fs (&context
);
6188 conn
= find_next_fs (&context
, conn
, ret
)) {
6189 ret
= RXAFS_GetVolumeStatus (conn
->connection
, fid
.fid
.Volume
,
6190 volstat
, volumename
, offlinemsg
,
6192 if (!try_next_fs (ret
, &fid
))
6196 arla_warn (ADEBFCACHE
, ret
, "GetVolumeStatus");
6197 free_fs_server_context (&context
);
6198 if (host_downp(ret
))
6200 if (ret
== 0 && volumename
[0] == '\0') {
6201 if (volcache_getname (fid
.fid
.Volume
, fid
.Cell
,
6202 volumename
, volumenamesz
) == -1)
6203 strlcpy(volumename
, "<unknown>", volumenamesz
);
6206 fcache_release (dire
);
6211 * Store volume status
6215 setvolstat(VenusFid fid
, CredCacheEntry
*ce
,
6216 AFSStoreVolumeStatus
*volstat
,
6222 ConnCacheEntry
*conn
;
6224 fs_server_context context
;
6226 if (connected_mode
!= CONNECTED
)
6229 ret
= fcache_get (&dire
, fid
, ce
);
6231 arla_warn (ADEBFCACHE
, ret
, "fcache_get");
6235 ret
= init_fs_context(dire
, ce
, &context
);
6239 for (conn
= find_first_fs (&context
);
6241 conn
= find_next_fs (&context
, conn
, ret
)) {
6242 ret
= RXAFS_SetVolumeStatus (conn
->connection
, fid
.fid
.Volume
,
6243 volstat
, volumename
, offlinemsg
,
6245 if (!try_next_fs (ret
, &fid
))
6249 if (host_downp(ret
))
6251 arla_warn (ADEBFCACHE
, ret
, "SetVolumeStatus");
6253 free_fs_server_context (&context
);
6255 fcache_release (dire
);
6260 * Get `fbuf' from `centry'
6262 * Assume that data is valid and `centry' is exclusive locked.
6266 fcache_get_fbuf (FCacheEntry
*centry
, fbuf
*fbuf
, int fbuf_flags
)
6270 AssertExclLocked(¢ry
->lock
);
6272 len
= fcache_get_status_length(¢ry
->status
);
6273 return abuf_create(fbuf
, centry
, len
, fbuf_flags
);
6281 sum_node (List
*list
, Listitem
*li
, void *arg
)
6284 FCacheEntry
*e
= listdata (li
);
6286 if (e
!= CLEANER_MARKER
)
6294 fcache_calculate_usage (void)
6298 listiter (kernel_node_lru
, sum_node
, &size
);
6299 listiter (node_lru
, sum_node
, &size
);
6309 fcache_realfid (const FCacheEntry
*entry
)
6311 if (entry
->flags
.vol_root
6312 || (entry
->fid
.fid
.Vnode
== 1 && entry
->fid
.fid
.Unique
== 1)) {
6313 long voltype
= getvoltype(entry
->fid
.fid
.Volume
, entry
->volume
);
6314 return &entry
->volume
->parent
[voltype
].mp_fid
;
6325 check_dir (List
*list
, Listitem
*li
, void *arg
)
6327 FCacheEntry
*e
= listdata (li
);
6332 if (e
== CLEANER_MARKER
)
6335 if (fcache_islocked(e
))
6338 fcache_lock(e
, TRUE
);
6340 len
= fcache_get_status_length(&e
->status
);
6341 if (!fcache_have_wanted(e
, 0, len
))
6344 ret
= fcache_get_fbuf(e
, &the_fbuf
, FBUF_READ
);
6348 ret
= fdir_dirp(&the_fbuf
);
6350 abuf_end (&the_fbuf
);
6352 if (e
->status
.FileType
== TYPE_DIR
)
6364 * Verifies that directories seems to be directories and files doesn't
6365 * seems to be directories, note that false positives are
6366 * possible in the latter of these cases, so this should not be turned on
6371 fcache_check_dirs(void)
6373 listiter (node_lru
, check_dir
, NULL
);
6374 listiter (kernel_node_lru
, check_dir
, NULL
);
6377 struct check_block_arg
{
6382 * As much paranoia as possible.
6386 check_block(List
*list
, Listitem
*li
, void *arg
)
6388 struct check_block_arg
*cba
= (struct check_block_arg
*)arg
;
6389 struct block
*b
= (struct block
*)listdata(li
);
6391 if (b
!= CLEANER_MARKER
) {
6392 assert(b
->lru_le
== li
);
6393 assert(listprev(list
, li
) == cba
->prev
);
6394 assert(block_offset(b
->offset
) == b
->offset
);
6395 assert(b
->node
->flags
.usedp
);
6396 assert(!block_emptyp(b
->node
));
6398 if (list
== kernel_block_lru
) {
6399 assert(b
->flags
.kernelp
);
6400 assert(b
->node
->flags
.kernelp
);
6401 } else if (list
== block_lru
) {
6402 assert(!b
->flags
.kernelp
);
6414 * Verify that the block lists are consistent.
6418 fcache_check_blocks(void)
6420 struct check_block_arg arg
;
6423 listiter(block_lru
, check_block
, &arg
);
6426 listiter(kernel_block_lru
, check_block
, &arg
);