windows-largefile-support-20060623
[openafs.git] / src / WINNT / afsd / cm_volume.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
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
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #ifndef DJGPP
14 #include <windows.h>
15 #include <winsock2.h>
16 #include <nb30.h>
17 #else
18 #include <sys/socket.h>
19 #endif /* !DJGPP */
20 #include <string.h>
21 #include <malloc.h>
22 #include "afsd.h"
23 #include <osi.h>
24 #include <rx/rx.h>
25
26 osi_rwlock_t cm_volumeLock;
27
28 long 
29 cm_ValidateVolume(void)
30 {
31     cm_volume_t * volp;
32     afs_uint32 count;
33
34     for (volp = cm_data.allVolumesp, count = 0; volp; volp=volp->nextp, 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");
38             return -1;
39         }
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");
43             return -2;
44         }
45         if ( volp->nextp && volp->nextp->magic != CM_VOLUME_MAGIC ) {
46             afsi_log("cm_ValidateVolume failure: volp->nextp->magic != CM_VOLUME_MAGIC");
47             fprintf(stderr, "cm_ValidateVolume failure: volp->nextp->magic != CM_VOLUME_MAGIC\n");
48             return -3;
49         }
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");
54             return -4;
55         }
56     }
57
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");
61         return -5;
62     }
63     
64     return 0;
65 }
66
67 long
68 cm_ShutdownVolume(void)
69 {
70     cm_volume_t * volp;
71
72     for (volp = cm_data.allVolumesp; volp; volp=volp->nextp)
73         lock_FinalizeMutex(&volp->mx);
74
75     return 0;
76 }
77
78 void cm_InitVolume(int newFile, long maxVols)
79 {
80     static osi_once_t once;
81
82     if (osi_Once(&once)) {
83         lock_InitializeRWLock(&cm_volumeLock, "cm global volume lock");
84
85         if ( newFile ) {
86             cm_data.allVolumesp = NULL;
87             cm_data.currentVolumes = 0;
88             cm_data.maxVolumes = maxVols;
89         } else {
90             cm_volume_t * volp;
91
92             for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
93                 lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
94                 volp->flags |= CM_VOLUMEFLAG_RESET;
95                 volp->rwServersp = NULL;
96                 volp->roServersp = NULL;
97                 volp->bkServersp = NULL;
98             }
99         }
100         osi_EndOnce(&once);
101     }
102 }
103
104 /*
105  * Update a volume.  Caller holds volume's lock (volp->mx).
106  *
107  *
108  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:38    (JHutz)
109  *    Yes, we support multihomed fileservers.
110  *    Since before we got the code from IBM.
111  *    But to find out about multiple addresses on a multihomed server, you need
112  *    to use VL_GetEntryByNameU and VL_GetAddrsU.  If you use
113  *    VL_GetEntryByNameO or VL_GetEntryByNameN, the vlserver just gives you one
114  *    address per server.
115  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:39    (JHutz)
116  *    see src/afs/afs_volume.c, paying particular attention to
117  *    afs_NewVolumeByName, afs_SetupVolume, and InstallUVolumeEntry
118  *  shadow / openafs / jaltman {ANDREW.CMU.EDU}  01:40    (Jeffrey Altman)
119  *    thanks.  The windows client calls the 0 versions.
120  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:51    (JHutz)
121  *    Oh.  Ew.
122  *    By not using the N versions, you only get up to 8 sites instead of 13.
123  *    By not using the U versions, you don't get to know about multihomed serve
124  *  shadow / openafs / jhutz@CS.CMU.EDU {ANDREW.CMU.EDU}  01:52    (JHutz)
125  *    Of course, you probably want to support the older versions for backward
126  *    compatibility.  If you do that, you need to call the newest interface
127  *    first, and fall back to successively older versions if you get
128  *    RXGEN_OPCODE.
129  */
130 #define MULTIHOMED 1
131 long cm_UpdateVolume(struct cm_cell *cellp, cm_user_t *userp, cm_req_t *reqp,
132         cm_volume_t *volp)
133 {
134     cm_conn_t *connp;
135     int i, j, k;
136     cm_serverRef_t *tsrp;
137     cm_server_t *tsp;
138     struct sockaddr_in tsockAddr;
139     long tflags;
140     u_long tempAddr;
141     struct vldbentry vldbEntry;
142     struct nvldbentry nvldbEntry;
143 #ifdef MULTIHOMED
144     struct uvldbentry uvldbEntry;
145 #endif
146     int type = -1;
147     int ROcount = 0;
148     long code;
149
150     /* clear out old bindings */
151     if (volp->rwServersp)
152         cm_FreeServerList(&volp->rwServersp);
153     if (volp->roServersp)
154         cm_FreeServerList(&volp->roServersp);
155     if (volp->bkServersp)
156         cm_FreeServerList(&volp->bkServersp);
157
158 #ifdef AFS_FREELANCE_CLIENT
159     if ( cellp->cellID == AFS_FAKE_ROOT_CELL_ID && atoi(volp->namep)==AFS_FAKE_ROOT_VOL_ID ) 
160     {
161         memset(&vldbEntry, 0, sizeof(vldbEntry));
162         vldbEntry.flags |= VLF_RWEXISTS;
163         vldbEntry.volumeId[0] = AFS_FAKE_ROOT_VOL_ID;
164         code = 0;
165         type = 0;
166     } else
167 #endif
168     {
169         /* now we have volume structure locked and held; make RPC to fill it */
170         osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s", volp->cellp->name, volp->namep);
171         do {
172             code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
173             if (code) 
174                 continue;
175 #ifdef MULTIHOMED
176             code = VL_GetEntryByNameU(connp->callp, volp->namep, &uvldbEntry);
177             type = 2;
178             if ( code == RXGEN_OPCODE ) 
179 #endif
180             {
181                 code = VL_GetEntryByNameN(connp->callp, volp->namep, &nvldbEntry);
182                 type = 1;
183             }
184             if ( code == RXGEN_OPCODE ) {
185                 code = VL_GetEntryByNameO(connp->callp, volp->namep, &vldbEntry);
186                 type = 0;
187             }
188         } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
189         code = cm_MapVLRPCError(code, reqp);
190         if ( code )
191             osi_Log3(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s FAILURE, code 0x%x", 
192                       volp->cellp->name, volp->namep, code);
193         else
194             osi_Log2(afsd_logp, "CALL VL_GetEntryByName{UNO} name %s:%s SUCCESS", 
195                       volp->cellp->name, volp->namep);
196     }
197     if (code == 0) {
198         afs_int32 flags;
199         afs_int32 nServers;
200         afs_int32 rwID;
201         afs_int32 roID;
202         afs_int32 bkID;
203         afs_int32 serverNumber[NMAXNSERVERS];
204         afs_int32 serverFlags[NMAXNSERVERS];
205
206         switch ( type ) {
207         case 0:
208             flags = vldbEntry.flags;
209             nServers = vldbEntry.nServers;
210             rwID = vldbEntry.volumeId[0];
211             roID = vldbEntry.volumeId[1];
212             bkID = vldbEntry.volumeId[2];
213             for ( i=0; i<nServers; i++ ) {
214                 serverFlags[i] = vldbEntry.serverFlags[i];
215                 serverNumber[i] = vldbEntry.serverNumber[i];
216             }
217             break;
218         case 1:
219             flags = nvldbEntry.flags;
220             nServers = nvldbEntry.nServers;
221             rwID = nvldbEntry.volumeId[0];
222             roID = nvldbEntry.volumeId[1];
223             bkID = nvldbEntry.volumeId[2];
224             for ( i=0; i<nServers; i++ ) {
225                 serverFlags[i] = nvldbEntry.serverFlags[i];
226                 serverNumber[i] = nvldbEntry.serverNumber[i];
227             }
228             break;
229 #ifdef MULTIHOMED
230         case 2:
231             flags = uvldbEntry.flags;
232             nServers = uvldbEntry.nServers;
233             rwID = uvldbEntry.volumeId[0];
234             roID = uvldbEntry.volumeId[1];
235             bkID = uvldbEntry.volumeId[2];
236             for ( i=0, j=0; i<nServers && j<NMAXNSERVERS; i++ ) {
237                 if ( !(uvldbEntry.serverFlags[i] & VLSERVER_FLAG_UUID) ) {
238                     serverFlags[j] = uvldbEntry.serverFlags[i];
239                     serverNumber[j] = uvldbEntry.serverNumber[i].time_low;
240                     j++;
241                 } else {
242                     afs_uint32 * addrp, nentries, code, unique;
243                     bulkaddrs  addrs;
244                     ListAddrByAttributes attrs;
245                     afsUUID uuid;
246
247                     memset((char *)&attrs, 0, sizeof(attrs));
248                     attrs.Mask = VLADDR_UUID;
249                     attrs.uuid = uvldbEntry.serverNumber[i];
250                     memset((char *)&uuid, 0, sizeof(uuid));
251                     memset((char *)&addrs, 0, sizeof(addrs));
252
253                     do {
254                         code = cm_ConnByMServers(cellp->vlServersp, userp, reqp, &connp);
255                         if (code) 
256                             continue;
257                    
258                         code = VL_GetAddrsU(connp->callp, &attrs, &uuid, &unique, &nentries, &addrs);
259
260                         if (code == 0 && nentries == 0)
261                             code = VL_NOENT;
262                     } while (cm_Analyze(connp, userp, reqp, NULL, NULL, cellp->vlServersp, NULL, code));
263                     code = cm_MapVLRPCError(code, reqp);
264                     if (code)
265                         return code;
266
267                     addrp = addrs.bulkaddrs_val;
268                     for (k = 0; k < nentries && j < NMAXNSERVERS; j++, k++) {
269                         serverFlags[j] = uvldbEntry.serverFlags[i];
270                         serverNumber[j] = addrp[k];
271                     }
272
273                     free(addrs.bulkaddrs_val);  /* This is wrong */
274                 }
275             }
276             nServers = j;                                       /* update the server count */
277             break;
278 #endif
279         }
280
281         /* decode the response */
282         lock_ObtainWrite(&cm_volumeLock);
283         if (flags & VLF_RWEXISTS)
284             volp->rwID = rwID;
285         else
286             volp->rwID = 0;
287         if (flags & VLF_ROEXISTS)
288             volp->roID = roID;
289         else
290             volp->roID = 0;
291         if (flags & VLF_BACKEXISTS)
292             volp->bkID = bkID;
293         else
294             volp->bkID = 0;
295         lock_ReleaseWrite(&cm_volumeLock);
296         for (i=0; i<nServers; i++) {
297             /* create a server entry */
298             tflags = serverFlags[i];
299             if (tflags & VLSF_DONTUSE) 
300                 continue;
301             tsockAddr.sin_family = AF_INET;
302             tempAddr = htonl(serverNumber[i]);
303             tsockAddr.sin_addr.s_addr = tempAddr;
304             tsp = cm_FindServer(&tsockAddr, CM_SERVER_FILE);
305             if (!tsp)
306                 tsp = cm_NewServer(&tsockAddr, CM_SERVER_FILE,
307                                     cellp);
308
309             /* if this server was created by fs setserverprefs */
310             if ( !tsp->cellp ) 
311                 tsp->cellp = cellp;
312
313             osi_assert(tsp != NULL);
314                         
315             /* and add it to the list(s). */
316             /*
317              * Each call to cm_NewServerRef() increments the
318              * ref count of tsp.  These reference will be dropped,
319              * if and when the volume is reset; see reset code
320              * earlier in this function.
321              */
322             if ((tflags & VLSF_RWVOL) && (flags & VLF_RWEXISTS)) {
323                 tsrp = cm_NewServerRef(tsp);
324                 cm_InsertServerList(&volp->rwServersp, tsrp);
325                 lock_ObtainWrite(&cm_serverLock);
326                 tsrp->refCount--;       /* drop allocation reference */
327                 lock_ReleaseWrite(&cm_serverLock);
328             }
329             if ((tflags & VLSF_ROVOL) && (flags & VLF_ROEXISTS)) {
330                 tsrp = cm_NewServerRef(tsp);
331                 cm_InsertServerList(&volp->roServersp, tsrp);
332                 lock_ObtainWrite(&cm_serverLock);
333                 tsrp->refCount--;       /* drop allocation reference */
334                 lock_ReleaseWrite(&cm_serverLock);
335                 ROcount++;
336             }
337             /* We don't use VLSF_BACKVOL !?! */
338             if ((tflags & VLSF_RWVOL) && (flags & VLF_BACKEXISTS)) {
339                 tsrp = cm_NewServerRef(tsp);
340                 cm_InsertServerList(&volp->bkServersp, tsrp);
341                 lock_ObtainWrite(&cm_serverLock);
342                 tsrp->refCount--;       /* drop allocation reference */
343                 lock_ReleaseWrite(&cm_serverLock);
344             }
345             /* Drop the reference obtained by cm_FindServer() */
346             cm_PutServer(tsp);
347         }       
348
349         /*
350          * Randomize RO list
351          *
352          * If the first n servers have the same ipRank, then we 
353          * randomly pick one among them and move it to the beginning.
354          * We don't bother to re-order the whole list because
355          * the rest of the list is used only if the first server is
356          * down.  We only do this for the RO list; we assume the other
357          * lists are length 1.
358          */
359         if (ROcount > 1) {
360             cm_RandomizeServer(&volp->roServersp);
361         }
362     }
363     return code;
364 }
365
366 long cm_GetVolumeByID(cm_cell_t *cellp, long volumeID, cm_user_t *userp,
367         cm_req_t *reqp, cm_volume_t **outVolpp)
368 {
369         cm_volume_t *volp;
370         char volNameString[100];
371         long code;
372
373         lock_ObtainWrite(&cm_volumeLock);
374         for(volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
375                 if (cellp == volp->cellp &&
376                         ((unsigned) volumeID == volp->rwID ||
377                          (unsigned) volumeID == volp->roID ||
378                          (unsigned) volumeID == volp->bkID))
379                                 break;
380         }
381
382         /* hold the volume if we found it */
383     if (volp) 
384         volp->refCount++;
385         lock_ReleaseWrite(&cm_volumeLock);
386         
387         /* return it held */
388         if (volp) {
389                 lock_ObtainMutex(&volp->mx);
390         
391                 if (volp->flags & CM_VOLUMEFLAG_RESET) {
392                         code = cm_UpdateVolume(cellp, userp, reqp, volp);
393                         if (code == 0) {
394                                 volp->flags &= ~CM_VOLUMEFLAG_RESET;
395                         }
396                 }
397                 else
398                         code = 0;
399                 lock_ReleaseMutex(&volp->mx);
400                 if (code == 0)
401                         *outVolpp = volp;
402                 return code;
403         }
404         
405         /* otherwise, we didn't find it so consult the VLDB */
406         sprintf(volNameString, "%u", volumeID);
407         code = cm_GetVolumeByName(cellp, volNameString, userp, reqp,
408                                   0, outVolpp);
409         return code;
410 }
411
412 long cm_GetVolumeByName(struct cm_cell *cellp, char *volumeNamep,
413         struct cm_user *userp, struct cm_req *reqp,
414         long flags, cm_volume_t **outVolpp)
415 {
416         cm_volume_t *volp;
417         long code;
418         
419         /* initialize this */
420         code = 0;
421
422         lock_ObtainWrite(&cm_volumeLock);
423         for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
424             if (cellp == volp->cellp && strcmp(volumeNamep, volp->namep) == 0) {
425                 break;
426             }
427         }
428         
429         /* otherwise, get from VLDB */
430         if (!volp) {
431             if ( cm_data.currentVolumes >= cm_data.maxVolumes ) {
432                 for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
433                     if ( volp->refCount == 0 ) {
434                         /* There is one we can re-use */
435                         break;
436                     }
437                 }
438                 osi_panic("Exceeded Max Volumes", __FILE__, __LINE__);
439             }
440
441             if (volp) {
442                 volp->rwID = volp->roID = volp->bkID = 0;
443                 volp->dotdotFid.cell = 0;
444                 volp->dotdotFid.volume = 0;
445                 volp->dotdotFid.unique = 0;
446                 volp->dotdotFid.vnode = 0;
447             } else {
448                 volp = &cm_data.volumeBaseAddress[cm_data.currentVolumes++];
449                 memset(volp, 0, sizeof(cm_volume_t));
450                 volp->magic = CM_VOLUME_MAGIC;
451                 volp->nextp = cm_data.allVolumesp;
452                 cm_data.allVolumesp = volp;
453                 lock_InitializeMutex(&volp->mx, "cm_volume_t mutex");
454             }
455             volp->cellp = cellp;
456             strncpy(volp->namep, volumeNamep, VL_MAXNAMELEN);
457             volp->namep[VL_MAXNAMELEN-1] = '\0';
458             volp->refCount = 1; /* starts off held */
459             volp->flags = CM_VOLUMEFLAG_RESET;
460         }
461         else {
462             volp->refCount++;
463         }
464         
465         /* next should work since no one could have gotten ptr to this structure yet */
466         lock_ReleaseWrite(&cm_volumeLock);
467         lock_ObtainMutex(&volp->mx);
468         
469         if (volp->flags & CM_VOLUMEFLAG_RESET) {
470             code = cm_UpdateVolume(cellp, userp, reqp, volp);
471             if (code == 0)
472                 volp->flags &= ~CM_VOLUMEFLAG_RESET;
473         }
474
475         if (code == 0)
476             *outVolpp = volp;
477         lock_ReleaseMutex(&volp->mx);
478         return code;
479 }
480
481 void cm_ForceUpdateVolume(cm_fid_t *fidp, cm_user_t *userp, cm_req_t *reqp)
482 {
483         cm_cell_t *cellp;
484         cm_volume_t *volp;
485
486         if (!fidp) return;
487
488         cellp = cm_FindCellByID(fidp->cell);
489         if (!cellp) return;
490
491         /* search for the volume */
492         lock_ObtainWrite(&cm_volumeLock);
493         for(volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
494                 if (cellp == volp->cellp &&
495                         (fidp->volume == volp->rwID ||
496                          fidp->volume == volp->roID ||
497                          fidp->volume == volp->bkID))
498                                 break;
499         }
500
501         /* hold the volume if we found it */
502         if (volp) volp->refCount++;
503         lock_ReleaseWrite(&cm_volumeLock);
504
505         /* update it */
506         cm_data.mountRootGen = time(NULL);
507         lock_ObtainMutex(&volp->mx);
508         volp->flags |= CM_VOLUMEFLAG_RESET;
509 #ifdef COMMENT
510     /* Mark the volume to be updated but don't update it now.
511      * This function is called only from within cm_Analyze
512      * when cm_ConnByMServers has failed with all servers down
513      * The problem is that cm_UpdateVolume is going to call
514      * cm_ConnByMServers which may cause a recursive chain
515      * of calls each returning a retry on failure.
516      * Instead, set the flag so the next time the volume is
517      * accessed by Name or ID the UpdateVolume call will
518      * occur.
519      */
520         code = cm_UpdateVolume(cellp, userp, reqp, volp);
521         if (code == 0)
522                 volp->flags &= ~CM_VOLUMEFLAG_RESET;
523 #endif
524         lock_ReleaseMutex(&volp->mx);
525
526         cm_PutVolume(volp);
527 }
528
529 /* find the appropriate servers from a volume */
530 cm_serverRef_t **cm_GetVolServers(cm_volume_t *volp, unsigned long volume)
531 {
532     cm_serverRef_t **serverspp;
533     cm_serverRef_t *current;;
534
535     lock_ObtainWrite(&cm_serverLock);
536
537     if (volume == volp->rwID)
538         serverspp = &volp->rwServersp;
539     else if (volume == volp->roID)
540         serverspp = &volp->roServersp;
541     else if (volume == volp->bkID)
542         serverspp = &volp->bkServersp;
543     else 
544         osi_panic("bad volume ID in cm_GetVolServers", __FILE__, __LINE__);
545         
546     for (current = *serverspp; current; current = current->next)
547         current->refCount++;
548
549     lock_ReleaseWrite(&cm_serverLock);
550
551     return serverspp;
552 }
553
554 void cm_PutVolume(cm_volume_t *volp)
555 {
556     lock_ObtainWrite(&cm_volumeLock);
557     osi_assert(volp->refCount-- > 0);
558     lock_ReleaseWrite(&cm_volumeLock);
559 }
560
561 /* return the read-only volume, if there is one, or the read-write volume if
562  * not.
563  */
564 long cm_GetROVolumeID(cm_volume_t *volp)
565 {
566         long id;
567
568         lock_ObtainMutex(&volp->mx);
569         if (volp->roID && volp->roServersp)
570                 id = volp->roID;
571         else
572                 id = volp->rwID;
573         lock_ReleaseMutex(&volp->mx);
574
575         return id;
576 }
577
578 void cm_CheckVolumes(void)
579 {
580         cm_volume_t *volp;
581
582         cm_data.mountRootGen = time(NULL);
583         lock_ObtainWrite(&cm_volumeLock);
584         for (volp = cm_data.allVolumesp; volp; volp=volp->nextp) {
585                 volp->refCount++;
586                 lock_ReleaseWrite(&cm_volumeLock);
587                 lock_ObtainMutex(&volp->mx);
588
589                 volp->flags |= CM_VOLUMEFLAG_RESET;
590
591                 lock_ReleaseMutex(&volp->mx);
592                 lock_ObtainWrite(&cm_volumeLock);
593                 osi_assert(volp->refCount-- > 0);
594         }
595         lock_ReleaseWrite(&cm_volumeLock);
596
597         /* We should also refresh cached mount points */
598 }
599
600 /*
601 ** Finds all volumes that reside on this server and reorders their
602 ** RO list according to the changed rank of server.
603 */
604 void cm_ChangeRankVolume(cm_server_t       *tsp)
605 {
606         int             code;
607         cm_volume_t*    volp;
608
609         /* find volumes which might have RO copy on server*/
610         lock_ObtainWrite(&cm_volumeLock);
611         for(volp = cm_data.allVolumesp; volp; volp=volp->nextp)
612         {
613                 code = 1 ;      /* assume that list is unchanged */
614                 volp->refCount++;
615                 lock_ReleaseWrite(&cm_volumeLock);
616                 lock_ObtainMutex(&volp->mx);
617
618                 if ((tsp->cellp==volp->cellp) && (volp->roServersp))
619                     code =cm_ChangeRankServer(&volp->roServersp, tsp);
620
621                 /* this volume list was changed */
622                 if ( !code )
623                         cm_RandomizeServer(&volp->roServersp);
624
625                 lock_ReleaseMutex(&volp->mx);
626                 lock_ObtainWrite(&cm_volumeLock);
627                 osi_assert(volp->refCount-- > 0);
628         }
629         lock_ReleaseWrite(&cm_volumeLock);
630 }