2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afs/param.h>
18 #include <sys/socket.h>
26 osi_rwlock_t cm_volumeLock;
29 cm_ValidateVolume(void)
34 for (volp = cm_data.allVolumesp, count = 0; volp; volp=volp->allNextp, count++) {
35 if ( volp->magic != CM_VOLUME_MAGIC ) {
36 afsi_log("cm_ValidateVolume failure: volp->magic != CM_VOLUME_MAGIC");
37 fprintf(stderr, "cm_ValidateVolume failure: volp->magic != CM_VOLUME_MAGIC\n");
40 if ( volp->cellp && volp->cellp->magic != CM_CELL_MAGIC ) {
41 afsi_log("cm_ValidateVolume failure: volp->cellp->magic != CM_CELL_MAGIC");
42 fprintf(stderr, "cm_ValidateVolume failure: volp->cellp->magic != CM_CELL_MAGIC\n");
45 if ( volp->allNextp && volp->allNextp->magic != CM_VOLUME_MAGIC ) {
46 afsi_log("cm_ValidateVolume failure: volp->allNextp->magic != CM_VOLUME_MAGIC");
47 fprintf(stderr, "cm_ValidateVolume failure: volp->allNextp->magic != CM_VOLUME_MAGIC\n");
50 if ( count != 0 && volp == cm_data.allVolumesp ||
51 count > cm_data.maxVolumes ) {
52 afsi_log("cm_ValidateVolume failure: cm_data.allVolumep loop detected");
53 fprintf(stderr, "cm_ValidateVolume failure: cm_data.allVolumep loop detected\n");
58 if ( count != cm_data.currentVolumes ) {
59 afsi_log("cm_ValidateVolume failure: count != cm_data.currentVolumes");
60 fprintf(stderr, "cm_ValidateVolume failure: count != cm_data.currentVolumes\n");
68 cm_ShutdownVolume(void)
72 for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
75 cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_alldown);
77 cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_alldown);
79 cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_alldown);
81 lock_FinalizeMutex(&volp->mx);
87 void cm_InitVolume(int newFile, long maxVols)
89 static osi_once_t once;
91 if (osi_Once(&once)) {
92 lock_InitializeRWLock(&cm_volumeLock, "cm global volume lock");
95 cm_data.allVolumesp = NULL;
96 cm_data.currentVolumes = 0;
97 cm_data.maxVolumes = maxVols;
98 memset(cm_data.volumeNameHashTablep, 0, sizeof(cm_volume_t *) * cm_data.volumeHashTableSize);
99 memset(cm_data.volumeRWIDHashTablep, 0, sizeof(cm_volume_t *) * cm_data.volumeHashTableSize);
100 memset(cm_data.volumeROIDHashTablep, 0, sizeof(cm_volume_t *) * cm_data.volumeHashTableSize);
101 memset(cm_data.volumeBKIDHashTablep, 0, sizeof(cm_volume_t *) * cm_data.volumeHashTableSize);
102 cm_data.volumeLRUFirstp = cm_data.volumeLRULastp = NULL;
106 for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
107 lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
108 volp->flags |= CM_VOLUMEFLAG_RESET;
109 volp->rw.state = vl_unknown;
110 volp->rw.serversp = NULL;
111 volp->ro.state = vl_unknown;
112 volp->ro.serversp = NULL;
113 volp->bk.state = vl_unknown;
114 volp->bk.serversp = NULL;
116 cm_VolumeStatusNotification(volp, volp->rw.ID, vl_alldown, volp->rw.state);
118 cm_VolumeStatusNotification(volp, volp->ro.ID, vl_alldown, volp->ro.state);
120 cm_VolumeStatusNotification(volp, volp->bk.ID, vl_alldown, volp->bk.state);
128 /* returns true if the id is a decimal integer, in which case we interpret it
129 * as an id. make the cache manager much simpler.
130 * Stolen from src/volser/vlprocs.c */
132 cm_VolNameIsID(char *aname)
135 while (tc = *aname++) {
136 if (tc > '9' || tc < '0')
144 * Update a volume. Caller holds volume's lock (volp->mx).
147 * shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU} 01:38 (JHutz)
148 * Yes, we support multihomed fileservers.
149 * Since before we got the code from IBM.
150 * But to find out about multiple addresses on a multihomed server, you need
151 * to use VL_GetEntryByNameU and VL_GetAddrsU. If you use
152 * VL_GetEntryByNameO or VL_GetEntryByNameN, the vlserver just gives you one
153 * address per server.
154 * shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU} 01:39 (JHutz)
155 * see src/afs/afs_volume.c, paying particular attention to
156 * afs_NewVolumeByName, afs_SetupVolume, and InstallUVolumeEntry
157 * shadow / openafs / jaltman {ANDREW.CMU.EDU} 01:40 (Jeffrey Altman)
158 * thanks. The windows client calls the 0 versions.
159 * shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU} 01:51 (JHutz)
161 * By not using the N versions, you only get up to 8 sites instead of 13.
162 * By not using the U versions, you don't get to know about multihomed serve
163 * shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU} 01:52 (JHutz)
164 * Of course, you probably want to support the older versions for backward
165 * compatibility. If you do that, you need to call the newest interface
166 * first, and fall back to successively older versions if you get
170 long cm_UpdateVolume(struct cm_cell *cellp, cm_user_t *userp, cm_req_t *reqp,
175 cm_serverRef_t *tsrp;
177 struct sockaddr_in tsockAddr;
180 struct vldbentry vldbEntry;
181 struct nvldbentry nvldbEntry;
183 struct uvldbentry uvldbEntry;
188 enum volstatus rwNewstate = vl_online;
189 enum volstatus roNewstate = vl_online;
190 enum volstatus bkNewstate = vl_online;
192 /* clear out old bindings */
193 if (volp->rw.serversp)
194 cm_FreeServerList(&volp->rw.serversp, CM_FREESERVERLIST_DELETE);
195 if (volp->ro.serversp)
196 cm_FreeServerList(&volp->ro.serversp, CM_FREESERVERLIST_DELETE);
197 if (volp->bk.serversp)
198 cm_FreeServerList(&volp->bk.serversp, CM_FREESERVERLIST_DELETE);
200 #ifdef AFS_FREELANCE_CLIENT
201 if ( cellp->cellID == AFS_FAKE_ROOT_CELL_ID && atoi(volp->namep)==AFS_FAKE_ROOT_VOL_ID )
203 memset(&vldbEntry, 0, sizeof(vldbEntry));
204 vldbEntry.flags |= VLF_RWEXISTS;
205 vldbEntry.volumeId[0] = AFS_FAKE_ROOT_VOL_ID;
211 /* now we have volume structure locked and held; make RPC to fill it */
212 osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s", volp->cellp->name, volp->namep);
214 code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
218 code = VL_GetEntryByNameU(connp->callp, volp->namep, &uvldbEntry);
220 if ( code == RXGEN_OPCODE )
223 code = VL_GetEntryByNameN(connp->callp, volp->namep, &nvldbEntry);
226 if ( code == RXGEN_OPCODE ) {
227 code = VL_GetEntryByNameO(connp->callp, volp->namep, &vldbEntry);
230 } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
231 code = cm_MapVLRPCError(code, reqp);
233 osi_Log3(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s FAILURE, code 0x%x",
234 volp->cellp->name, volp->namep, code);
236 osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s SUCCESS",
237 volp->cellp->name, volp->namep);
240 /* We can end up here with code == CM_ERROR_NOSUCHVOLUME if the base volume name
241 * does not exist but there might exist a .readonly volume. If the base name
242 * doesn't exist we will not care about the .backup that might be left behind
243 * since there should be no method to access it.
245 if (code == CM_ERROR_NOSUCHVOLUME && volp->rw.ID == 0 && strlen(volp->namep) < (VL_MAXNAMELEN - 9)) {
246 char name[VL_MAXNAMELEN];
248 snprintf(name, VL_MAXNAMELEN, "%s.readonly", volp->namep);
250 /* now we have volume structure locked and held; make RPC to fill it */
251 osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s", volp->cellp->name, name);
253 code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
257 code = VL_GetEntryByNameU(connp->callp, name, &uvldbEntry);
259 if ( code == RXGEN_OPCODE )
262 code = VL_GetEntryByNameN(connp->callp, name, &nvldbEntry);
265 if ( code == RXGEN_OPCODE ) {
266 code = VL_GetEntryByNameO(connp->callp, name, &vldbEntry);
269 } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
270 code = cm_MapVLRPCError(code, reqp);
272 osi_Log3(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s FAILURE, code 0x%x",
273 volp->cellp->name, name, code);
275 osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s SUCCESS",
276 volp->cellp->name, name);
285 afs_int32 serverNumber[NMAXNSERVERS];
286 afs_int32 serverFlags[NMAXNSERVERS];
287 afs_int32 rwServers_alldown = 1;
288 afs_int32 roServers_alldown = 1;
289 afs_int32 bkServers_alldown = 1;
290 char name[VL_MAXNAMELEN];
294 flags = vldbEntry.flags;
295 nServers = vldbEntry.nServers;
296 rwID = vldbEntry.volumeId[0];
297 roID = vldbEntry.volumeId[1];
298 bkID = vldbEntry.volumeId[2];
299 for ( i=0; i<nServers; i++ ) {
300 serverFlags[i] = vldbEntry.serverFlags[i];
301 serverNumber[i] = vldbEntry.serverNumber[i];
303 strncpy(name, vldbEntry.name, VL_MAXNAMELEN);
304 name[VL_MAXNAMELEN - 1] = '\0';
307 flags = nvldbEntry.flags;
308 nServers = nvldbEntry.nServers;
309 rwID = nvldbEntry.volumeId[0];
310 roID = nvldbEntry.volumeId[1];
311 bkID = nvldbEntry.volumeId[2];
312 for ( i=0; i<nServers; i++ ) {
313 serverFlags[i] = nvldbEntry.serverFlags[i];
314 serverNumber[i] = nvldbEntry.serverNumber[i];
316 strncpy(name, nvldbEntry.name, VL_MAXNAMELEN);
317 name[VL_MAXNAMELEN - 1] = '\0';
321 flags = uvldbEntry.flags;
322 nServers = uvldbEntry.nServers;
323 rwID = uvldbEntry.volumeId[0];
324 roID = uvldbEntry.volumeId[1];
325 bkID = uvldbEntry.volumeId[2];
326 for ( i=0, j=0; i<nServers && j<NMAXNSERVERS; i++ ) {
327 if ( !(uvldbEntry.serverFlags[i] & VLSERVER_FLAG_UUID) ) {
328 serverFlags[j] = uvldbEntry.serverFlags[i];
329 serverNumber[j] = uvldbEntry.serverNumber[i].time_low;
332 afs_uint32 * addrp, nentries, code, unique;
334 ListAddrByAttributes attrs;
337 memset((char *)&attrs, 0, sizeof(attrs));
338 attrs.Mask = VLADDR_UUID;
339 attrs.uuid = uvldbEntry.serverNumber[i];
340 memset((char *)&uuid, 0, sizeof(uuid));
341 memset((char *)&addrs, 0, sizeof(addrs));
344 code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
348 code = VL_GetAddrsU(connp->callp, &attrs, &uuid, &unique, &nentries, &addrs);
350 if (code == 0 && nentries == 0)
352 } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
353 code = cm_MapVLRPCError(code, reqp);
357 addrp = addrs.bulkaddrs_val;
358 for (k = 0; k < nentries && j < NMAXNSERVERS; j++, k++) {
359 serverFlags[j] = uvldbEntry.serverFlags[i];
360 serverNumber[j] = addrp[k];
363 free(addrs.bulkaddrs_val); /* This is wrong */
366 nServers = j; /* update the server count */
367 strncpy(name, uvldbEntry.name, VL_MAXNAMELEN);
368 name[VL_MAXNAMELEN - 1] = '\0';
373 /* decode the response */
374 lock_ObtainWrite(&cm_volumeLock);
375 if (cm_VolNameIsID(volp->namep)) {
380 if (len >= 8 && strcmp(name + len - 7, ".backup") == 0) {
381 name[len - 7] = '\0';
382 } else if (len >= 10 && strcmp(name + len - 9, ".readonly") == 0) {
383 name[len - 9] = '\0';
386 osi_Log2(afsd_logp, "cm_UpdateVolume name %s -> %s", volp->namep, name);
388 if (volp->flags & CM_VOLUMEFLAG_IN_HASH)
389 cm_RemoveVolumeFromNameHashTable(volp);
391 strcpy(volp->namep, name);
393 cm_AddVolumeToNameHashTable(volp);
396 if (flags & VLF_RWEXISTS) {
397 if (volp->rw.ID != rwID) {
398 if (volp->rw.flags & CM_VOLUMEFLAG_IN_HASH)
399 cm_RemoveVolumeFromIDHashTable(volp, RWVOL);
401 cm_AddVolumeToIDHashTable(volp, RWVOL);
404 if (volp->rw.flags & CM_VOLUMEFLAG_IN_HASH)
405 cm_RemoveVolumeFromIDHashTable(volp, RWVOL);
408 if (flags & VLF_ROEXISTS) {
409 if (volp->ro.ID != roID) {
410 if (volp->ro.flags & CM_VOLUMEFLAG_IN_HASH)
411 cm_RemoveVolumeFromIDHashTable(volp, ROVOL);
413 cm_AddVolumeToIDHashTable(volp, ROVOL);
416 if (volp->ro.flags & CM_VOLUMEFLAG_IN_HASH)
417 cm_RemoveVolumeFromIDHashTable(volp, ROVOL);
420 if (flags & VLF_BACKEXISTS) {
421 if (volp->bk.ID != bkID) {
422 if (volp->bk.flags & CM_VOLUMEFLAG_IN_HASH)
423 cm_RemoveVolumeFromIDHashTable(volp, BACKVOL);
425 cm_AddVolumeToIDHashTable(volp, BACKVOL);
428 if (volp->bk.flags & CM_VOLUMEFLAG_IN_HASH)
429 cm_RemoveVolumeFromIDHashTable(volp, BACKVOL);
432 lock_ReleaseWrite(&cm_volumeLock);
433 for (i=0; i<nServers; i++) {
434 /* create a server entry */
435 tflags = serverFlags[i];
436 if (tflags & VLSF_DONTUSE)
438 tsockAddr.sin_family = AF_INET;
439 tempAddr = htonl(serverNumber[i]);
440 tsockAddr.sin_addr.s_addr = tempAddr;
441 tsp = cm_FindServer(&tsockAddr, CM_SERVER_FILE);
443 tsp = cm_NewServer(&tsockAddr, CM_SERVER_FILE,
446 /* if this server was created by fs setserverprefs */
450 osi_assert(tsp != NULL);
452 /* and add it to the list(s). */
454 * Each call to cm_NewServerRef() increments the
455 * ref count of tsp. These reference will be dropped,
456 * if and when the volume is reset; see reset code
457 * earlier in this function.
459 if ((tflags & VLSF_RWVOL) && (flags & VLF_RWEXISTS)) {
460 tsrp = cm_NewServerRef(tsp, rwID);
461 cm_InsertServerList(&volp->rw.serversp, tsrp);
463 lock_ObtainWrite(&cm_serverLock);
464 tsrp->refCount--; /* drop allocation reference */
465 lock_ReleaseWrite(&cm_serverLock);
467 if (!(tsp->flags & CM_SERVERFLAG_DOWN))
468 rwServers_alldown = 0;
470 if ((tflags & VLSF_ROVOL) && (flags & VLF_ROEXISTS)) {
471 tsrp = cm_NewServerRef(tsp, roID);
472 cm_InsertServerList(&volp->ro.serversp, tsrp);
473 lock_ObtainWrite(&cm_serverLock);
474 tsrp->refCount--; /* drop allocation reference */
475 lock_ReleaseWrite(&cm_serverLock);
478 if (!(tsp->flags & CM_SERVERFLAG_DOWN))
479 roServers_alldown = 0;
481 /* We don't use VLSF_BACKVOL !?! */
482 /* Because only the backup on the server holding the RW
483 * volume can be valid. This check prevents errors if a
484 * RW is moved but the old backup is not removed.
486 if ((tflags & VLSF_RWVOL) && (flags & VLF_BACKEXISTS)) {
487 tsrp = cm_NewServerRef(tsp, bkID);
488 cm_InsertServerList(&volp->bk.serversp, tsrp);
489 lock_ObtainWrite(&cm_serverLock);
490 tsrp->refCount--; /* drop allocation reference */
491 lock_ReleaseWrite(&cm_serverLock);
493 if (!(tsp->flags & CM_SERVERFLAG_DOWN))
494 bkServers_alldown = 0;
496 /* Drop the reference obtained by cm_FindServer() */
503 * If the first n servers have the same ipRank, then we
504 * randomly pick one among them and move it to the beginning.
505 * We don't bother to re-order the whole list because
506 * the rest of the list is used only if the first server is
507 * down. We only do this for the RO list; we assume the other
508 * lists are length 1.
511 cm_RandomizeServer(&volp->ro.serversp);
514 rwNewstate = rwServers_alldown ? vl_alldown : vl_online;
515 roNewstate = roServers_alldown ? vl_alldown : vl_online;
516 bkNewstate = bkServers_alldown ? vl_alldown : vl_online;
518 rwNewstate = roNewstate = bkNewstate = vl_alldown;
521 if (volp->rw.state != rwNewstate) {
523 cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, rwNewstate);
524 volp->rw.state = rwNewstate;
526 if (volp->ro.state != roNewstate) {
528 cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, roNewstate);
529 volp->ro.state = roNewstate;
531 if (volp->bk.state != bkNewstate) {
533 cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, bkNewstate);
534 volp->bk.state = bkNewstate;
540 void cm_GetVolume(cm_volume_t *volp)
543 lock_ObtainWrite(&cm_volumeLock);
545 lock_ReleaseWrite(&cm_volumeLock);
550 long cm_GetVolumeByID(cm_cell_t *cellp, afs_uint32 volumeID, cm_user_t *userp,
551 cm_req_t *reqp, afs_uint32 flags, cm_volume_t **outVolpp)
554 #ifdef SEARCH_ALL_VOLUMES
557 char volNameString[VL_MAXNAMELEN];
561 lock_ObtainRead(&cm_volumeLock);
562 #ifdef SEARCH_ALL_VOLUMES
563 for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
564 if (cellp == volp->cellp &&
565 ((unsigned) volumeID == volp->rw.ID ||
566 (unsigned) volumeID == volp->ro.ID ||
567 (unsigned) volumeID == volp->bk.ID))
572 #endif /* SEARCH_ALL_VOLUMES */
574 hash = CM_VOLUME_ID_HASH(volumeID);
575 /* The volumeID can be any one of the three types. So we must
576 * search the hash table for all three types until we find it.
577 * We will search in the order of RO, RW, BK.
579 for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = volp->ro.nextp) {
580 if ( cellp == volp->cellp && volumeID == volp->ro.ID )
585 for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = volp->rw.nextp) {
586 if ( cellp == volp->cellp && volumeID == volp->rw.ID )
592 for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = volp->bk.nextp) {
593 if ( cellp == volp->cellp && volumeID == volp->bk.ID )
598 #ifdef SEARCH_ALL_VOLUMES
599 osi_assert(volp == volp2);
602 lock_ReleaseRead(&cm_volumeLock);
604 /* hold the volume if we found it */
610 lock_ObtainMutex(&volp->mx);
613 if (volp->flags & CM_VOLUMEFLAG_RESET) {
614 code = cm_UpdateVolume(cellp, userp, reqp, volp);
616 volp->flags &= ~CM_VOLUMEFLAG_RESET;
618 lock_ReleaseMutex(&volp->mx);
622 lock_ObtainWrite(&cm_volumeLock);
623 cm_AdjustVolumeLRU(volp);
624 lock_ReleaseWrite(&cm_volumeLock);
631 /* otherwise, we didn't find it so consult the VLDB */
632 sprintf(volNameString, "%u", volumeID);
633 code = cm_GetVolumeByName(cellp, volNameString, userp, reqp,
639 long cm_GetVolumeByName(struct cm_cell *cellp, char *volumeNamep,
640 struct cm_user *userp, struct cm_req *reqp,
641 afs_uint32 flags, cm_volume_t **outVolpp)
644 #ifdef SEARCH_ALL_VOLUMES
648 char name[VL_MAXNAMELEN];
653 strncpy(name, volumeNamep, VL_MAXNAMELEN);
654 name[VL_MAXNAMELEN-1] = '\0';
657 if (len >= 8 && strcmp(name + len - 7, ".backup") == 0) {
659 name[len - 7] = '\0';
660 } else if (len >= 10 && strcmp(name + len - 9, ".readonly") == 0) {
662 name[len - 9] = '\0';
667 lock_ObtainRead(&cm_volumeLock);
668 #ifdef SEARCH_ALL_VOLUMES
669 for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
670 if (cellp == volp->cellp && strcmp(name, volp->namep) == 0) {
675 #endif /* SEARCH_ALL_VOLUMES */
677 hash = CM_VOLUME_NAME_HASH(name);
678 for (volp = cm_data.volumeNameHashTablep[hash]; volp; volp = volp->nameNextp) {
679 if (cellp == volp->cellp && strcmp(name, volp->namep) == 0)
683 #ifdef SEARCH_ALL_VOLUMES
684 osi_assert(volp2 == volp);
687 if (!volp && (flags & CM_GETVOL_FLAG_CREATE)) {
688 /* otherwise, get from VLDB */
690 if ( cm_data.currentVolumes >= cm_data.maxVolumes ) {
692 #ifdef RECYCLE_FROM_ALL_VOLUMES_LIST
693 for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
694 if ( volp->refCount == 0 ) {
695 /* There is one we can re-use */
700 for ( volp = cm_data.volumeLRULastp;
702 volp = (cm_volume_t *) osi_QPrev(&volp->q))
704 if ( volp->refCount == 0 ) {
705 /* There is one we can re-use */
711 osi_panic("Exceeded Max Volumes", __FILE__, __LINE__);
713 lock_ReleaseRead(&cm_volumeLock);
714 lock_ObtainMutex(&volp->mx);
715 lock_ObtainWrite(&cm_volumeLock);
717 osi_Log2(afsd_logp, "Recycling Volume %s:%s",
718 volp->cellp->name, volp->namep);
720 if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE)
721 cm_RemoveVolumeFromLRU(volp);
722 if (volp->flags & CM_VOLUMEFLAG_IN_HASH)
723 cm_RemoveVolumeFromNameHashTable(volp);
724 if (volp->rw.flags & CM_VOLUMEFLAG_IN_HASH)
725 cm_RemoveVolumeFromIDHashTable(volp, RWVOL);
726 if (volp->ro.flags & CM_VOLUMEFLAG_IN_HASH)
727 cm_RemoveVolumeFromIDHashTable(volp, ROVOL);
728 if (volp->bk.flags & CM_VOLUMEFLAG_IN_HASH)
729 cm_RemoveVolumeFromIDHashTable(volp, BACKVOL);
732 cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_unknown);
734 cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_unknown);
736 cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_unknown);
738 volp->rw.ID = volp->ro.ID = volp->bk.ID = 0;
739 volp->dotdotFid.cell = 0;
740 volp->dotdotFid.volume = 0;
741 volp->dotdotFid.unique = 0;
742 volp->dotdotFid.vnode = 0;
744 volp = &cm_data.volumeBaseAddress[cm_data.currentVolumes++];
745 memset(volp, 0, sizeof(cm_volume_t));
746 volp->magic = CM_VOLUME_MAGIC;
747 volp->allNextp = cm_data.allVolumesp;
748 cm_data.allVolumesp = volp;
749 lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
750 lock_ReleaseRead(&cm_volumeLock);
751 lock_ObtainMutex(&volp->mx);
752 lock_ObtainWrite(&cm_volumeLock);
755 strncpy(volp->namep, name, VL_MAXNAMELEN);
756 volp->namep[VL_MAXNAMELEN-1] = '\0';
757 volp->refCount = 1; /* starts off held */
758 volp->flags = CM_VOLUMEFLAG_RESET;
759 volp->rw.state = volp->ro.state = volp->bk.state = vl_unknown;
760 volp->rw.nextp = volp->ro.nextp = volp->bk.nextp = NULL;
761 volp->rw.flags = volp->ro.flags = volp->bk.flags = 0;
762 cm_AddVolumeToNameHashTable(volp);
763 lock_ReleaseWrite(&cm_volumeLock);
766 lock_ReleaseRead(&cm_volumeLock);
769 lock_ObtainMutex(&volp->mx);
771 return CM_ERROR_NOSUCHVOLUME;
775 /* if we get here we are holding the mutex */
776 if (volp->flags & CM_VOLUMEFLAG_RESET) {
777 code = cm_UpdateVolume(cellp, userp, reqp, volp);
779 volp->flags &= ~CM_VOLUMEFLAG_RESET;
781 lock_ReleaseMutex(&volp->mx);
783 if (code == 0 && (type == BACKVOL && volp->bk.ID == 0 ||
784 type == ROVOL && volp->ro.ID == 0))
785 code = CM_ERROR_NOSUCHVOLUME;
790 lock_ObtainWrite(&cm_volumeLock);
791 cm_AdjustVolumeLRU(volp);
792 lock_ReleaseWrite(&cm_volumeLock);
799 void cm_ForceUpdateVolume(cm_fid_t *fidp, cm_user_t *userp, cm_req_t *reqp)
803 #ifdef SEARCH_ALL_VOLUMES
810 cellp = cm_FindCellByID(fidp->cell);
813 /* search for the volume */
814 lock_ObtainRead(&cm_volumeLock);
815 #ifdef SEARCH_ALL_VOLUMES
816 for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
817 if (cellp == volp->cellp &&
818 (fidp->volume == volp->rw.ID ||
819 fidp->volume == volp->ro.ID ||
820 fidp->volume == volp->bk.ID))
823 #endif /* SEARCH_ALL_VOLUMES */
825 hash = CM_VOLUME_ID_HASH(fidp->volume);
826 /* The volumeID can be any one of the three types. So we must
827 * search the hash table for all three types until we find it.
828 * We will search in the order of RO, RW, BK.
830 for ( volp = cm_data.volumeROIDHashTablep[hash]; volp; volp = volp->ro.nextp) {
831 if ( cellp == volp->cellp && fidp->volume == volp->ro.ID )
836 for ( volp = cm_data.volumeRWIDHashTablep[hash]; volp; volp = volp->rw.nextp) {
837 if ( cellp == volp->cellp && fidp->volume == volp->rw.ID )
843 for ( volp = cm_data.volumeBKIDHashTablep[hash]; volp; volp = volp->bk.nextp) {
844 if ( cellp == volp->cellp && fidp->volume == volp->bk.ID )
849 #ifdef SEARCH_ALL_VOLUMES
850 osi_assert(volp == volp2);
853 lock_ReleaseRead(&cm_volumeLock);
855 /* hold the volume if we found it */
860 cm_data.mountRootGen = time(NULL);
861 lock_ObtainMutex(&volp->mx);
862 volp->flags |= CM_VOLUMEFLAG_RESET;
864 /* Mark the volume to be updated but don't update it now.
865 * This function is called only from within cm_Analyze
866 * when cm_ConnByMServers has failed with all servers down
867 * The problem is that cm_UpdateVolume is going to call
868 * cm_ConnByMServers which may cause a recursive chain
869 * of calls each returning a retry on failure.
870 * Instead, set the flag so the next time the volume is
871 * accessed by Name or ID the UpdateVolume call will
874 code = cm_UpdateVolume(cellp, userp, reqp, volp);
876 volp->flags &= ~CM_VOLUMEFLAG_RESET;
878 lock_ReleaseMutex(&volp->mx);
883 /* find the appropriate servers from a volume */
884 cm_serverRef_t **cm_GetVolServers(cm_volume_t *volp, afs_uint32 volume)
886 cm_serverRef_t **serverspp;
887 cm_serverRef_t *current;;
889 lock_ObtainWrite(&cm_serverLock);
891 if (volume == volp->rw.ID)
892 serverspp = &volp->rw.serversp;
893 else if (volume == volp->ro.ID)
894 serverspp = &volp->ro.serversp;
895 else if (volume == volp->bk.ID)
896 serverspp = &volp->bk.serversp;
898 osi_panic("bad volume ID in cm_GetVolServers", __FILE__, __LINE__);
900 for (current = *serverspp; current; current = current->next)
903 lock_ReleaseWrite(&cm_serverLock);
908 void cm_PutVolume(cm_volume_t *volp)
910 lock_ObtainWrite(&cm_volumeLock);
911 osi_assert(volp->refCount-- > 0);
912 lock_ReleaseWrite(&cm_volumeLock);
915 /* return the read-only volume, if there is one, or the read-write volume if
918 long cm_GetROVolumeID(cm_volume_t *volp)
922 lock_ObtainMutex(&volp->mx);
923 if (volp->ro.ID && volp->ro.serversp)
927 lock_ReleaseMutex(&volp->mx);
932 void cm_RefreshVolumes(void)
937 cm_data.mountRootGen = time(NULL);
939 /* force a re-loading of volume data from the vldb */
940 lock_ObtainWrite(&cm_volumeLock);
941 for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
943 lock_ReleaseWrite(&cm_volumeLock);
945 lock_ObtainMutex(&volp->mx);
946 volp->flags |= CM_VOLUMEFLAG_RESET;
947 lock_ReleaseMutex(&volp->mx);
949 lock_ObtainWrite(&cm_volumeLock);
950 osi_assert(volp->refCount-- > 0);
952 lock_ReleaseWrite(&cm_volumeLock);
954 /* force mount points to be re-evaluated so that
955 * if the volume location has changed we will pick
958 for ( scp = cm_data.scacheLRUFirstp;
960 scp = (cm_scache_t *) osi_QNext(&scp->q)) {
961 if ( scp->fileType == CM_SCACHETYPE_MOUNTPOINT
962 #ifdef AFS_FREELANCE_CLIENT
963 && !(scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID)
966 lock_ObtainMutex(&scp->mx);
967 scp->mountPointStringp[0] = '\0';
968 lock_ReleaseMutex(&scp->mx);
975 /* The return code is 0 if the volume is not online and
976 * 1 if the volume is online
979 cm_CheckOfflineVolume(cm_volume_t *volp, afs_uint32 volID)
983 AFSFetchVolumeStatus volStat;
988 struct rx_connection * callp;
990 char offLineMsg[256];
993 cm_serverRef_t *serversp;
996 OfflineMsg = offLineMsg;
999 lock_ObtainMutex(&volp->mx);
1001 if (volp->rw.ID != 0 && (!volID || volID == volp->rw.ID) &&
1002 (volp->rw.state == vl_busy || volp->rw.state == vl_offline)) {
1005 for (serversp = volp->rw.serversp; serversp; serversp = serversp->next) {
1006 if (serversp->status == srv_busy || serversp->status == srv_offline)
1007 serversp->status = srv_not_busy;
1011 code = cm_ConnFromVolume(volp, volp->rw.ID, cm_rootUserp, &req, &connp);
1015 callp = cm_GetRxConn(connp);
1016 code = RXAFS_GetVolumeStatus(callp, volp->rw.ID,
1017 &volStat, &Name, &OfflineMsg, &MOTD);
1018 rx_PutConnection(callp);
1020 } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1021 code = cm_MapRPCError(code, &req);
1023 if (code == 0 && volStat.Online) {
1024 cm_VolumeStatusNotification(volp, volp->rw.ID, volp->rw.state, vl_online);
1025 volp->rw.state = vl_online;
1030 if (volp->ro.ID != 0 && (!volID || volID == volp->ro.ID) &&
1031 (volp->ro.state == vl_busy || volp->ro.state == vl_offline)) {
1034 for (serversp = volp->ro.serversp; serversp; serversp = serversp->next) {
1035 if (serversp->status == srv_busy || serversp->status == srv_offline)
1036 serversp->status = srv_not_busy;
1040 code = cm_ConnFromVolume(volp, volp->ro.ID, cm_rootUserp, &req, &connp);
1044 callp = cm_GetRxConn(connp);
1045 code = RXAFS_GetVolumeStatus(callp, volp->ro.ID,
1046 &volStat, &Name, &OfflineMsg, &MOTD);
1047 rx_PutConnection(callp);
1049 } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1050 code = cm_MapRPCError(code, &req);
1052 if (code == 0 && volStat.Online) {
1053 cm_VolumeStatusNotification(volp, volp->ro.ID, volp->ro.state, vl_online);
1054 volp->ro.state = vl_online;
1059 if (volp->bk.ID != 0 && (!volID || volID == volp->bk.ID) &&
1060 (volp->bk.state == vl_busy || volp->bk.state == vl_offline)) {
1063 for (serversp = volp->bk.serversp; serversp; serversp = serversp->next) {
1064 if (serversp->status == srv_busy || serversp->status == srv_offline)
1065 serversp->status = srv_not_busy;
1069 code = cm_ConnFromVolume(volp, volp->bk.ID, cm_rootUserp, &req, &connp);
1073 callp = cm_GetRxConn(connp);
1074 code = RXAFS_GetVolumeStatus(callp, volp->bk.ID,
1075 &volStat, &Name, &OfflineMsg, &MOTD);
1076 rx_PutConnection(callp);
1078 } while (cm_Analyze(connp, cm_rootUserp, &req, NULL, NULL, NULL, NULL, code));
1079 code = cm_MapRPCError(code, &req);
1081 if (code == 0 && volStat.Online) {
1082 cm_VolumeStatusNotification(volp, volp->bk.ID, volp->bk.state, vl_online);
1083 volp->bk.state = vl_online;
1088 lock_ReleaseMutex(&volp->mx);
1093 /* called from the Daemon thread */
1094 void cm_CheckOfflineVolumes(void)
1098 lock_ObtainWrite(&cm_volumeLock);
1099 for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) {
1101 lock_ReleaseWrite(&cm_volumeLock);
1103 cm_CheckOfflineVolume(volp, 0);
1105 lock_ObtainWrite(&cm_volumeLock);
1106 osi_assert(volp->refCount-- > 0);
1108 lock_ReleaseWrite(&cm_volumeLock);
1112 cm_UpdateVolumeStatus(cm_volume_t *volp, afs_uint32 volID)
1114 struct cm_vol_state * statep = NULL;
1115 enum volstatus newStatus;
1116 cm_serverRef_t *tsrp;
1118 int someBusy = 0, someOffline = 0, allOffline = 1, allBusy = 1, allDown = 1;
1120 if (volp->rw.ID == volID) {
1122 } else if (volp->ro.ID == volID) {
1124 } else if (volp->bk.ID == volID) {
1135 lock_ObtainWrite(&cm_serverLock);
1136 for (tsrp = statep->serversp; tsrp; tsrp=tsrp->next) {
1138 cm_GetServerNoLock(tsp);
1139 if (!(tsp->flags & CM_SERVERFLAG_DOWN)) {
1141 if (tsrp->status == srv_busy) {
1144 } else if (tsrp->status == srv_offline) {
1152 cm_PutServerNoLock(tsp);
1154 lock_ReleaseWrite(&cm_serverLock);
1157 newStatus = vl_alldown;
1158 else if (allBusy || (someBusy && someOffline))
1159 newStatus = vl_busy;
1160 else if (allOffline)
1161 newStatus = vl_offline;
1163 newStatus = vl_online;
1166 if (statep->ID && statep->state != newStatus)
1167 cm_VolumeStatusNotification(volp, statep->ID, statep->state, newStatus);
1169 statep->state = newStatus;
1173 ** Finds all volumes that reside on this server and reorders their
1174 ** RO list according to the changed rank of server.
1176 void cm_ChangeRankVolume(cm_server_t *tsp)
1181 /* find volumes which might have RO copy on server*/
1182 lock_ObtainWrite(&cm_volumeLock);
1183 for(volp = cm_data.allVolumesp; volp; volp=volp->allNextp)
1185 code = 1 ; /* assume that list is unchanged */
1187 lock_ReleaseWrite(&cm_volumeLock);
1188 lock_ObtainMutex(&volp->mx);
1190 if ((tsp->cellp==volp->cellp) && (volp->ro.serversp))
1191 code =cm_ChangeRankServer(&volp->ro.serversp, tsp);
1193 /* this volume list was changed */
1195 cm_RandomizeServer(&volp->ro.serversp);
1197 lock_ReleaseMutex(&volp->mx);
1198 lock_ObtainWrite(&cm_volumeLock);
1199 osi_assert(volp->refCount-- > 0);
1201 lock_ReleaseWrite(&cm_volumeLock);
1204 /* dump all volumes that have reference count > 0 to a file.
1205 * cookie is used to identify this batch for easy parsing,
1206 * and it a string provided by a caller
1208 int cm_DumpVolumes(FILE *outputFile, char *cookie, int lock)
1215 lock_ObtainRead(&cm_scacheLock);
1216 lock_ObtainRead(&cm_volumeLock);
1219 sprintf(output, "%s - dumping volumes - cm_data.currentVolumes=%d, cm_data.maxVolumes=%d\r\n", cookie, cm_data.currentVolumes, cm_data.maxVolumes);
1220 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1222 for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp)
1227 for (scp = cm_data.allSCachesp; scp; scp = scp->allNextp)
1229 if (scp->volp == volp)
1233 sprintf(output, "%s - volp=0x%p cell=%s name=%s rwID=%u roID=%u bkID=%u flags=0x%x fid (cell=%d, volume=%d, vnode=%d, unique=%d) refCount=%u scpRefs=%u\r\n",
1234 cookie, volp, volp->cellp->name, volp->namep, volp->rw.ID, volp->ro.ID, volp->bk.ID, volp->flags,
1235 volp->dotdotFid.cell, volp->dotdotFid.volume, volp->dotdotFid.vnode, volp->dotdotFid.unique,
1236 volp->refCount, scprefs);
1237 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1239 sprintf(output, "%s - Done dumping volumes.\r\n", cookie);
1240 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
1243 lock_ReleaseRead(&cm_volumeLock);
1244 lock_ReleaseRead(&cm_scacheLock);
1251 * String hash function used by SDBM project.
1252 * It was chosen because it is fast and provides
1255 afs_uint32 SDBMHash(const char * str)
1257 afs_uint32 hash = 0;
1263 for(i = 0, len = strlen(str); i < len; i++)
1265 hash = str[i] + (hash << 6) + (hash << 16) - hash;
1268 return (hash & 0x7FFFFFFF);
1271 /* call with volume write-locked and mutex held */
1272 void cm_AddVolumeToNameHashTable(cm_volume_t *volp)
1276 if (volp->flags & CM_VOLUMEFLAG_IN_HASH)
1279 i = CM_VOLUME_NAME_HASH(volp->namep);
1281 volp->nameNextp = cm_data.volumeNameHashTablep[i];
1282 cm_data.volumeNameHashTablep[i] = volp;
1283 volp->flags |= CM_VOLUMEFLAG_IN_HASH;
1286 /* call with volume write-locked and mutex held */
1287 void cm_RemoveVolumeFromNameHashTable(cm_volume_t *volp)
1289 cm_volume_t **lvolpp;
1293 if (volp->flags & CM_VOLUMEFLAG_IN_HASH) {
1294 /* hash it out first */
1295 i = CM_VOLUME_NAME_HASH(volp->namep);
1296 for (lvolpp = &cm_data.volumeNameHashTablep[i], tvolp = cm_data.volumeNameHashTablep[i];
1298 lvolpp = &tvolp->nameNextp, tvolp = tvolp->nameNextp) {
1299 if (tvolp == volp) {
1300 *lvolpp = volp->nameNextp;
1301 volp->flags &= ~CM_VOLUMEFLAG_IN_HASH;
1302 volp->nameNextp = NULL;
1309 /* call with volume write-locked and mutex held */
1310 void cm_AddVolumeToIDHashTable(cm_volume_t *volp, afs_uint32 volType)
1313 struct cm_vol_state * statep;
1329 if (statep->flags & CM_VOLUMEFLAG_IN_HASH)
1332 i = CM_VOLUME_ID_HASH(statep->ID);
1336 statep->nextp = cm_data.volumeRWIDHashTablep[i];
1337 cm_data.volumeRWIDHashTablep[i] = volp;
1340 statep->nextp = cm_data.volumeROIDHashTablep[i];
1341 cm_data.volumeROIDHashTablep[i] = volp;
1344 statep->nextp = cm_data.volumeBKIDHashTablep[i];
1345 cm_data.volumeBKIDHashTablep[i] = volp;
1348 statep->flags |= CM_VOLUMEFLAG_IN_HASH;
1352 /* call with volume write-locked and mutex held */
1353 void cm_RemoveVolumeFromIDHashTable(cm_volume_t *volp, afs_uint32 volType)
1355 cm_volume_t **lvolpp;
1357 struct cm_vol_state * statep;
1374 if (statep->flags & CM_VOLUMEFLAG_IN_HASH) {
1375 /* hash it out first */
1376 i = CM_VOLUME_ID_HASH(statep->ID);
1380 lvolpp = &cm_data.volumeRWIDHashTablep[i];
1381 tvolp = cm_data.volumeRWIDHashTablep[i];
1384 lvolpp = &cm_data.volumeROIDHashTablep[i];
1385 tvolp = cm_data.volumeROIDHashTablep[i];
1388 lvolpp = &cm_data.volumeBKIDHashTablep[i];
1389 tvolp = cm_data.volumeBKIDHashTablep[i];
1393 if (tvolp == volp) {
1394 *lvolpp = statep->nextp;
1395 statep->flags &= ~CM_VOLUMEFLAG_IN_HASH;
1396 statep->nextp = NULL;
1402 lvolpp = &tvolp->rw.nextp;
1403 tvolp = tvolp->rw.nextp;
1406 lvolpp = &tvolp->ro.nextp;
1407 tvolp = tvolp->ro.nextp;
1410 lvolpp = &tvolp->bk.nextp;
1411 tvolp = tvolp->bk.nextp;
1418 /* must be called with cm_volumeLock write-locked! */
1419 void cm_AdjustVolumeLRU(cm_volume_t *volp)
1421 if (volp == cm_data.volumeLRULastp)
1422 cm_data.volumeLRULastp = (cm_volume_t *) osi_QPrev(&volp->q);
1423 if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE)
1424 osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1425 osi_QAdd((osi_queue_t **) &cm_data.volumeLRUFirstp, &volp->q);
1426 volp->flags |= CM_VOLUMEFLAG_IN_LRU_QUEUE;
1427 if (!cm_data.volumeLRULastp)
1428 cm_data.volumeLRULastp = volp;
1431 /* must be called with cm_volumeLock write-locked! */
1432 void cm_RemoveVolumeFromLRU(cm_volume_t *volp)
1434 if (volp->flags & CM_VOLUMEFLAG_IN_LRU_QUEUE) {
1435 if (volp == cm_data.volumeLRULastp)
1436 cm_data.volumeLRULastp = (cm_volume_t *) osi_QPrev(&volp->q);
1437 osi_QRemoveHT((osi_queue_t **) &cm_data.volumeLRUFirstp, (osi_queue_t **) &cm_data.volumeLRULastp, &volp->q);
1438 volp->flags &= ~CM_VOLUMEFLAG_IN_LRU_QUEUE;
1442 static char * volstatus_str(enum volstatus vs)
1458 void cm_VolumeStatusNotification(cm_volume_t * volp, afs_uint32 volID, enum volstatus old, enum volstatus new)
1460 char volstr[CELL_MAXNAMELEN + VL_MAXNAMELEN]="";
1463 if (volID == volp->rw.ID)
1465 else if (volID == volp->ro.ID)
1467 else if (volID == volp->bk.ID)
1471 snprintf(volstr, sizeof(volstr), "%s:%s%s", volp->cellp->name, volp->namep, ext);
1473 osi_Log4(afsd_logp, "VolumeStatusNotification: %-48s [%10u] (%s -> %s)",
1474 volstr, volID, volstatus_str(old), volstatus_str(new));
1476 cm_VolStatus_Change_Notification(volp->cellp->cellID, volID, new);