#include "afsd.h"
#include "cm_getaddrs.h"
+typedef struct _uuid2addrsEntry {
+ osi_queue_t q;
+ afsUUID uuid;
+ afs_int32 unique;
+ afs_uint32 nentries;
+ bulkaddrs addrs;
+ afs_int32 refCount;
+ afs_uint32 flags;
+} uuid2addrsEntry_t;
+
+#define CM_GETADDRS_FLAG_DELETE 1
+
+/* lock for hash table */
+osi_rwlock_t cm_getaddrsLock;
+
+/* hash table */
+#define CM_GETADDRS_HASHTABLESIZE 128
+static afs_uint32 cm_getaddrsHashTableSize = CM_GETADDRS_HASHTABLESIZE;
+
+#define CM_GETADDRS_HASH(uuidp) \
+ (opr_jhash((const afs_uint32 *)uuidp, 4, 0) & (cm_getaddrsHashTableSize - 1))
+
+static struct _uuid2addrsHashTableBucket {
+ uuid2addrsEntry_t * Firstp;
+ uuid2addrsEntry_t * Endp;
+} cm_getaddrsHashTable[CM_GETADDRS_HASHTABLESIZE];
+
+/*
+ * Return a cached entry that matches the requested
+ * uuid if the requested unique is less than or equal
+ * to the cached entry. Otherwise, return NULL.
+ *
+ * Returned entries are returned with a reference.
+ */
+static uuid2addrsEntry_t *
+cm_getaddrsFind(afsUUID *uuid, afs_int32 srv_unique)
+{
+ uuid2addrsEntry_t * entry = NULL;
+ afs_uint32 hash = CM_GETADDRS_HASH(uuid);
+
+ lock_ObtainRead(&cm_getaddrsLock);
+
+ for ( entry = cm_getaddrsHashTable[hash].Firstp;
+ entry;
+ entry = (uuid2addrsEntry_t *)osi_QNext(&entry->q))
+ {
+ if (memcmp(uuid, &entry->uuid, sizeof(afsUUID)) == 0 &&
+ srv_unique <= entry->unique &&
+ !(entry->flags & CM_GETADDRS_FLAG_DELETE))
+ break;
+ }
+
+ if (entry)
+ entry->refCount++;
+ lock_ReleaseRead(&cm_getaddrsLock);
+
+ return entry;
+}
+
+static void
+cm_getaddrsPut(uuid2addrsEntry_t *entry)
+{
+ lock_ObtainRead(&cm_getaddrsLock);
+ entry->refCount--;
+ lock_ReleaseRead(&cm_getaddrsLock);
+}
+
+/*
+ * Add a bulkaddrs list to the getaddrs queue.
+ * Once this function is called the bulkaddrs structure
+ * no longer belongs to the caller and must not be
+ * accessed. It may be freed if there is a race with
+ * another thread.
+ */
+
+static uuid2addrsEntry_t *
+cm_getaddrsAdd(afsUUID *uuid, afs_int32 srv_unique,
+ afs_uint32 nentries, bulkaddrs *addrsp)
+{
+ uuid2addrsEntry_t * entry, *next, *retentry = NULL;
+ afs_uint32 hash = CM_GETADDRS_HASH(uuid);
+ int insert = 1;
+
+ lock_ObtainWrite(&cm_getaddrsLock);
+ for ( entry = cm_getaddrsHashTable[hash].Firstp;
+ entry;
+ entry = next)
+ {
+ next = (uuid2addrsEntry_t *)osi_QNext(&entry->q);
+
+ if ((entry->flags & CM_GETADDRS_FLAG_DELETE) &&
+ entry->refCount == 0)
+ {
+ osi_QRemoveHT((osi_queue_t **) &cm_getaddrsHashTable[hash].Firstp,
+ (osi_queue_t **) &cm_getaddrsHashTable[hash].Endp,
+ &entry->q);
+ xdr_free((xdrproc_t) xdr_bulkaddrs, &entry->addrs);
+ continue;
+ }
+
+ if (memcmp(uuid, &entry->uuid, sizeof(afsUUID)) == 0)
+ {
+
+ if (srv_unique <= entry->unique) {
+ /*
+ * out of date or duplicate entry.
+ * discard the input.
+ */
+ xdr_free((xdrproc_t) xdr_bulkaddrs, addrsp);
+ insert = 0;
+ retentry = entry;
+ retentry->refCount++;
+ continue;
+ }
+
+ /*
+ * this entry is newer than the one we found,
+ * if it is unused then update it
+ */
+ if (entry->refCount == 0) {
+ xdr_free((xdrproc_t) xdr_bulkaddrs, &entry->addrs);
+
+ entry->unique = srv_unique;
+ entry->addrs = *addrsp;
+ entry->nentries = nentries;
+ insert = 0;
+ retentry = entry;
+ retentry->refCount++;
+ continue;
+ }
+ }
+ }
+
+ if (insert) {
+ entry = calloc(1, sizeof(uuid2addrsEntry_t));
+ if (entry) {
+ memcpy(&entry->uuid, uuid, sizeof(afsUUID));
+ entry->unique = srv_unique;
+ entry->addrs = *addrsp;
+ entry->nentries = nentries;
+
+ osi_QAddH((osi_queue_t **) &cm_getaddrsHashTable[hash].Firstp,
+ (osi_queue_t **) &cm_getaddrsHashTable[hash].Endp,
+ &entry->q);
+ retentry = entry;
+ retentry->refCount++;
+ }
+ }
+ lock_ReleaseWrite(&cm_getaddrsLock);
+
+ return retentry;
+}
+
/*
* cm_GetAddrsU takes as input a uuid and a unique value which
* represent the set of addresses that are required. These values
afs_int32 serverUnique[])
{
afs_uint32 code = 0;
- cm_conn_t *connp;
- struct rx_connection *rxconnp;
- afs_uint32 * addrp, nentries;
- afs_int32 unique;
- bulkaddrs addrs;
- ListAddrByAttributes attrs;
- afsUUID uuid;
- int i;
-
- memset(&uuid, 0, sizeof(uuid));
- memset(&addrs, 0, sizeof(addrs));
- memset(&attrs, 0, sizeof(attrs));
-
- attrs.Mask = VLADDR_UUID;
- attrs.uuid = *Uuid;
-
- do {
- code = cm_ConnByMServers(cellp->vlServersp, 0, userp, reqp, &connp);
- if (code)
- continue;
- rxconnp = cm_GetRxConn(connp);
- code = VL_GetAddrsU(rxconnp, &attrs, &uuid, &unique, &nentries,
- &addrs);
- rx_PutConnection(rxconnp);
- } while (cm_Analyze(connp, userp, reqp, NULL, cellp, 0, NULL, NULL,
- &cellp->vlServersp, NULL, code));
+ uuid2addrsEntry_t *entry;
+
+ entry = cm_getaddrsFind(Uuid, Unique);
+ if (entry == NULL)
+ {
+ cm_conn_t *connp;
+ struct rx_connection *rxconnp;
+ ListAddrByAttributes attrs;
+ afs_int32 unique;
+ afsUUID uuid;
+ afs_uint32 nentries;
+ bulkaddrs addrs;
+
+ memset(&uuid, 0, sizeof(uuid));
+ memset(&addrs, 0, sizeof(addrs));
+ memset(&attrs, 0, sizeof(attrs));
- code = cm_MapVLRPCError(code, reqp);
+ attrs.Mask = VLADDR_UUID;
+ attrs.uuid = *Uuid;
- if (afsd_logp->enabled) {
- char uuidstr[128];
- afsUUID_to_string(Uuid, uuidstr, sizeof(uuidstr));
+ do {
+ code = cm_ConnByMServers(cellp->vlServersp, 0, userp, reqp, &connp);
+ if (code)
+ continue;
+ rxconnp = cm_GetRxConn(connp);
+ code = VL_GetAddrsU(rxconnp, &attrs, &uuid, &unique, &nentries,
+ &addrs);
+ rx_PutConnection(rxconnp);
+ } while (cm_Analyze(connp, userp, reqp, NULL, cellp, 0, NULL, NULL,
+ &cellp->vlServersp, NULL, code));
+
+ code = cm_MapVLRPCError(code, reqp);
+
+ if (afsd_logp->enabled) {
+ char uuidstr[128];
+ afsUUID_to_string(Uuid, uuidstr, sizeof(uuidstr));
+
+ if (code)
+ osi_Log2(afsd_logp,
+ "CALL VL_GetAddrsU serverNumber %s FAILURE, code 0x%x",
+ osi_LogSaveString(afsd_logp, uuidstr),
+ code);
+ else
+ osi_Log1(afsd_logp, "CALL VL_GetAddrsU serverNumber %s SUCCESS",
+ osi_LogSaveString(afsd_logp, uuidstr));
+ }
if (code)
- osi_Log2(afsd_logp,
- "CALL VL_GetAddrsU serverNumber %s FAILURE, code 0x%x",
- osi_LogSaveString(afsd_logp, uuidstr),
- code);
- else
- osi_Log1(afsd_logp, "CALL VL_GetAddrsU serverNumber %s SUCCESS",
- osi_LogSaveString(afsd_logp, uuidstr));
+ return CM_ERROR_RETRY;
+
+ if (nentries == 0) {
+ xdr_free((xdrproc_t) xdr_bulkaddrs, &addrs);
+ code = CM_ERROR_INVAL;
+ } else {
+ /* addrs will either be cached or freed by cm_getaddrsAdd() */
+ entry = cm_getaddrsAdd(&uuid, unique, nentries, &addrs);
+ }
}
- if (code)
- return CM_ERROR_RETRY;
+ if (entry != NULL) {
+ afs_uint32 * addrp;
+ afs_uint32 n;
- if (nentries == 0) {
- code = CM_ERROR_INVAL;
- } else {
- addrp = addrs.bulkaddrs_val;
- for (i = 0; i < nentries && (*index) < NMAXNSERVERS; (*index)++, i++) {
+ addrp = entry->addrs.bulkaddrs_val;
+ for (n = 0; n < entry->nentries && (*index) < NMAXNSERVERS;
+ (*index)++, n++) {
serverFlags[*index] = Flags;
- serverNumber[*index] = addrp[i];
- serverUUID[*index] = uuid;
- serverUnique[*index] = unique;
+ serverNumber[*index] = addrp[n];
+ serverUUID[*index] = entry->uuid;
+ serverUnique[*index] = entry->unique;
}
+ cm_getaddrsPut(entry);
}
- xdr_free((xdrproc_t) xdr_bulkaddrs, &addrs);
return code;
}
+
+void
+cm_getaddrsInit(void)
+{
+ static osi_once_t once;
+
+ if (osi_Once(&once)) {
+ lock_InitializeRWLock(&cm_getaddrsLock, "cm_getaddrsLock",
+ LOCK_HIERARCHY_GETADDRS_GLOBAL);
+ osi_EndOnce(&once);
+ }
+}
+
+void
+cm_getaddrsShutdown(void)
+{
+ lock_FinalizeRWLock(&cm_getaddrsLock);
+}