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>
22 osi_rwlock_t cm_volumeLock;
25 cm_ValidateVolume(void)
30 for (volp = cm_data.allVolumesp, count = 0; volp; volp=volp->nextp, count++) {
31 if ( volp->magic != CM_VOLUME_MAGIC ) {
32 afsi_log("cm_ValidateVolume failure: volp->magic != CM_VOLUME_MAGIC");
33 fprintf(stderr, "cm_ValidateVolume failure: volp->magic != CM_VOLUME_MAGIC\n");
36 if ( volp->cellp && volp->cellp->magic != CM_CELL_MAGIC ) {
37 afsi_log("cm_ValidateVolume failure: volp->cellp->magic != CM_CELL_MAGIC");
38 fprintf(stderr, "cm_ValidateVolume failure: volp->cellp->magic != CM_CELL_MAGIC\n");
41 if ( volp->nextp && volp->nextp->magic != CM_VOLUME_MAGIC ) {
42 afsi_log("cm_ValidateVolume failure: volp->nextp->magic != CM_VOLUME_MAGIC");
43 fprintf(stderr, "cm_ValidateVolume failure: volp->nextp->magic != CM_VOLUME_MAGIC\n");
46 if ( count != 0 && volp == cm_data.allVolumesp ||
47 count > cm_data.maxVolumes ) {
48 afsi_log("cm_ValidateVolume failure: cm_data.allVolumep loop detected");
49 fprintf(stderr, "cm_ValidateVolume failure: cm_data.allVolumep loop detected\n");
54 if ( count != cm_data.currentVolumes ) {
55 afsi_log("cm_ValidateVolume failure: count != cm_data.currentVolumes");
56 fprintf(stderr, "cm_ValidateVolume failure: count != cm_data.currentVolumes\n");
64 cm_ShutdownVolume(void)
68 for (volp = cm_data.allVolumesp; volp; volp=volp->nextp)
69 lock_FinalizeMutex(&volp->mx);
74 void cm_InitVolume(int newFile, long maxVols)
76 static osi_once_t once;
78 if (osi_Once(&once)) {
79 lock_InitializeRWLock(&cm_volumeLock, "cm global volume lock");
82 cm_data.allVolumesp = NULL;
83 cm_data.currentVolumes = 0;
84 cm_data.maxVolumes = maxVols;
88 for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
89 lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
90 volp->flags |= CM_VOLUMEFLAG_RESET;
91 volp->rwServersp = NULL;
92 volp->roServersp = NULL;
93 volp->bkServersp = NULL;
101 * Update a volume. Caller holds volume's lock (volp->mx).
104 * shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU} 01:38 (JHutz)
105 * Yes, we support multihomed fileservers.
106 * Since before we got the code from IBM.
107 * But to find out about multiple addresses on a multihomed server, you need
108 * to use VL_GetEntryByNameU and VL_GetAddrsU. If you use
109 * VL_GetEntryByNameO or VL_GetEntryByNameN, the vlserver just gives you one
110 * address per server.
111 * shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU} 01:39 (JHutz)
112 * see src/afs/afs_volume.c, paying particular attention to
113 * afs_NewVolumeByName, afs_SetupVolume, and InstallUVolumeEntry
114 * shadow / openafs / jaltman {ANDREW.CMU.EDU} 01:40 (Jeffrey Altman)
115 * thanks. The windows client calls the 0 versions.
116 * shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU} 01:51 (JHutz)
118 * By not using the N versions, you only get up to 8 sites instead of 13.
119 * By not using the U versions, you don't get to know about multihomed serve
120 * shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU} 01:52 (JHutz)
121 * Of course, you probably want to support the older versions for backward
122 * compatibility. If you do that, you need to call the newest interface
123 * first, and fall back to successively older versions if you get
127 long cm_UpdateVolume(struct cm_cell *cellp, cm_user_t *userp, cm_req_t *reqp,
132 cm_serverRef_t *tsrp;
134 struct sockaddr_in tsockAddr;
137 struct vldbentry vldbEntry;
138 struct nvldbentry nvldbEntry;
140 struct uvldbentry uvldbEntry;
146 /* clear out old bindings */
147 if (volp->rwServersp)
148 cm_FreeServerList(&volp->rwServersp);
149 if (volp->roServersp)
150 cm_FreeServerList(&volp->roServersp);
151 if (volp->bkServersp)
152 cm_FreeServerList(&volp->bkServersp);
154 #ifdef AFS_FREELANCE_CLIENT
155 if ( cellp->cellID == AFS_FAKE_ROOT_CELL_ID && atoi(volp->namep)==AFS_FAKE_ROOT_VOL_ID )
157 memset(&vldbEntry, 0, sizeof(vldbEntry));
158 vldbEntry.flags |= VLF_RWEXISTS;
159 vldbEntry.volumeId[0] = AFS_FAKE_ROOT_VOL_ID;
165 /* now we have volume structure locked and held; make RPC to fill it */
166 osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s", volp->cellp->name, volp->namep);
168 code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
172 code = VL_GetEntryByNameU(connp->callp, volp->namep, &uvldbEntry);
174 if ( code == RXGEN_OPCODE )
177 code = VL_GetEntryByNameN(connp->callp, volp->namep, &nvldbEntry);
180 if ( code == RXGEN_OPCODE ) {
181 code = VL_GetEntryByNameO(connp->callp, volp->namep, &vldbEntry);
184 } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
185 code = cm_MapVLRPCError(code, reqp);
187 osi_Log3(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s FAILURE, code 0x%x",
188 volp->cellp->name, volp->namep, code);
190 osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s SUCCESS",
191 volp->cellp->name, volp->namep);
199 afs_int32 serverNumber[NMAXNSERVERS];
200 afs_int32 serverFlags[NMAXNSERVERS];
204 flags = vldbEntry.flags;
205 nServers = vldbEntry.nServers;
206 rwID = vldbEntry.volumeId[0];
207 roID = vldbEntry.volumeId[1];
208 bkID = vldbEntry.volumeId[2];
209 for ( i=0; i<nServers; i++ ) {
210 serverFlags[i] = vldbEntry.serverFlags[i];
211 serverNumber[i] = vldbEntry.serverNumber[i];
215 flags = nvldbEntry.flags;
216 nServers = nvldbEntry.nServers;
217 rwID = nvldbEntry.volumeId[0];
218 roID = nvldbEntry.volumeId[1];
219 bkID = nvldbEntry.volumeId[2];
220 for ( i=0; i<nServers; i++ ) {
221 serverFlags[i] = nvldbEntry.serverFlags[i];
222 serverNumber[i] = nvldbEntry.serverNumber[i];
227 flags = uvldbEntry.flags;
228 nServers = uvldbEntry.nServers;
229 rwID = uvldbEntry.volumeId[0];
230 roID = uvldbEntry.volumeId[1];
231 bkID = uvldbEntry.volumeId[2];
232 for ( i=0, j=0; i<nServers && j<NMAXNSERVERS; i++ ) {
233 if ( !(uvldbEntry.serverFlags[i] & VLSERVER_FLAG_UUID) ) {
234 serverFlags[j] = uvldbEntry.serverFlags[i];
235 serverNumber[j] = uvldbEntry.serverNumber[i].time_low;
238 afs_uint32 * addrp, nentries, code, unique;
240 ListAddrByAttributes attrs;
243 memset((char *)&attrs, 0, sizeof(attrs));
244 attrs.Mask = VLADDR_UUID;
245 attrs.uuid = uvldbEntry.serverNumber[i];
246 memset((char *)&uuid, 0, sizeof(uuid));
247 memset((char *)&addrs, 0, sizeof(addrs));
250 code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
254 code = VL_GetAddrsU(connp->callp, &attrs, &uuid, &unique, &nentries, &addrs);
256 if (code == 0 && nentries == 0)
258 } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
259 code = cm_MapVLRPCError(code, reqp);
263 addrp = addrs.bulkaddrs_val;
264 for (k = 0; k < nentries && j < NMAXNSERVERS; j++, k++) {
265 serverFlags[j] = uvldbEntry.serverFlags[i];
266 serverNumber[j] = addrp[k];
269 free(addrs.bulkaddrs_val); /* This is wrong */
272 nServers = j; /* update the server count */
277 /* decode the response */
278 lock_ObtainWrite(&cm_volumeLock);
279 if (flags & VLF_RWEXISTS)
283 if (flags & VLF_ROEXISTS)
287 if (flags & VLF_BACKEXISTS)
291 lock_ReleaseWrite(&cm_volumeLock);
292 for (i=0; i<nServers; i++) {
293 /* create a server entry */
294 tflags = serverFlags[i];
295 if (tflags & VLSF_DONTUSE)
297 tsockAddr.sin_family = AF_INET;
298 tempAddr = htonl(serverNumber[i]);
299 tsockAddr.sin_addr.s_addr = tempAddr;
300 tsp = cm_FindServer(&tsockAddr, CM_SERVER_FILE);
302 tsp = cm_NewServer(&tsockAddr, CM_SERVER_FILE,
305 /* if this server was created by fs setserverprefs */
309 osi_assert(tsp != NULL);
311 /* and add it to the list(s). */
313 * Each call to cm_NewServerRef() increments the
314 * ref count of tsp. These reference will be dropped,
315 * if and when the volume is reset; see reset code
316 * earlier in this function.
318 if ((tflags & VLSF_RWVOL) && (flags & VLF_RWEXISTS)) {
319 tsrp = cm_NewServerRef(tsp);
320 cm_InsertServerList(&volp->rwServersp, tsrp);
321 lock_ObtainWrite(&cm_serverLock);
322 tsrp->refCount--; /* drop allocation reference */
323 lock_ReleaseWrite(&cm_serverLock);
325 if ((tflags & VLSF_ROVOL) && (flags & VLF_ROEXISTS)) {
326 tsrp = cm_NewServerRef(tsp);
327 cm_InsertServerList(&volp->roServersp, tsrp);
328 lock_ObtainWrite(&cm_serverLock);
329 tsrp->refCount--; /* drop allocation reference */
330 lock_ReleaseWrite(&cm_serverLock);
333 /* We don't use VLSF_BACKVOL !?! */
334 if ((tflags & VLSF_RWVOL) && (flags & VLF_BACKEXISTS)) {
335 tsrp = cm_NewServerRef(tsp);
336 cm_InsertServerList(&volp->bkServersp, tsrp);
337 lock_ObtainWrite(&cm_serverLock);
338 tsrp->refCount--; /* drop allocation reference */
339 lock_ReleaseWrite(&cm_serverLock);
341 /* Drop the reference obtained by cm_FindServer() */
348 * If the first n servers have the same ipRank, then we
349 * randomly pick one among them and move it to the beginning.
350 * We don't bother to re-order the whole list because
351 * the rest of the list is used only if the first server is
352 * down. We only do this for the RO list; we assume the other
353 * lists are length 1.
356 cm_RandomizeServer(&volp->roServersp);
362 void cm_GetVolume(cm_volume_t *volp)
365 lock_ObtainWrite(&cm_volumeLock);
367 lock_ReleaseWrite(&cm_volumeLock);
371 long cm_GetVolumeByID(cm_cell_t *cellp, long volumeID, cm_user_t *userp,
372 cm_req_t *reqp, cm_volume_t **outVolpp)
375 char volNameString[100];
378 lock_ObtainWrite(&cm_volumeLock);
379 for(volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
380 if (cellp == volp->cellp &&
381 ((unsigned) volumeID == volp->rwID ||
382 (unsigned) volumeID == volp->roID ||
383 (unsigned) volumeID == volp->bkID))
387 /* hold the volume if we found it */
390 lock_ReleaseWrite(&cm_volumeLock);
394 lock_ObtainMutex(&volp->mx);
397 if (volp->flags & CM_VOLUMEFLAG_RESET) {
398 code = cm_UpdateVolume(cellp, userp, reqp, volp);
400 volp->flags &= ~CM_VOLUMEFLAG_RESET;
402 lock_ReleaseMutex(&volp->mx);
411 /* otherwise, we didn't find it so consult the VLDB */
412 sprintf(volNameString, "%u", volumeID);
413 code = cm_GetVolumeByName(cellp, volNameString, userp, reqp,
418 long cm_GetVolumeByName(struct cm_cell *cellp, char *volumeNamep,
419 struct cm_user *userp, struct cm_req *reqp,
420 long flags, cm_volume_t **outVolpp)
425 lock_ObtainWrite(&cm_volumeLock);
426 for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
427 if (cellp == volp->cellp && strcmp(volumeNamep, volp->namep) == 0) {
432 /* otherwise, get from VLDB */
434 if ( cm_data.currentVolumes >= cm_data.maxVolumes ) {
435 for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
436 if ( volp->refCount == 0 ) {
437 /* There is one we can re-use */
442 osi_panic("Exceeded Max Volumes", __FILE__, __LINE__);
446 volp->rwID = volp->roID = volp->bkID = 0;
447 volp->dotdotFid.cell = 0;
448 volp->dotdotFid.volume = 0;
449 volp->dotdotFid.unique = 0;
450 volp->dotdotFid.vnode = 0;
452 volp = &cm_data.volumeBaseAddress[cm_data.currentVolumes++];
453 memset(volp, 0, sizeof(cm_volume_t));
454 volp->magic = CM_VOLUME_MAGIC;
455 volp->nextp = cm_data.allVolumesp;
456 cm_data.allVolumesp = volp;
457 lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
460 strncpy(volp->namep, volumeNamep, VL_MAXNAMELEN);
461 volp->namep[VL_MAXNAMELEN-1] = '\0';
462 volp->refCount = 1; /* starts off held */
463 volp->flags = CM_VOLUMEFLAG_RESET;
469 /* next should work since no one could have gotten ptr to this structure yet */
470 lock_ReleaseWrite(&cm_volumeLock);
471 lock_ObtainMutex(&volp->mx);
473 if (volp->flags & CM_VOLUMEFLAG_RESET) {
474 code = cm_UpdateVolume(cellp, userp, reqp, volp);
476 volp->flags &= ~CM_VOLUMEFLAG_RESET;
484 lock_ReleaseMutex(&volp->mx);
488 void cm_ForceUpdateVolume(cm_fid_t *fidp, cm_user_t *userp, cm_req_t *reqp)
495 cellp = cm_FindCellByID(fidp->cell);
498 /* search for the volume */
499 lock_ObtainWrite(&cm_volumeLock);
500 for(volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
501 if (cellp == volp->cellp &&
502 (fidp->volume == volp->rwID ||
503 fidp->volume == volp->roID ||
504 fidp->volume == volp->bkID))
508 /* hold the volume if we found it */
511 lock_ReleaseWrite(&cm_volumeLock);
514 cm_data.mountRootGen = time(NULL);
515 lock_ObtainMutex(&volp->mx);
516 volp->flags |= CM_VOLUMEFLAG_RESET;
518 /* Mark the volume to be updated but don't update it now.
519 * This function is called only from within cm_Analyze
520 * when cm_ConnByMServers has failed with all servers down
521 * The problem is that cm_UpdateVolume is going to call
522 * cm_ConnByMServers which may cause a recursive chain
523 * of calls each returning a retry on failure.
524 * Instead, set the flag so the next time the volume is
525 * accessed by Name or ID the UpdateVolume call will
528 code = cm_UpdateVolume(cellp, userp, reqp, volp);
530 volp->flags &= ~CM_VOLUMEFLAG_RESET;
532 lock_ReleaseMutex(&volp->mx);
537 /* find the appropriate servers from a volume */
538 cm_serverRef_t **cm_GetVolServers(cm_volume_t *volp, unsigned long volume)
540 cm_serverRef_t **serverspp;
541 cm_serverRef_t *current;;
543 lock_ObtainWrite(&cm_serverLock);
545 if (volume == volp->rwID)
546 serverspp = &volp->rwServersp;
547 else if (volume == volp->roID)
548 serverspp = &volp->roServersp;
549 else if (volume == volp->bkID)
550 serverspp = &volp->bkServersp;
552 osi_panic("bad volume ID in cm_GetVolServers", __FILE__, __LINE__);
554 for (current = *serverspp; current; current = current->next)
557 lock_ReleaseWrite(&cm_serverLock);
562 void cm_PutVolume(cm_volume_t *volp)
564 lock_ObtainWrite(&cm_volumeLock);
565 osi_assert(volp->refCount-- > 0);
566 lock_ReleaseWrite(&cm_volumeLock);
569 /* return the read-only volume, if there is one, or the read-write volume if
572 long cm_GetROVolumeID(cm_volume_t *volp)
576 lock_ObtainMutex(&volp->mx);
577 if (volp->roID && volp->roServersp)
581 lock_ReleaseMutex(&volp->mx);
586 void cm_CheckVolumes(void)
590 cm_data.mountRootGen = time(NULL);
591 lock_ObtainWrite(&cm_volumeLock);
592 for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
594 lock_ReleaseWrite(&cm_volumeLock);
595 lock_ObtainMutex(&volp->mx);
597 volp->flags |= CM_VOLUMEFLAG_RESET;
599 lock_ReleaseMutex(&volp->mx);
600 lock_ObtainWrite(&cm_volumeLock);
601 osi_assert(volp->refCount-- > 0);
603 lock_ReleaseWrite(&cm_volumeLock);
605 /* We should also refresh cached mount points */
609 ** Finds all volumes that reside on this server and reorders their
610 ** RO list according to the changed rank of server.
612 void cm_ChangeRankVolume(cm_server_t *tsp)
617 /* find volumes which might have RO copy on server*/
618 lock_ObtainWrite(&cm_volumeLock);
619 for(volp = cm_data.allVolumesp; volp; volp=volp->nextp)
621 code = 1 ; /* assume that list is unchanged */
623 lock_ReleaseWrite(&cm_volumeLock);
624 lock_ObtainMutex(&volp->mx);
626 if ((tsp->cellp==volp->cellp) && (volp->roServersp))
627 code =cm_ChangeRankServer(&volp->roServersp, tsp);
629 /* this volume list was changed */
631 cm_RandomizeServer(&volp->roServersp);
633 lock_ReleaseMutex(&volp->mx);
634 lock_ObtainWrite(&cm_volumeLock);
635 osi_assert(volp->refCount-- > 0);
637 lock_ReleaseWrite(&cm_volumeLock);
640 /* dump all scp's that have reference count > 0 to a file.
641 * cookie is used to identify this batch for easy parsing,
642 * and it a string provided by a caller
644 int cm_DumpVolumes(FILE *outputFile, char *cookie, int lock)
651 lock_ObtainRead(&cm_scacheLock);
652 lock_ObtainRead(&cm_volumeLock);
655 sprintf(output, "%s - dumping volumes - cm_data.currentVolumes=%d, cm_data.maxVolumes=%d\n", cookie, cm_data.currentVolumes, cm_data.maxVolumes);
656 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
658 for (volp = cm_data.allVolumesp; volp; volp=volp->nextp)
660 if (volp->refCount != 0)
665 for (scp = cm_data.scacheLRULastp; scp; scp = (cm_scache_t *) osi_QPrev(&scp->q))
667 if (scp->volp == volp)
671 sprintf(output, "%s 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\n",
672 cookie, volp->cellp->name, volp->namep, volp->rwID, volp->roID, volp->bkID, volp->flags,
673 volp->dotdotFid.cell, volp->dotdotFid.volume, volp->dotdotFid.vnode, volp->dotdotFid.unique,
674 volp->refCount, scprefs);
675 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
678 sprintf(output, "%s - Done dumping volumes.\n", cookie);
679 WriteFile(outputFile, output, (DWORD)strlen(output), &zilch, NULL);
682 lock_ReleaseRead(&cm_volumeLock);
683 lock_ReleaseRead(&cm_scacheLock);