* This software has been released under the terms of the IBM Public
* License. For details, see the LICENSE file in the top-level source
* directory or online at http://www.openafs.org/dl/license10.html
+ *
+ * Portions Copyright (c) 2006 Sine Nomine Associates
*/
/*
#include <afs/ptclient.h> /* need definition of prlist for host.h */
#include "host.h"
+#include "callback.h"
+#ifdef AFS_DEMAND_ATTACH_FS
+#include "../tviced/serialize_state.h"
+#endif /* AFS_DEMAND_ATTACH_FS */
+
extern afsUUID FS_HostUUID;
extern int hostCount;
-int ShowProblems = 1;
-
-/* Maximum number of call backs to break at once, single fid */
-/* There is some debate as to just how large this value should be */
-/* Ideally, it would be very very large, but I am afraid that the */
-/* cache managers will all send in their responses simultaneously, */
-/* thereby swamping the file server. As a result, something like */
-/* 10 or 15 might be a better bet. */
-#define MAX_CB_HOSTS 10
-
-/* max time to break a callback, otherwise client is dead or net is hosed */
-#define MAXCBT 25
-
-#define u_byte unsigned char
+static int ShowProblems = 1;
struct cbcounters cbstuff;
-struct cbstruct {
- struct host *hp;
- afs_uint32 thead;
-};
-
-struct FileEntry {
- afs_uint32 vnode;
- afs_uint32 unique;
- afs_uint32 volid;
- afs_uint32 fnext;
- afs_uint32 ncbs;
- afs_uint32 firstcb;
- afs_uint32 status;
- afs_uint32 spare;
-} *FE; /* Don't use FE[0] */
-#define FE_LATER 0x1
-
-struct CallBack {
- afs_uint32 cnext; /* Next call back entry */
- afs_uint32 fhead; /* Head of this call back chain */
- u_byte thead; /* Head of timeout chain */
- u_byte status; /* Call back status; see definitions, below */
- afs_uint32 hhead; /* Head of host table chain */
- afs_uint32 tprev, tnext; /* Timeout chain */
- afs_uint32 hprev, hnext; /* Chain from host table */
- unsigned short spare; /* make it a multiple of 32 bits. */
-} *CB; /* Don't use CB[0] */
-
-/* status values for status field of CallBack structure */
-#define CB_NORMAL 1 /* Normal call back */
-#define CB_DELAYED 2 /* Delayed call back due to rpc problems.
- * The call back entry will be added back to the
- * host list at the END of the list, so that
- * searching backwards in the list will find all
- * the (consecutive)host. delayed call back entries */
-#define CB_VOLUME 3 /* Callback for a volume */
-#define CB_BULK 4 /* Normal callbacks, handed out from FetchBulkStatus */
-
-/* call back indices to pointers, and vice-versa */
-#define itocb(i) ((i)?CB+(i):0)
-#define cbtoi(cbp) (!(cbp)?0:(cbp)-CB)
-
-/* file entry indices to pointers, and vice-versa */
-#define itofe(i) ((i)?FE+(i):0)
-#define fetoi(fep) (!(fep)?0:(fep)-FE)
-
-/* Timeouts: there are 128 possible timeout values in effect at any
- * given time. Each timeout represents timeouts in an interval of 128
- * seconds. So the maximum timeout for a call back is 128*128=16384
- * seconds, or 4 1/2 hours. The timeout cleanup stuff is called only
- * if space runs out or by the file server every 5 minutes. This 5
- * minute slack should be allowed for--so a maximum time of 4 hours
- * is safer.
- *
- * Timeouts must be chosen to correspond to an exact multiple
- * of 128, because all times are truncated to a 128 multiple, and
- * timed out if the current truncated time is <= to the truncated time
- * corresponding to the timeout queue.
- */
+static struct FileEntry * FE = NULL; /* don't use FE[0] */
+static struct CallBack * CB = NULL; /* don't use CB[0] */
-/* Unix time to Call Back time, and vice-versa. Call back time is
- in units of 128 seconds, corresponding to time queues. */
-#define CBtime(uxtime) ((uxtime)>>7)
-#define UXtime(cbtime) ((cbtime)<<7)
+static struct CallBack * CBfree = NULL;
+static struct FileEntry * FEfree = NULL;
-/* Given a Unix time, compute the closest Unix time that corresponds to
- a time queue, rounding up */
-#define TimeCeiling(uxtime) (((uxtime)+127)&~127)
/* Time to live for call backs depends upon number of users of the file.
* TimeOuts is indexed by this number/8 (using TimeOut macro). Times
/* minimum time given for a call back */
static int MinTimeOut = (7 * 60);
-#define TimeOutCutoff ((sizeof(TimeOuts)/sizeof(TimeOuts[0]))*8)
-#define TimeOut(nusers) ((nusers)>=TimeOutCutoff? MinTimeOut: TimeOuts[(nusers)>>3])
-
-/* time out at server is 3 minutes more than ws */
-#define ServerBias (3*60)
-
/* Heads of CB queues; a timeout index is 1+index into this array */
-static afs_uint32 timeout[128];
-
-/* Convert cbtime to timeout queue index */
-#define TIndex(cbtime) (((cbtime)&127)+1)
-
-/* Convert cbtime to pointer to timeout queue head */
-#define THead(cbtime) (&timeout[TIndex(cbtime)-1])
+static afs_uint32 timeout[CB_NUM_TIMEOUT_QUEUES];
static afs_int32 tfirst; /* cbtime of oldest unexpired call back time queue */
-/* Normalize index into timeout array so that two such indices will be
- ordered correctly, so that they can be compared to see which times
- sooner, or so that the difference in time out times between them
- can be computed. */
-#define TNorm(index) ((index)<TIndex(tfirst)?(index)+128:(index))
-
-/* This converts a timeout index into the actual time it will expire */
-#define TIndexToTime(index) (UXtime(TNorm(index) - TIndex(tfirst) + tfirst))
-
-
-/* Convert pointer to timeout queue head to index, and vice versa */
-#define ttoi(t) ((t-timeout)+1)
-#define itot(i) ((timeout)+(i-1))
/* 16 byte object get/free routines */
struct object {
struct object *next;
};
-struct VCBParams {
- struct cbstruct cba[MAX_CB_HOSTS]; /* re-entrant storage */
- unsigned int ncbas;
- afs_uint32 thead; /* head of timeout queue for youngest callback */
- struct AFSFid *fid;
-};
-
-struct CallBack *CBfree = 0;
-struct FileEntry *FEfree = 0;
-
/* Prototypes for static routines */
static struct FileEntry *FindFE(register AFSFid * fid);
static struct CallBack *iGetCB(register int *nused);
#define FreeCB(cb) iFreeCB((struct CallBack *)cb, &cbstuff.nCBs)
#define FreeFE(fe) iFreeFE((struct FileEntry *)fe, &cbstuff.nFEs)
+
/* Other protos - move out sometime */
void PrintCB(register struct CallBack *cb, afs_uint32 now);
-#define VHASH 512 /* Power of 2 */
-static afs_uint32 HashTable[VHASH]; /* File entry hash table */
-#define VHash(volume, unique) (((volume)+(unique))&(VHASH-1))
+static afs_uint32 HashTable[FEHASH_SIZE]; /* File entry hash table */
static struct FileEntry *
FindFE(register AFSFid * fid)
register int fei;
register struct FileEntry *fe;
- hash = VHash(fid->Volume, fid->Unique);
+ hash = FEHash(fid->Volume, fid->Unique);
for (fei = HashTable[hash]; fei; fei = fe->fnext) {
fe = itofe(fei);
if (fe->volid == fid->Volume && fe->unique == fid->Unique
if (!host->cblist) {
host->cblist = cb->hnext = cb->hprev = cbtoi(cb);
} else {
- register struct CallBack *hhp = itocb(host->cblist);
+ register struct CallBack *fcb = itocb(host->cblist);
- cb->hprev = hhp->hprev;
- cb->hnext = host->cblist;
- hhp->hprev = (itocb(hhp->hprev)->hnext = cbtoi(cb));
+ cb->hprev = fcb->hprev;
+ cb->hnext = cbtoi(fcb);
+ fcb->hprev = (itocb(fcb->hprev)->hnext = cbtoi(cb));
}
return 0;
}
/* N.B. This one also deletes the CB, and also possibly parent FE, so
* make sure that it is not on any other list before calling this
* routine */
-int Ccdelpt = 0, CcdelB = 0;
+static int Ccdelpt = 0, CcdelB = 0;
static int
CDelPtr(register struct FileEntry *fe, register afs_uint32 * cbp,
FDel(register struct FileEntry *fe)
{
register int fei = fetoi(fe);
- register afs_uint32 *p = &HashTable[VHash(fe->volid, fe->unique)];
+ register afs_uint32 *p = &HashTable[FEHash(fe->volid, fe->unique)];
while (*p && *p != fei)
p = &itofe(*p)->fnext;
return 0;
}
+/* initialize the callback package */
int
InitCallBack(int nblks)
{
tfirst = CBtime(FT_ApproxTime());
/* N.B. The "-1", below, is because
* FE[0] and CB[0] are not used--and not allocated */
- FE = ((struct FileEntry *)(calloc(nblks, sizeof(struct FileEntry)))) - 1;
+ FE = ((struct FileEntry *)(calloc(nblks, sizeof(struct FileEntry))));
if (!FE) {
ViceLog(0, ("Failed malloc in InitCallBack\n"));
assert(0);
}
+ FE--; /* FE[0] is supposed to point to junk */
cbstuff.nFEs = nblks;
while (cbstuff.nFEs)
FreeFE(&FE[cbstuff.nFEs]); /* This is correct */
- CB = ((struct CallBack *)(calloc(nblks, sizeof(struct CallBack)))) - 1;
+ CB = ((struct CallBack *)(calloc(nblks, sizeof(struct CallBack))));
if (!CB) {
ViceLog(0, ("Failed malloc in InitCallBack\n"));
assert(0);
}
+ CB--; /* CB[0] is supposed to point to junk */
cbstuff.nCBs = nblks;
while (cbstuff.nCBs)
FreeCB(&CB[cbstuff.nCBs]); /* This is correct */
fe->unique = fid->Unique;
fe->ncbs = 0;
fe->status = 0;
- hash = VHash(fid->Volume, fid->Unique);
+ hash = FEHash(fid->Volume, fid->Unique);
fe->fnext = HashTable[hash];
HashTable[hash] = fetoi(fe);
}
H_LOCK;
fid.Volume = volume, fid.Vnode = fid.Unique = 0;
- for (hash = 0; hash < VHASH; hash++) {
+ for (hash = 0; hash < FEHASH_SIZE; hash++) {
for (feip = &HashTable[hash]; (fe = itofe(*feip));) {
if (fe->volid == volume) {
register struct CallBack *cbnext;
BreakVolumeCallBacksLater(afs_uint32 volume)
{
int hash;
- afs_int32 *feip;
+ afs_uint32 *feip;
struct FileEntry *fe;
struct CallBack *cb;
struct host *host;
ViceLog(25, ("Setting later on volume %u\n", volume));
H_LOCK;
- for (hash = 0; hash < VHASH; hash++) {
+ for (hash = 0; hash < FEHASH_SIZE; hash++) {
for (feip = &HashTable[hash]; (fe = itofe(*feip)) != NULL; ) {
if (fe->volid == volume) {
register struct CallBack *cbnext;
FSYNC_LOCK;
fe->status |= FE_LATER;
FSYNC_UNLOCK;
- found++;
+ found = 1;
}
feip = &fe->fnext;
}
{
struct AFSFid fid;
int hash;
- afs_int32 *feip;
+ afs_uint32 *feip;
struct CallBack *cb;
struct FileEntry *fe = NULL;
struct FileEntry *myfe = NULL;
/* Pick the first volume we see to clean up */
fid.Volume = fid.Vnode = fid.Unique = 0;
- for (hash = 0; hash < VHASH; hash++) {
+ for (hash = 0; hash < FEHASH_SIZE; hash++) {
for (feip = &HashTable[hash]; (fe = itofe(*feip)) != NULL; ) {
if (fe && (fe->status & FE_LATER)
&& (fid.Volume == 0 || fid.Volume == fe->volid)) {
#ifndef INTERPRET_DUMP
+#ifdef AFS_DEMAND_ATTACH_FS
+/*
+ * demand attach fs
+ * callback state serialization
+ */
+static int cb_stateSaveTimeouts(struct fs_dump_state * state);
+static int cb_stateSaveFEHash(struct fs_dump_state * state);
+static int cb_stateSaveFEs(struct fs_dump_state * state);
+static int cb_stateSaveFE(struct fs_dump_state * state, struct FileEntry * fe);
+static int cb_stateRestoreTimeouts(struct fs_dump_state * state);
+static int cb_stateRestoreFEHash(struct fs_dump_state * state);
+static int cb_stateRestoreFEs(struct fs_dump_state * state);
+static int cb_stateRestoreFE(struct fs_dump_state * state);
+static int cb_stateRestoreCBs(struct fs_dump_state * state, struct FileEntry * fe,
+ struct iovec * iov, int niovecs);
+
+static int cb_stateVerifyFEHash(struct fs_dump_state * state);
+static int cb_stateVerifyFE(struct fs_dump_state * state, struct FileEntry * fe);
+static int cb_stateVerifyFCBList(struct fs_dump_state * state, struct FileEntry * fe);
+static int cb_stateVerifyTimeoutQueues(struct fs_dump_state * state);
+
+static int cb_stateFEToDiskEntry(struct FileEntry *, struct FEDiskEntry *);
+static int cb_stateDiskEntryToFE(struct fs_dump_state * state,
+ struct FEDiskEntry *, struct FileEntry *);
+
+static int cb_stateCBToDiskEntry(struct CallBack *, struct CBDiskEntry *);
+static int cb_stateDiskEntryToCB(struct fs_dump_state * state,
+ struct CBDiskEntry *, struct CallBack *);
+
+static int cb_stateFillHeader(struct callback_state_header * hdr);
+static int cb_stateCheckHeader(struct callback_state_header * hdr);
+
+static int cb_stateAllocMap(struct fs_dump_state * state);
+
+int
+cb_stateSave(struct fs_dump_state * state)
+{
+ int ret = 0;
+
+ AssignInt64(state->eof_offset, &state->hdr->cb_offset);
+
+ /* invalidate callback state header */
+ memset(state->cb_hdr, 0, sizeof(struct callback_state_header));
+ if (fs_stateWriteHeader(state, &state->hdr->cb_offset, state->cb_hdr,
+ sizeof(struct callback_state_header))) {
+ ret = 1;
+ goto done;
+ }
+
+ fs_stateIncEOF(state, sizeof(struct callback_state_header));
+
+ /* dump timeout state */
+ if (cb_stateSaveTimeouts(state)) {
+ ret = 1;
+ goto done;
+ }
+
+ /* dump fe hashtable state */
+ if (cb_stateSaveFEHash(state)) {
+ ret = 1;
+ goto done;
+ }
+
+ /* dump callback state */
+ if (cb_stateSaveFEs(state)) {
+ ret = 1;
+ goto done;
+ }
+
+ /* write the callback state header to disk */
+ cb_stateFillHeader(state->cb_hdr);
+ if (fs_stateWriteHeader(state, &state->hdr->cb_offset, state->cb_hdr,
+ sizeof(struct callback_state_header))) {
+ ret = 1;
+ goto done;
+ }
+
+ done:
+ return ret;
+}
+
+int
+cb_stateRestore(struct fs_dump_state * state)
+{
+ int ret = 0;
+
+ if (fs_stateReadHeader(state, &state->hdr->cb_offset, state->cb_hdr,
+ sizeof(struct callback_state_header))) {
+ ret = 1;
+ goto done;
+ }
+
+ if (cb_stateCheckHeader(state->cb_hdr)) {
+ ret = 1;
+ goto done;
+ }
+
+ if (cb_stateAllocMap(state)) {
+ ret = 1;
+ goto done;
+ }
+
+ if (cb_stateRestoreTimeouts(state)) {
+ ret = 1;
+ goto done;
+ }
+
+ if (cb_stateRestoreFEHash(state)) {
+ ret = 1;
+ goto done;
+ }
+
+ /* restore FEs and CBs from disk */
+ if (cb_stateRestoreFEs(state)) {
+ ret = 1;
+ goto done;
+ }
+
+ /* restore the timeout queue heads */
+ tfirst = state->cb_hdr->tfirst;
+
+ done:
+ return ret;
+}
+
+int
+cb_stateRestoreIndices(struct fs_dump_state * state)
+{
+ int i, ret = 0;
+ struct FileEntry * fe;
+ struct CallBack * cb;
+
+ /* restore indices in the FileEntry structures */
+ for (i = 1; i < state->fe_map.len; i++) {
+ if (state->fe_map.entries[i].new_idx) {
+ fe = itofe(state->fe_map.entries[i].new_idx);
+
+ /* restore the fe->fnext entry */
+ if (fe_OldToNew(state, fe->fnext, &fe->fnext)) {
+ ret = 1;
+ goto done;
+ }
+
+ /* restore the fe->firstcb entry */
+ if (cb_OldToNew(state, fe->firstcb, &fe->firstcb)) {
+ ret = 1;
+ goto done;
+ }
+ }
+ }
+
+ /* restore indices in the CallBack structures */
+ for (i = 1; i < state->cb_map.len; i++) {
+ if (state->cb_map.entries[i].new_idx) {
+ cb = itocb(state->cb_map.entries[i].new_idx);
+
+ /* restore the cb->cnext entry */
+ if (cb_OldToNew(state, cb->cnext, &cb->cnext)) {
+ ret = 1;
+ goto done;
+ }
+
+ /* restore the cb->fhead entry */
+ if (fe_OldToNew(state, cb->fhead, &cb->fhead)) {
+ ret = 1;
+ goto done;
+ }
+
+ /* restore the cb->hhead entry */
+ if (h_OldToNew(state, cb->hhead, &cb->hhead)) {
+ ret = 1;
+ goto done;
+ }
+
+ /* restore the cb->tprev entry */
+ if (cb_OldToNew(state, cb->tprev, &cb->tprev)) {
+ ret = 1;
+ goto done;
+ }
+
+ /* restore the cb->tnext entry */
+ if (cb_OldToNew(state, cb->tnext, &cb->tnext)) {
+ ret = 1;
+ goto done;
+ }
+
+ /* restore the cb->hprev entry */
+ if (cb_OldToNew(state, cb->hprev, &cb->hprev)) {
+ ret = 1;
+ goto done;
+ }
+
+ /* restore the cb->hnext entry */
+ if (cb_OldToNew(state, cb->hnext, &cb->hnext)) {
+ ret = 1;
+ goto done;
+ }
+ }
+ }
+
+ /* restore the timeout queue head indices */
+ for (i = 0; i < state->cb_timeout_hdr->records; i++) {
+ if (cb_OldToNew(state, timeout[i], &timeout[i])) {
+ ret = 1;
+ goto done;
+ }
+ }
+
+ /* restore the FE hash table queue heads */
+ for (i = 0; i < state->cb_fehash_hdr->records; i++) {
+ if (fe_OldToNew(state, HashTable[i], &HashTable[i])) {
+ ret = 1;
+ goto done;
+ }
+ }
+
+ done:
+ return ret;
+}
+
+int
+cb_stateVerify(struct fs_dump_state * state)
+{
+ int ret = 0;
+
+ if (cb_stateVerifyFEHash(state)) {
+ ret = 1;
+ }
+
+ if (cb_stateVerifyTimeoutQueues(state)) {
+ ret = 1;
+ }
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateVerifyFEHash(struct fs_dump_state * state)
+{
+ int ret = 0, i;
+ struct FileEntry * fe;
+ afs_uint32 fei, chain_len;
+
+ for (i = 0; i < FEHASH_SIZE; i++) {
+ chain_len = 0;
+ for (fei = HashTable[i], fe = itofe(fei);
+ fe;
+ fei = fe->fnext, fe = itofe(fei)) {
+ if (fei > cbstuff.nblks) {
+ ViceLog(0, ("cb_stateVerifyFEHash: error: index out of range (fei=%d)\n", fei));
+ ret = 1;
+ break;
+ }
+ if (cb_stateVerifyFE(state, fe)) {
+ ret = 1;
+ }
+ if (chain_len > FS_STATE_FE_MAX_HASH_CHAIN_LEN) {
+ ViceLog(0, ("cb_stateVerifyFEHash: error: hash chain %d length exceeds %d; assuming there's a loop\n",
+ i, FS_STATE_FE_MAX_HASH_CHAIN_LEN));
+ ret = 1;
+ break;
+ }
+ chain_len++;
+ }
+ }
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateVerifyFE(struct fs_dump_state * state, struct FileEntry * fe)
+{
+ int ret = 0;
+
+ if ((fe->firstcb && !fe->ncbs) ||
+ (!fe->firstcb && fe->ncbs)) {
+ ViceLog(0, ("cb_stateVerifyFE: error: fe->firstcb does not agree with fe->ncbs (fei=%d, fe->firstcb=%d, fe->ncbs=%d)\n",
+ fetoi(fe), fe->firstcb, fe->ncbs));
+ ret = 1;
+ }
+ if (cb_stateVerifyFCBList(state, fe)) {
+ ViceLog(0, ("cb_stateVerifyFE: error: FCBList failed verification (fei=%d)\n", fetoi(fe)));
+ ret = 1;
+ }
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateVerifyFCBList(struct fs_dump_state * state, struct FileEntry * fe)
+{
+ int ret = 0;
+ afs_uint32 cbi, fei, chain_len = 0;
+ struct CallBack * cb;
+
+ fei = fetoi(fe);
+
+ for (cbi = fe->firstcb, cb = itocb(cbi);
+ cb;
+ cbi = cb->cnext, cb = itocb(cbi)) {
+ if (cbi > cbstuff.nblks) {
+ ViceLog(0, ("cb_stateVerifyFCBList: error: list index out of range (cbi=%d, ncbs=%d)\n",
+ cbi, cbstuff.nblks));
+ ret = 1;
+ goto done;
+ }
+ if (cb->fhead != fei) {
+ ViceLog(0, ("cb_stateVerifyFCBList: error: cb->fhead != fei (fei=%d, cb->fhead=%d)\n",
+ fei, cb->fhead));
+ ret = 1;
+ }
+ if (chain_len > FS_STATE_FCB_MAX_LIST_LEN) {
+ ViceLog(0, ("cb_stateVerifyFCBList: error: list length exceeds %d (fei=%d); assuming there's a loop\n",
+ FS_STATE_FCB_MAX_LIST_LEN, fei));
+ ret = 1;
+ goto done;
+ }
+ chain_len++;
+ }
+
+ if (fe->ncbs != chain_len) {
+ ViceLog(0, ("cb_stateVerifyFCBList: error: list length mismatch (len=%d, fe->ncbs=%d)\n",
+ chain_len, fe->ncbs));
+ ret = 1;
+ }
+
+ done:
+ return ret;
+}
+
+int
+cb_stateVerifyHCBList(struct fs_dump_state * state, struct host * host)
+{
+ int ret = 0;
+ afs_uint32 hi, chain_len, cbi;
+ struct CallBack *cb, *ncb;
+
+ hi = h_htoi(host);
+ chain_len = 0;
+
+ for (cbi = host->cblist, cb = itocb(cbi);
+ cb;
+ cbi = cb->hnext, cb = ncb) {
+ if (chain_len && (host->cblist == cbi)) {
+ /* we've wrapped around the circular list, and everything looks ok */
+ break;
+ }
+ if (cb->hhead != hi) {
+ ViceLog(0, ("cb_stateVerifyHCBList: error: incorrect cb->hhead (cbi=%d, h->index=%d, cb->hhead=%d)\n",
+ cbi, hi, cb->hhead));
+ ret = 1;
+ }
+ if (!cb->hprev || !cb->hnext) {
+ ViceLog(0, ("cb_stateVerifyHCBList: error: null index in circular list (cbi=%d, h->index=%d)\n",
+ cbi, hi));
+ ret = 1;
+ goto done;
+ }
+ if ((cb->hprev > cbstuff.nblks) ||
+ (cb->hnext > cbstuff.nblks)) {
+ ViceLog(0, ("cb_stateVerifyHCBList: error: list index out of range (cbi=%d, h->index=%d, cb->hprev=%d, cb->hnext=%d, nCBs=%d)\n",
+ cbi, hi, cb->hprev, cb->hnext, cbstuff.nblks));
+ ret = 1;
+ goto done;
+ }
+ ncb = itocb(cb->hnext);
+ if (cbi != ncb->hprev) {
+ ViceLog(0, ("cb_stateVerifyHCBList: error: corrupt linked list (cbi=%d, h->index=%d)\n",
+ cbi, hi));
+ ret = 1;
+ goto done;
+ }
+ if (chain_len > FS_STATE_HCB_MAX_LIST_LEN) {
+ ViceLog(0, ("cb_stateVerifyFCBList: error: list length exceeds %d (h->index=%d); assuming there's a loop\n",
+ FS_STATE_HCB_MAX_LIST_LEN, hi));
+ ret = 1;
+ goto done;
+ }
+ chain_len++;
+ }
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateVerifyTimeoutQueues(struct fs_dump_state * state)
+{
+ int ret = 0, i;
+ afs_uint32 cbi, chain_len;
+ struct CallBack *cb, *ncb;
+
+ for (i = 0; i < CB_NUM_TIMEOUT_QUEUES; i++) {
+ chain_len = 0;
+ for (cbi = timeout[i], cb = itocb(cbi);
+ cb;
+ cbi = cb->tnext, cb = ncb) {
+ if (chain_len && (cbi == timeout[i])) {
+ /* we've wrapped around the circular list, and everything looks ok */
+ break;
+ }
+ if (cbi > cbstuff.nblks) {
+ ViceLog(0, ("cb_stateVerifyTimeoutQueues: error: list index out of range (cbi=%d, tindex=%d)\n",
+ cbi, i));
+ ret = 1;
+ break;
+ }
+ if (itot(cb->thead) != &timeout[i]) {
+ ViceLog(0, ("cb_stateVerifyTimeoutQueues: error: cb->thead points to wrong timeout queue (tindex=%d, cbi=%d, cb->thead=%d)\n",
+ i, cbi, cb->thead));
+ ret = 1;
+ }
+ if (!cb->tprev || !cb->tnext) {
+ ViceLog(0, ("cb_stateVerifyTimeoutQueues: null index in circular list (cbi=%d, tindex=%d)\n",
+ cbi, i));
+ ret = 1;
+ break;
+ }
+ if ((cb->tprev > cbstuff.nblks) ||
+ (cb->tnext > cbstuff.nblks)) {
+ ViceLog(0, ("cb_stateVerifyTimeoutQueues: list index out of range (cbi=%d, tindex=%d, cb->tprev=%d, cb->tnext=%d, nCBs=%d)\n",
+ cbi, i, cb->tprev, cb->tnext, cbstuff.nblks));
+ ret = 1;
+ break;
+ }
+ ncb = itocb(cb->tnext);
+ if (cbi != ncb->tprev) {
+ ViceLog(0, ("cb_stateVerifyTimeoutQueues: corrupt linked list (cbi=%d, tindex=%d)\n",
+ cbi, i));
+ ret = 1;
+ break;
+ }
+ if (chain_len > FS_STATE_TCB_MAX_LIST_LEN) {
+ ViceLog(0, ("cb_stateVerifyTimeoutQueues: list length exceeds %d (tindex=%d); assuming there's a loop\n",
+ FS_STATE_TCB_MAX_LIST_LEN, i));
+ ret = 1;
+ break;
+ }
+ chain_len++;
+ }
+ }
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateSaveTimeouts(struct fs_dump_state * state)
+{
+ int ret = 0;
+ struct iovec iov[2];
+
+ AssignInt64(state->eof_offset, &state->cb_hdr->timeout_offset);
+
+ memset(state->cb_timeout_hdr, 0, sizeof(struct callback_state_fehash_header));
+ state->cb_timeout_hdr->magic = CALLBACK_STATE_TIMEOUT_MAGIC;
+ state->cb_timeout_hdr->records = CB_NUM_TIMEOUT_QUEUES;
+ state->cb_timeout_hdr->len = sizeof(struct callback_state_timeout_header) +
+ (state->cb_timeout_hdr->records * sizeof(afs_uint32));
+
+ iov[0].iov_base = (char *)state->cb_timeout_hdr;
+ iov[0].iov_len = sizeof(struct callback_state_timeout_header);
+ iov[1].iov_base = (char *)timeout;
+ iov[1].iov_len = sizeof(timeout);
+
+ if (fs_stateSeek(state, &state->cb_hdr->timeout_offset)) {
+ ret = 1;
+ goto done;
+ }
+
+ if (fs_stateWriteV(state, iov, 2)) {
+ ret = 1;
+ goto done;
+ }
+
+ fs_stateIncEOF(state, state->cb_timeout_hdr->len);
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateRestoreTimeouts(struct fs_dump_state * state)
+{
+ int ret = 0, len;
+
+ if (fs_stateReadHeader(state, &state->cb_hdr->timeout_offset,
+ state->cb_timeout_hdr,
+ sizeof(struct callback_state_timeout_header))) {
+ ret = 1;
+ goto done;
+ }
+
+ if (state->cb_timeout_hdr->magic != CALLBACK_STATE_TIMEOUT_MAGIC) {
+ ret = 1;
+ goto done;
+ }
+ if (state->cb_timeout_hdr->records != CB_NUM_TIMEOUT_QUEUES) {
+ ret = 1;
+ goto done;
+ }
+
+ len = state->cb_timeout_hdr->records * sizeof(afs_uint32);
+
+ if (state->cb_timeout_hdr->len !=
+ (sizeof(struct callback_state_timeout_header) + len)) {
+ ret = 1;
+ goto done;
+ }
+
+ if (fs_stateRead(state, timeout, len)) {
+ ret = 1;
+ goto done;
+ }
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateSaveFEHash(struct fs_dump_state * state)
+{
+ int ret = 0;
+ struct iovec iov[2];
+
+ AssignInt64(state->eof_offset, &state->cb_hdr->fehash_offset);
+
+ memset(state->cb_fehash_hdr, 0, sizeof(struct callback_state_fehash_header));
+ state->cb_fehash_hdr->magic = CALLBACK_STATE_FEHASH_MAGIC;
+ state->cb_fehash_hdr->records = FEHASH_SIZE;
+ state->cb_fehash_hdr->len = sizeof(struct callback_state_fehash_header) +
+ (state->cb_fehash_hdr->records * sizeof(afs_uint32));
+
+ iov[0].iov_base = (char *)state->cb_fehash_hdr;
+ iov[0].iov_len = sizeof(struct callback_state_fehash_header);
+ iov[1].iov_base = (char *)HashTable;
+ iov[1].iov_len = sizeof(HashTable);
+
+ if (fs_stateSeek(state, &state->cb_hdr->fehash_offset)) {
+ ret = 1;
+ goto done;
+ }
+
+ if (fs_stateWriteV(state, iov, 2)) {
+ ret = 1;
+ goto done;
+ }
+
+ fs_stateIncEOF(state, state->cb_fehash_hdr->len);
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateRestoreFEHash(struct fs_dump_state * state)
+{
+ int ret = 0, len;
+
+ if (fs_stateReadHeader(state, &state->cb_hdr->fehash_offset,
+ state->cb_fehash_hdr,
+ sizeof(struct callback_state_fehash_header))) {
+ ret = 1;
+ goto done;
+ }
+
+ if (state->cb_fehash_hdr->magic != CALLBACK_STATE_FEHASH_MAGIC) {
+ ret = 1;
+ goto done;
+ }
+ if (state->cb_fehash_hdr->records != FEHASH_SIZE) {
+ ret = 1;
+ goto done;
+ }
+
+ len = state->cb_fehash_hdr->records * sizeof(afs_uint32);
+
+ if (state->cb_fehash_hdr->len !=
+ (sizeof(struct callback_state_fehash_header) + len)) {
+ ret = 1;
+ goto done;
+ }
+
+ if (fs_stateRead(state, HashTable, len)) {
+ ret = 1;
+ goto done;
+ }
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateSaveFEs(struct fs_dump_state * state)
+{
+ int ret = 0;
+ register int fei, hash;
+ register struct FileEntry *fe;
+
+ AssignInt64(state->eof_offset, &state->cb_hdr->fe_offset);
+
+ for (hash = 0; hash < FEHASH_SIZE ; hash++) {
+ for (fei = HashTable[hash]; fei; fei = fe->fnext) {
+ fe = itofe(fei);
+ if (cb_stateSaveFE(state, fe)) {
+ ret = 1;
+ goto done;
+ }
+ }
+ }
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateRestoreFEs(struct fs_dump_state * state)
+{
+ int count, nFEs, ret = 0;
+
+ nFEs = state->cb_hdr->nFEs;
+
+ for (count = 0; count < nFEs; count++) {
+ if (cb_stateRestoreFE(state)) {
+ ret = 1;
+ goto done;
+ }
+ }
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateSaveFE(struct fs_dump_state * state, struct FileEntry * fe)
+{
+ int ret = 0, iovcnt, cbi, idx, len, written = 0;
+ afs_uint32 fei;
+ struct callback_state_entry_header hdr;
+ struct FEDiskEntry fedsk;
+ struct CBDiskEntry cbdsk[16];
+ struct iovec iov[16];
+ struct CallBack *cb;
+
+ fei = fetoi(fe);
+ if (fei > state->cb_hdr->fe_max) {
+ state->cb_hdr->fe_max = fei;
+ }
+
+ memset(&hdr, 0, sizeof(struct callback_state_entry_header));
+
+ if (cb_stateFEToDiskEntry(fe, &fedsk)) {
+ ret = 1;
+ goto done;
+ }
+
+ iov[0].iov_base = (char *)&hdr;
+ len = iov[0].iov_len = sizeof(hdr);
+ iov[1].iov_base = (char *)&fedsk;
+ len += iov[1].iov_len = sizeof(struct FEDiskEntry);
+ iovcnt = 2;
+
+ for (cbi = fe->firstcb, cb = itocb(cbi), idx = 2;
+ cb != NULL;
+ cbi = cb->cnext, cb = itocb(cbi), idx++, hdr.nCBs++) {
+ if (cbi > state->cb_hdr->cb_max) {
+ state->cb_hdr->cb_max = cbi;
+ }
+ if (cb_stateCBToDiskEntry(cb, &cbdsk[idx])) {
+ ret = 1;
+ goto done;
+ }
+ cbdsk[idx].index = cbi;
+ iov[idx].iov_base = (char *)&cbdsk[idx];
+ len += iov[idx].iov_len = sizeof(struct CBDiskEntry);
+ iovcnt++;
+ if ((iovcnt == 16) || (!cb->cnext)) {
+ if (fs_stateWriteV(state, iov, iovcnt)) {
+ ret = 1;
+ goto done;
+ }
+ written = 1;
+ iovcnt = 0;
+ len = 0;
+ }
+ }
+
+ hdr.magic = CALLBACK_STATE_ENTRY_MAGIC;
+ hdr.len = sizeof(hdr) + sizeof(struct FEDiskEntry) +
+ (hdr.nCBs * sizeof(struct CBDiskEntry));
+
+ if (!written) {
+ if (fs_stateWriteV(state, iov, iovcnt)) {
+ ret = 1;
+ goto done;
+ }
+ } else {
+ if (fs_stateWriteHeader(state, &state->eof_offset, &hdr, sizeof(hdr))) {
+ ret = 1;
+ goto done;
+ }
+ }
+
+ fs_stateIncEOF(state, hdr.len);
+
+ if (written) {
+ if (fs_stateSeek(state, &state->eof_offset)) {
+ ret = 1;
+ goto done;
+ }
+ }
+
+ state->cb_hdr->nFEs++;
+ state->cb_hdr->nCBs += hdr.nCBs;
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateRestoreFE(struct fs_dump_state * state)
+{
+ int ret = 0, iovcnt, len, nCBs, idx;
+ struct callback_state_entry_header hdr;
+ struct FEDiskEntry fedsk;
+ struct CBDiskEntry cbdsk[16];
+ struct iovec iov[16];
+ struct FileEntry * fe;
+ struct CallBack * cb;
+
+ iov[0].iov_base = (char *)&hdr;
+ len = iov[0].iov_len = sizeof(hdr);
+ iov[1].iov_base = (char *)&fedsk;
+ len += iov[1].iov_len = sizeof(fedsk);
+ iovcnt = 2;
+
+ if (fs_stateReadV(state, iov, iovcnt)) {
+ ret = 1;
+ goto done;
+ }
+
+ if (hdr.magic != CALLBACK_STATE_ENTRY_MAGIC) {
+ ret = 1;
+ goto done;
+ }
+
+ fe = GetFE();
+ if (fe == NULL) {
+ ViceLog(0, ("cb_stateRestoreFE: ran out of free FileEntry structures\n"));
+ ret = 1;
+ goto done;
+ }
+
+ if (cb_stateDiskEntryToFE(state, &fedsk, fe)) {
+ ret = 1;
+ goto done;
+ }
+
+ if (hdr.nCBs) {
+ for (iovcnt = 0, idx = 0, len = 0, nCBs = 0;
+ nCBs < hdr.nCBs;
+ idx++, nCBs++) {
+ iov[idx].iov_base = (char *)&cbdsk[idx];
+ len += iov[idx].iov_len = sizeof(struct CBDiskEntry);
+ iovcnt++;
+ if ((iovcnt == 16) || (nCBs == hdr.nCBs - 1)) {
+ if (fs_stateReadV(state, iov, iovcnt)) {
+ ret = 1;
+ goto done;
+ }
+ if (cb_stateRestoreCBs(state, fe, iov, iovcnt)) {
+ ret = 1;
+ goto done;
+ }
+ len = 0;
+ iovcnt = 0;
+ }
+ }
+ }
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateRestoreCBs(struct fs_dump_state * state, struct FileEntry * fe,
+ struct iovec * iov, int niovecs)
+{
+ int ret = 0, idx;
+ register struct CallBack * cb;
+ struct CBDiskEntry * cbdsk;
+ afs_uint32 fei;
+
+ fei = fetoi(fe);
+
+ for (idx = 0; idx < niovecs; idx++) {
+ cbdsk = (struct CBDiskEntry *) iov[idx].iov_base;
+ if ((cb = GetCB()) == NULL) {
+ ViceLog(0, ("cb_stateRestoreCBs: ran out of free CallBack structures\n"));
+ ret = 1;
+ goto done;
+ }
+ if (cb_stateDiskEntryToCB(state, cbdsk, cb)) {
+ ViceLog(0, ("cb_stateRestoreCBs: corrupt CallBack disk entry\n"));
+ ret = 1;
+ goto done;
+ }
+ }
+
+ done:
+ return ret;
+}
+
+
+static int
+cb_stateFillHeader(struct callback_state_header * hdr)
+{
+ hdr->stamp.magic = CALLBACK_STATE_MAGIC;
+ hdr->stamp.version = CALLBACK_STATE_VERSION;
+ hdr->tfirst = tfirst;
+ return 0;
+}
+
+static int
+cb_stateCheckHeader(struct callback_state_header * hdr)
+{
+ int ret = 0;
+
+ if (hdr->stamp.magic != CALLBACK_STATE_MAGIC) {
+ ret = 1;
+ } else if (hdr->stamp.version != CALLBACK_STATE_VERSION) {
+ ret = 1;
+ } else if ((hdr->nFEs > cbstuff.nblks) || (hdr->nCBs > cbstuff.nblks)) {
+ ViceLog(0, ("cb_stateCheckHeader: saved callback state larger than callback memory allocation\n"));
+ ret = 1;
+ }
+ return ret;
+}
+
+/* disk entry conversion routines */
+static int
+cb_stateFEToDiskEntry(struct FileEntry * in, struct FEDiskEntry * out)
+{
+ memcpy(&out->fe, in, sizeof(struct FileEntry));
+ out->index = fetoi(in);
+ return 0;
+}
+
+static int
+cb_stateDiskEntryToFE(struct fs_dump_state * state,
+ struct FEDiskEntry * in, struct FileEntry * out)
+{
+ int ret = 0;
+
+ memcpy(out, &in->fe, sizeof(struct FileEntry));
+
+ /* setup FE map entry */
+ if (!in->index || (in->index >= state->fe_map.len)) {
+ ViceLog(0, ("cb_stateDiskEntryToFE: index (%d) out of range",
+ in->index));
+ ret = 1;
+ goto done;
+ }
+ state->fe_map.entries[in->index].old_idx = in->index;
+ state->fe_map.entries[in->index].new_idx = fetoi(out);
+
+ done:
+ return ret;
+}
+
+static int
+cb_stateCBToDiskEntry(struct CallBack * in, struct CBDiskEntry * out)
+{
+ memcpy(&out->cb, in, sizeof(struct CallBack));
+ out->index = cbtoi(in);
+ return 0;
+}
+
+static int
+cb_stateDiskEntryToCB(struct fs_dump_state * state,
+ struct CBDiskEntry * in, struct CallBack * out)
+{
+ int ret = 0;
+
+ memcpy(out, &in->cb, sizeof(struct CallBack));
+
+ /* setup CB map entry */
+ if (!in->index || (in->index >= state->cb_map.len)) {
+ ViceLog(0, ("cb_stateDiskEntryToCB: index (%d) out of range\n",
+ in->index));
+ ret = 1;
+ goto done;
+ }
+ state->cb_map.entries[in->index].old_idx = in->index;
+ state->cb_map.entries[in->index].new_idx = cbtoi(out);
+
+ done:
+ return ret;
+}
+
+/* index map routines */
+static int
+cb_stateAllocMap(struct fs_dump_state * state)
+{
+ state->fe_map.len = state->cb_hdr->fe_max + 1;
+ state->cb_map.len = state->cb_hdr->cb_max + 1;
+ state->fe_map.entries = (struct idx_map_entry_t *)
+ calloc(state->fe_map.len, sizeof(struct idx_map_entry_t));
+ state->cb_map.entries = (struct idx_map_entry_t *)
+ calloc(state->cb_map.len, sizeof(struct idx_map_entry_t));
+ return ((state->fe_map.entries != NULL) && (state->cb_map.entries != NULL)) ? 0 : 1;
+}
+
+int
+fe_OldToNew(struct fs_dump_state * state, afs_uint32 old, afs_uint32 * new)
+{
+ int ret = 0;
+
+ /* FEs use a one-based indexing system, so old==0 implies no mapping */
+ if (!old) {
+ *new = 0;
+ goto done;
+ }
+
+ if (old >= state->fe_map.len) {
+ ViceLog(0, ("fe_OldToNew: index %d is out of range\n", old));
+ ret = 1;
+ } else if (state->fe_map.entries[old].old_idx != old) { /* sanity check */
+ ViceLog(0, ("fe_OldToNew: index %d points to an invalid FileEntry record\n", old));
+ ret = 1;
+ } else {
+ *new = state->fe_map.entries[old].new_idx;
+ }
+
+ done:
+ return ret;
+}
+
+int
+cb_OldToNew(struct fs_dump_state * state, afs_uint32 old, afs_uint32 * new)
+{
+ int ret = 0;
+
+ /* CBs use a one-based indexing system, so old==0 implies no mapping */
+ if (!old) {
+ *new = 0;
+ goto done;
+ }
+
+ if (old >= state->cb_map.len) {
+ ViceLog(0, ("cb_OldToNew: index %d is out of range\n", old));
+ ret = 1;
+ } else if (state->cb_map.entries[old].old_idx != old) { /* sanity check */
+ ViceLog(0, ("cb_OldToNew: index %d points to an invalid CallBack record\n", old));
+ ret = 1;
+ } else {
+ *new = state->cb_map.entries[old].new_idx;
+ }
+
+ done:
+ return ret;
+}
+#endif /* AFS_DEMAND_ATTACH_FS */
+
int
DumpCallBackState(void)
{
return 0;
}
-#endif
+#endif /* !INTERPRET_DUMP */
#ifdef INTERPRET_DUMP
struct CallBack *cb;
struct FileEntry *fe;
- for (hash = 0; hash < VHASH; hash++) {
+ for (hash = 0; hash < FEHASH_SIZE; hash++) {
for (feip = &HashTable[hash]; fe = itofe(*feip);) {
if (!vol || (fe->volid == vol)) {
register struct CallBack *cbnext;
H_UNLOCK;
}
}
+#ifdef AFS_DEMAND_ATTACH_FS
+ /* try to bail ASAP if the fileserver is shutting down */
+ FS_STATE_RDLOCK;
+ if (fs_state.mode == FS_MODE_SHUTDOWN) {
+ FS_STATE_UNLOCK;
+ multi_Abort;
+ }
+ FS_STATE_UNLOCK;
+#endif
}
multi_End_Ignore;
H_LOCK;