1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2013 Free Software Foundation Europe e.V.
5 Copyright (C) 2015-2019 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22 /*
23 * Kern Sibbald, MM
24 * Split from reserve.c October 2008
25 */
26 /**
27 * @file
28 * Volume management functions for Storage Daemon
29 */
30
31 #include "include/bareos.h"
32 #include "stored/stored.h"
33 #include "stored/stored_globals.h"
34 #include "stored/autochanger.h"
35 #include "include/jcr.h"
36 #include "lib/berrno.h"
37
38 namespace storagedaemon {
39
40 const int debuglevel = 150;
41
42 static brwlock_t vol_list_lock;
43 static dlist* vol_list = NULL;
44 static dlist* read_vol_list = NULL;
45 static pthread_mutex_t read_vol_lock = PTHREAD_MUTEX_INITIALIZER;
46
47 /* Global static variables */
48 #ifdef SD_DEBUG_LOCK
49 int vol_list_lock_count = 0;
50 int read_vol_list_lock_count = 0;
51 #else
52 static int vol_list_lock_count = 0;
53 static int read_vol_list_lock_count = 0;
54 #endif
55
56 /* Forward referenced functions */
57 static void FreeVolItem(VolumeReservationItem* vol);
58 static void FreeReadVolItem(VolumeReservationItem* vol);
59 static VolumeReservationItem* new_vol_item(DeviceControlRecord* dcr,
60 const char* VolumeName);
61 static void DebugListVolumes(const char* imsg);
62
63 /**
64 * For append volumes the key is the VolumeName.
65 */
CompareByVolumename(void * item1,void * item2)66 static int CompareByVolumename(void* item1, void* item2)
67 {
68 VolumeReservationItem* vol1 = (VolumeReservationItem*)item1;
69 VolumeReservationItem* vol2 = (VolumeReservationItem*)item2;
70
71 ASSERT(vol1->vol_name);
72 ASSERT(vol2->vol_name);
73
74 return strcmp(vol1->vol_name, vol2->vol_name);
75 }
76
77 /**
78 * For read volumes the key is JobId, VolumeName.
79 */
ReadCompare(void * item1,void * item2)80 static int ReadCompare(void* item1, void* item2)
81 {
82 VolumeReservationItem* vol1 = (VolumeReservationItem*)item1;
83 VolumeReservationItem* vol2 = (VolumeReservationItem*)item2;
84
85 ASSERT(vol1->vol_name);
86 ASSERT(vol2->vol_name);
87
88 if (vol1->GetJobid() == vol2->GetJobid()) {
89 return strcmp(vol1->vol_name, vol2->vol_name);
90 }
91
92 if (vol1->GetJobid() < vol2->GetJobid()) { return -1; }
93
94 return 1;
95 }
96
IsVolListEmpty()97 bool IsVolListEmpty() { return vol_list->empty(); }
98
99 /**
100 * Initialized the main volume list. Note, we are using a recursive lock.
101 */
InitVolListLock()102 void InitVolListLock()
103 {
104 int errstat;
105
106 if ((errstat = RwlInit(&vol_list_lock, PRIO_SD_VOL_LIST)) != 0) {
107 BErrNo be;
108 Emsg1(M_ABORT, 0, _("Unable to initialize volume list lock. ERR=%s\n"),
109 be.bstrerror(errstat));
110 }
111 }
112
TermVolListLock()113 void TermVolListLock() { RwlDestroy(&vol_list_lock); }
114
115 /**
116 * This allows a given thread to recursively call to LockVolumes()
117 */
_lockVolumes(const char * file,int line)118 void _lockVolumes(const char* file, int line)
119 {
120 int errstat;
121
122 vol_list_lock_count++;
123 if ((errstat = RwlWritelock_p(&vol_list_lock, file, line)) != 0) {
124 BErrNo be;
125 Emsg2(M_ABORT, 0, "RwlWritelock failure. stat=%d: ERR=%s\n", errstat,
126 be.bstrerror(errstat));
127 }
128 }
129
_unLockVolumes()130 void _unLockVolumes()
131 {
132 int errstat;
133
134 vol_list_lock_count--;
135 if ((errstat = RwlWriteunlock(&vol_list_lock)) != 0) {
136 BErrNo be;
137 Emsg2(M_ABORT, 0, "RwlWriteunlock failure. stat=%d: ERR=%s\n", errstat,
138 be.bstrerror(errstat));
139 }
140 }
141
_lockReadVolumes(const char * file,int line)142 void _lockReadVolumes(const char* file, int line)
143 {
144 read_vol_list_lock_count++;
145 pthread_mutex_lock(&read_vol_lock);
146 }
147
_unLockReadVolumes()148 void _unLockReadVolumes()
149 {
150 read_vol_list_lock_count--;
151 pthread_mutex_unlock(&read_vol_lock);
152 }
153
154 /**
155 * Add a volume to the read list.
156 *
157 * Note, we use VolumeReservationItem because it simplifies the code
158 * even though, the only part of VolumeReservationItem that we need is
159 * the volume name. The same volume may be in the list
160 * multiple times, but each one is distinguished by the
161 * JobId. We use JobId, VolumeName as the key.
162 *
163 * We can get called multiple times for the same volume because
164 * when parsing the bsr, the volume name appears multiple times.
165 */
AddReadVolume(JobControlRecord * jcr,const char * VolumeName)166 void AddReadVolume(JobControlRecord* jcr, const char* VolumeName)
167 {
168 VolumeReservationItem *nvol, *vol;
169
170 nvol = new_vol_item(NULL, VolumeName);
171 nvol->SetJobid(jcr->JobId);
172 nvol->SetReading();
173 LockReadVolumes();
174 vol = (VolumeReservationItem*)read_vol_list->binary_insert(nvol, ReadCompare);
175 if (vol != nvol) {
176 FreeReadVolItem(nvol);
177 Dmsg2(debuglevel, "read_vol=%s JobId=%d already in list.\n", VolumeName,
178 jcr->JobId);
179 } else {
180 Dmsg2(debuglevel, "add_read_vol=%s JobId=%d\n", VolumeName, jcr->JobId);
181 }
182 UnlockReadVolumes();
183 }
184
185 /**
186 * Remove a given volume name from the read list.
187 */
RemoveReadVolume(JobControlRecord * jcr,const char * VolumeName)188 void RemoveReadVolume(JobControlRecord* jcr, const char* VolumeName)
189 {
190 VolumeReservationItem vol, *fvol;
191
192 LockReadVolumes();
193 vol.vol_name = strdup(VolumeName);
194 vol.SetJobid(jcr->JobId);
195
196 fvol =
197 (VolumeReservationItem*)read_vol_list->binary_search(&vol, ReadCompare);
198 free(vol.vol_name);
199
200 if (fvol) {
201 Dmsg3(debuglevel, "remove_read_vol=%s JobId=%d found=%d\n", VolumeName,
202 jcr->JobId, fvol != NULL);
203 }
204 if (fvol) {
205 read_vol_list->remove(fvol);
206 FreeReadVolItem(fvol);
207 }
208 UnlockReadVolumes();
209 // pthread_cond_broadcast(&wait_next_vol);
210 }
211
212 /**
213 * Search for a Volume name in the read Volume list.
214 *
215 * Returns: VolumeReservationItem entry on success
216 * NULL if the Volume is not in the list
217 */
find_read_volume(const char * VolumeName)218 static VolumeReservationItem* find_read_volume(const char* VolumeName)
219 {
220 VolumeReservationItem vol, *fvol;
221
222 if (read_vol_list->empty()) {
223 Dmsg0(debuglevel, "find_read_vol: read_vol_list empty.\n");
224 return NULL;
225 }
226
227 /*
228 * Do not lock reservations here
229 */
230 LockReadVolumes();
231 vol.vol_name = strdup(VolumeName);
232
233 /*
234 * Note, we do want a simple CompareByVolumename on volume name only here
235 */
236 fvol = (VolumeReservationItem*)read_vol_list->binary_search(
237 &vol, CompareByVolumename);
238 free(vol.vol_name);
239
240 Dmsg2(debuglevel, "find_read_vol=%s found=%d\n", VolumeName, fvol != NULL);
241 UnlockReadVolumes();
242
243 return fvol;
244 }
245
246 /**
247 * List Volumes -- this should be moved to status.c
248 */
249 enum
250 {
251 debug_lock = true,
252 debug_nolock = false
253 };
254
DebugListVolumes(const char * imsg)255 static void DebugListVolumes(const char* imsg)
256 {
257 VolumeReservationItem* vol;
258 PoolMem msg(PM_MESSAGE);
259
260 foreach_vol (vol) {
261 if (vol->dev) {
262 Mmsg(msg, "List %s: %s in_use=%d swap=%d on device %s\n", imsg,
263 vol->vol_name, vol->IsInUse(), vol->IsSwapping(),
264 vol->dev->print_name());
265 } else {
266 Mmsg(msg, "List %s: %s in_use=%d swap=%d no dev\n", imsg, vol->vol_name,
267 vol->IsInUse(), vol->IsSwapping());
268 }
269 Dmsg1(debuglevel, "%s", msg.c_str());
270 }
271 endeach_vol(vol);
272 }
273
274 /**
275 * Create a Volume item to put in the Volume list
276 * Ensure that the device points to it.
277 */
new_vol_item(DeviceControlRecord * dcr,const char * VolumeName)278 static VolumeReservationItem* new_vol_item(DeviceControlRecord* dcr,
279 const char* VolumeName)
280 {
281 VolumeReservationItem* vol;
282 VolumeReservationItem emptyVol;
283
284 vol = (VolumeReservationItem*)malloc(sizeof(VolumeReservationItem));
285 *vol = emptyVol;
286 vol->vol_name = strdup(VolumeName);
287 if (dcr) {
288 vol->dev = dcr->dev;
289 Dmsg3(debuglevel, "new Vol=%s at %p dev=%s\n", VolumeName, vol->vol_name,
290 vol->dev->print_name());
291 }
292 vol->InitMutex();
293 vol->IncUseCount();
294
295 return vol;
296 }
297
FreeVolItem(VolumeReservationItem * vol)298 static void FreeVolItem(VolumeReservationItem* vol)
299 {
300 Device* dev = NULL;
301
302 vol->DecUseCount();
303 vol->Lock();
304 if (vol->UseCount() > 0) {
305 vol->Unlock();
306 return;
307 }
308 vol->Unlock();
309 free(vol->vol_name);
310 if (vol->dev) { dev = vol->dev; }
311 vol->DestroyMutex();
312 free(vol);
313 if (dev) { dev->vol = NULL; }
314 }
315
FreeReadVolItem(VolumeReservationItem * vol)316 static void FreeReadVolItem(VolumeReservationItem* vol)
317 {
318 Device* dev = NULL;
319
320 vol->DecUseCount();
321 vol->Lock();
322 if (vol->UseCount() > 0) {
323 vol->Unlock();
324 return;
325 }
326 vol->Unlock();
327 free(vol->vol_name);
328 if (vol->dev) { dev = vol->dev; }
329 vol->DestroyMutex();
330 free(vol);
331 if (dev) { dev->vol = NULL; }
332 }
333
334 /**
335 * Put a new Volume entry in the Volume list. This
336 * effectively reserves the volume so that it will
337 * not be mounted again.
338 *
339 * If the device has any current volume associated with it,
340 * and it is a different Volume, and the device is not busy,
341 * we release the old Volume item and insert the new one.
342 *
343 * It is assumed that the device is free and locked so that
344 * we can change the device structure.
345 *
346 * Some details of the Volume list handling:
347 *
348 * 1. The Volume list entry is attached to the drive (rather than
349 * attached to a job as it was previously. I.e. the drive that "owns"
350 * the volume (in use, mounted)
351 * must point to the volume (still to be maintained in a list).
352 *
353 * 2. The Volume is entered in the list when a drive is reserved.
354 *
355 * 3. When a drive is in use, the device code must appropriately update the
356 * volume name as it changes.
357 *
358 * This code keeps the same list entry as long as the drive
359 * has any volume associated with it but the volume name in the list
360 * must be updated when the drive has a different volume mounted.
361 *
362 * 4. A job that has reserved a volume, can un-reserve the volume, and if the
363 * volume is not mounted, and not reserved, and not in use, it will be
364 * removed from the list.
365 *
366 * 5. If a job wants to reserve a drive with a different Volume from the one on
367 * the drive, it can re-use the drive for the new Volume.
368 *
369 * 6. If a job wants a Volume that is in a different drive, it can either use
370 * the other drive or take the volume, only if the other drive is not in use or
371 * not reserved.
372 *
373 * One nice aspect of this is that the reserve use count and the writer use
374 * count already exist and are correctly programmed and will need no changes --
375 * use counts are always very tricky.
376 *
377 * The old code had a concept of "reserving" a Volume, but was changed
378 * to reserving and using a drive. A volume is must be attached to (owned by)
379 * a drive and can move from drive to drive or be unused given certain specific
380 * conditions of the drive. The key is that the drive must "own" the Volume.
381 *
382 * Return: VolumeReservationItem entry on success
383 * NULL volume busy on another drive
384 */
reserve_volume(DeviceControlRecord * dcr,const char * VolumeName)385 VolumeReservationItem* reserve_volume(DeviceControlRecord* dcr,
386 const char* VolumeName)
387 {
388 VolumeReservationItem *vol, *nvol;
389 Device* volatile dev = dcr->dev;
390
391 if (JobCanceled(dcr->jcr)) { return NULL; }
392 ASSERT(dev != NULL);
393
394 Dmsg2(debuglevel, "enter reserve_volume=%s drive=%s\n", VolumeName,
395 dcr->dev->print_name());
396
397 /*
398 * If aquiring a volume for writing it may not be on the read volume list.
399 */
400 if (me->filedevice_concurrent_read && dcr->IsWriting() &&
401 find_read_volume(VolumeName)) {
402 Mmsg(dcr->jcr->errmsg,
403 _("Could not reserve volume \"%s\" for append, because it is read by "
404 "another Job.\n"),
405 dev->VolHdr.VolumeName);
406 return NULL;
407 }
408
409 /*
410 * We lock the reservations system here to ensure when adding a new volume
411 * that no newly scheduled job can reserve it.
412 */
413 LockVolumes();
414 if (debug_level >= debuglevel) { DebugListVolumes("begin reserve_volume"); }
415
416 /*
417 * First, remove any old volume attached to this device as it is no longer
418 * used.
419 */
420 if (dev->vol) {
421 vol = dev->vol;
422 Dmsg4(debuglevel, "Vol attached=%s, newvol=%s volinuse=%d on %s\n",
423 vol->vol_name, VolumeName, vol->IsInUse(), dev->print_name());
424 /*
425 * Make sure we don't remove the current volume we are inserting
426 * because it was probably inserted by another job, or it
427 * is not being used and is marked as not reserved.
428 */
429 if (bstrcmp(vol->vol_name, VolumeName)) {
430 Dmsg2(debuglevel, "=== set reserved vol=%s dev=%s\n", VolumeName,
431 vol->dev->print_name());
432 goto get_out; /* Volume already on this device */
433 } else {
434 /*
435 * Don't release a volume if it was reserved by someone other than us
436 */
437 if (vol->IsInUse() && !dcr->reserved_volume) {
438 Dmsg1(debuglevel, "Cannot free vol=%s. It is reserved.\n",
439 vol->vol_name);
440 vol = NULL; /* vol in use */
441 goto get_out;
442 }
443 Dmsg2(debuglevel, "reserve_vol free vol=%s at %p\n", vol->vol_name,
444 vol->vol_name);
445
446 /*
447 * If old Volume is still mounted, must unload it
448 */
449 if (bstrcmp(vol->vol_name, dev->VolHdr.VolumeName)) {
450 Dmsg0(50, "SetUnload\n");
451 dev->SetUnload(); /* have to unload current volume */
452 }
453 FreeVolume(dev); /* Release old volume entry */
454
455 if (debug_level >= debuglevel) { DebugListVolumes("reserve_vol free"); }
456 }
457 }
458
459 /*
460 * Create a new Volume entry
461 */
462 nvol = new_vol_item(dcr, VolumeName);
463
464 /*
465 * See if this is a request for reading a file type device which can be
466 * accesses by multiple readers at once without disturbing each other.
467 */
468 if (me->filedevice_concurrent_read && !dcr->IsWriting() && dev->IsFile()) {
469 nvol->SetJobid(dcr->jcr->JobId);
470 nvol->SetReading();
471 vol = nvol;
472 dev->vol = vol;
473
474 /*
475 * Read volumes on file based devices are not inserted into the write volume
476 * list.
477 */
478 goto get_out;
479 } else {
480 /*
481 * Now try to insert the new Volume
482 */
483 vol = (VolumeReservationItem*)vol_list->binary_insert(nvol,
484 CompareByVolumename);
485 }
486
487 if (vol != nvol) {
488 Dmsg2(debuglevel, "Found vol=%s dev-same=%d\n", vol->vol_name,
489 dev == vol->dev);
490
491 /*
492 * At this point, a Volume with this name already is in the list,
493 * so we simply release our new Volume entry. Note, this should
494 * only happen if we are moving the volume from one drive to another.
495 */
496 Dmsg2(debuglevel, "reserve_vol free-tmp vol=%s at %p\n", vol->vol_name,
497 vol->vol_name);
498
499 /*
500 * Clear dev pointer so that FreeVolItem() doesn't take away our volume.
501 */
502 nvol->dev = NULL; /* don't zap dev entry */
503 FreeVolItem(nvol);
504
505 if (vol->dev) {
506 Dmsg2(debuglevel, "dev=%s vol->dev=%s\n", dev->print_name(),
507 vol->dev->print_name());
508 }
509
510 /*
511 * Check if we are trying to use the Volume on a different drive dev is our
512 * device vol->dev is where the Volume we want is
513 */
514 if (dev != vol->dev) {
515 /*
516 * Caller wants to switch Volume to another device
517 */
518 if (!vol->dev->IsBusy() && !vol->IsSwapping()) {
519 slot_number_t slot;
520
521 Dmsg3(debuglevel, "==== Swap vol=%s from dev=%s to %s\n", VolumeName,
522 vol->dev->print_name(), dev->print_name());
523 FreeVolume(dev); /* free any volume attached to our drive */
524 Dmsg1(50, "SetUnload dev=%s\n", dev->print_name());
525 dev->SetUnload(); /* Unload any volume that is on our drive */
526 dcr->SetDev(vol->dev); /* temp point to other dev */
527 slot = GetAutochangerLoadedSlot(dcr); /* get slot on other drive */
528 dcr->SetDev(dev); /* restore dev */
529 vol->SetSlotNumber(slot); /* save slot */
530 vol->dev->SetUnload(); /* unload the other drive */
531 vol->SetSwapping(); /* swap from other drive */
532 dev->swap_dev = vol->dev; /* remember to get this vol */
533 dev->SetLoad(); /* then reload on our drive */
534 vol->dev->vol = NULL; /* remove volume from other drive */
535 vol->dev = dev; /* point the Volume at our drive */
536 dev->vol = vol; /* point our drive at the Volume */
537 } else {
538 Jmsg7(dcr->jcr, M_WARNING, 0,
539 "Need volume from other drive, but swap not possible. "
540 "Status: read=%d num_writers=%d num_reserve=%d swap=%d "
541 "vol=%s from dev=%s to %s\n",
542 vol->dev->CanRead(), vol->dev->num_writers,
543 vol->dev->NumReserved(), vol->IsSwapping(), VolumeName,
544 vol->dev->print_name(), dev->print_name());
545 if (vol->IsSwapping() && dev->swap_dev) {
546 Dmsg3(debuglevel, "Swap failed vol=%s from=%s to dev=%s\n",
547 vol->vol_name, dev->swap_dev->print_name(), dev->print_name());
548 } else {
549 Dmsg3(debuglevel, "Swap failed vol=%s from=%p to dev=%s\n",
550 vol->vol_name, dev->swap_dev, dev->print_name());
551 }
552
553 if (debug_level >= debuglevel) { DebugListVolumes("failed swap"); }
554
555 vol = NULL; /* device busy */
556 goto get_out;
557 }
558 } else {
559 dev->vol = vol;
560 }
561 } else {
562 dev->vol = vol; /* point to newly inserted volume */
563 }
564
565 get_out:
566 if (vol) {
567 Dmsg2(debuglevel, "=== set in_use. vol=%s dev=%s\n", vol->vol_name,
568 vol->dev->print_name());
569 vol->SetInUse();
570 dcr->reserved_volume = true;
571 bstrncpy(dcr->VolumeName, vol->vol_name, sizeof(dcr->VolumeName));
572 }
573
574 if (debug_level >= debuglevel) { DebugListVolumes("end new volume"); }
575
576 UnlockVolumes();
577 return vol;
578 }
579
580 /**
581 * Start walk of vol chain
582 * The proper way to walk the vol chain is:
583 *
584 * VolumeReservationItem *vol;
585 * foreach_vol(vol) {
586 * ...
587 * }
588 * endeach_vol(vol);
589 *
590 * It is possible to leave out the endeach_vol(vol), but in that case,
591 * the last vol referenced must be explicitly released with:
592 *
593 * FreeVolItem(vol);
594 */
vol_walk_start()595 VolumeReservationItem* vol_walk_start()
596 {
597 VolumeReservationItem* vol;
598 LockVolumes();
599 vol = (VolumeReservationItem*)vol_list->first();
600 if (vol) {
601 vol->IncUseCount();
602 Dmsg2(debuglevel, "Inc walk_start UseCount=%d volname=%s\n",
603 vol->UseCount(), vol->vol_name);
604 }
605 UnlockVolumes();
606
607 return vol;
608 }
609
610 /**
611 * Get next vol from chain, and release current one
612 */
VolWalkNext(VolumeReservationItem * prev_vol)613 VolumeReservationItem* VolWalkNext(VolumeReservationItem* prev_vol)
614 {
615 VolumeReservationItem* vol;
616
617 LockVolumes();
618 vol = (VolumeReservationItem*)vol_list->next(prev_vol);
619 if (vol) {
620 vol->IncUseCount();
621 Dmsg2(debuglevel, "Inc walk_next UseCount=%d volname=%s\n", vol->UseCount(),
622 vol->vol_name);
623 }
624 if (prev_vol) { FreeVolItem(prev_vol); }
625 UnlockVolumes();
626
627 return vol;
628 }
629
630 /**
631 * Release last vol referenced
632 */
VolWalkEnd(VolumeReservationItem * vol)633 void VolWalkEnd(VolumeReservationItem* vol)
634 {
635 if (vol) {
636 LockVolumes();
637 Dmsg2(debuglevel, "Free walk_end UseCount=%d volname=%s\n", vol->UseCount(),
638 vol->vol_name);
639 FreeVolItem(vol);
640 UnlockVolumes();
641 }
642 }
643
644 /*
645 * Start walk of vol chain
646 * The proper way to walk the vol chain is:
647 *
648 * VolumeReservationItem *vol;
649 * foreach_read_vol(vol) {
650 * ...
651 * }
652 * endeach_read_vol(vol);
653 *
654 * It is possible to leave out the endeach_read_vol(vol), but in that case,
655 * the last vol referenced must be explicitly released with:
656 *
657 * FreeReadVolItem(vol);
658 */
read_vol_walk_start()659 VolumeReservationItem* read_vol_walk_start()
660 {
661 VolumeReservationItem* vol;
662 LockReadVolumes();
663 vol = (VolumeReservationItem*)read_vol_list->first();
664 if (vol) {
665 vol->IncUseCount();
666 Dmsg2(debuglevel, "Inc walk_start UseCount=%d volname=%s\n",
667 vol->UseCount(), vol->vol_name);
668 }
669 UnlockReadVolumes();
670
671 return vol;
672 }
673
674 /*
675 * Get next vol from chain, and release current one
676 */
ReadVolWalkNext(VolumeReservationItem * prev_vol)677 VolumeReservationItem* ReadVolWalkNext(VolumeReservationItem* prev_vol)
678 {
679 VolumeReservationItem* vol;
680
681 LockReadVolumes();
682 vol = (VolumeReservationItem*)read_vol_list->next(prev_vol);
683 if (vol) {
684 vol->IncUseCount();
685 Dmsg2(debuglevel, "Inc walk_next UseCount=%d volname=%s\n", vol->UseCount(),
686 vol->vol_name);
687 }
688 if (prev_vol) { FreeReadVolItem(prev_vol); }
689 UnlockReadVolumes();
690
691 return vol;
692 }
693
694 /*
695 * Release last vol referenced
696 */
ReadVolWalkEnd(VolumeReservationItem * vol)697 void ReadVolWalkEnd(VolumeReservationItem* vol)
698 {
699 if (vol) {
700 LockReadVolumes();
701 Dmsg2(debuglevel, "Free walk_end UseCount=%d volname=%s\n", vol->UseCount(),
702 vol->vol_name);
703 FreeReadVolItem(vol);
704 UnlockReadVolumes();
705 }
706 }
707
708 /**
709 * Search for a Volume name in the Volume list.
710 *
711 * Returns: VolumeReservationItem entry on success
712 * NULL if the Volume is not in the list
713 */
find_volume(const char * VolumeName)714 static VolumeReservationItem* find_volume(const char* VolumeName)
715 {
716 VolumeReservationItem vol, *fvol;
717
718 if (vol_list->empty()) { return NULL; }
719 /* Do not lock reservations here */
720 LockVolumes();
721 vol.vol_name = strdup(VolumeName);
722 fvol = (VolumeReservationItem*)vol_list->binary_search(&vol,
723 CompareByVolumename);
724 free(vol.vol_name);
725 Dmsg2(debuglevel, "find_vol=%s found=%d\n", VolumeName, fvol != NULL);
726
727 if (debug_level >= debuglevel) { DebugListVolumes("find_volume"); }
728
729 UnlockVolumes();
730 return fvol;
731 }
732
733 /**
734 * Free a Volume from the Volume list if it is no longer used
735 * Note, for tape drives we want to remember where the Volume
736 * was when last used, so rather than free the volume entry,
737 * we simply mark it "not reserved" so when the drive is really
738 * needed for another volume, we can reuse it.
739 *
740 * Returns: true if the Volume found and "removed" from the list
741 * false if the Volume is not in the list or is in use
742 */
VolumeUnused(DeviceControlRecord * dcr)743 bool VolumeUnused(DeviceControlRecord* dcr)
744 {
745 Device* dev = dcr->dev;
746
747 if (!dev->vol) {
748 Dmsg1(debuglevel, "vol_unused: no vol on %s\n", dev->print_name());
749 if (debug_level >= debuglevel) {
750 DebugListVolumes("null vol cannot unreserve_volume");
751 }
752
753 return false;
754 }
755
756 Dmsg1(debuglevel, "=== clear in_use vol=%s\n", dev->vol->vol_name);
757 dev->vol->ClearInUse();
758
759 if (dev->vol->IsSwapping()) {
760 Dmsg1(debuglevel, "vol_unused: vol being swapped on %s\n",
761 dev->print_name());
762
763 if (debug_level >= debuglevel) {
764 DebugListVolumes("swapping vol cannot FreeVolume");
765 }
766 return false;
767 }
768
769 /*
770 * If this is a tape, we do not free the volume, rather we wait
771 * until the autoloader unloads it, or until another tape is
772 * explicitly read in this drive. This allows the SD to remember
773 * where the tapes are or last were.
774 */
775 Dmsg4(debuglevel,
776 "=== set not reserved vol=%s num_writers=%d dev_reserved=%d dev=%s\n",
777 dev->vol->vol_name, dev->num_writers, dev->NumReserved(),
778 dev->print_name());
779 if (dev->IsTape() || dev->IsAutochanger()) {
780 return true;
781 } else {
782 /*
783 * Note, this frees the volume reservation entry, but the file descriptor
784 * remains open with the OS.
785 */
786 return FreeVolume(dev);
787 }
788 }
789
790 /**
791 * Unconditionally release the volume entry
792 */
FreeVolume(Device * dev)793 bool FreeVolume(Device* dev)
794 {
795 VolumeReservationItem* vol;
796
797 LockVolumes();
798 vol = dev->vol;
799 if (vol == NULL) {
800 Dmsg1(debuglevel, "No vol on dev %s\n", dev->print_name());
801 UnlockVolumes();
802 return false;
803 }
804
805 /*
806 * Don't free a volume while it is being swapped
807 */
808 if (!vol->IsSwapping()) {
809 Dmsg1(debuglevel, "=== clear in_use vol=%s\n", vol->vol_name);
810 dev->vol = NULL;
811
812 /*
813 * Volume is on write volume list if one of the folling is applicable:
814 * - The volume is written to.
815 * - Config option filedevice_concurrent_read is not on.
816 * - The device is not of type File.
817 */
818 if (vol->IsWriting() || !me->filedevice_concurrent_read || !dev->IsFile()) {
819 vol_list->remove(vol);
820 }
821 Dmsg2(debuglevel, "=== remove volume %s dev=%s\n", vol->vol_name,
822 dev->print_name());
823 FreeVolItem(vol);
824
825 if (debug_level >= debuglevel) { DebugListVolumes("FreeVolume"); }
826 } else {
827 Dmsg1(debuglevel, "=== cannot clear swapping vol=%s\n", vol->vol_name);
828 }
829 UnlockVolumes();
830 // pthread_cond_broadcast(&wait_next_vol);
831
832 return true;
833 }
834
835 /**
836 * Create the Volume list
837 */
CreateVolumeLists()838 void CreateVolumeLists()
839 {
840 VolumeReservationItem* vol = NULL;
841 if (vol_list == NULL) { vol_list = new dlist(vol, &vol->link); }
842 if (read_vol_list == NULL) { read_vol_list = new dlist(vol, &vol->link); }
843 }
844
845 /**
846 * Free normal append volumes list
847 */
FreeVolumeList(const char * what,dlist * vollist)848 static inline void FreeVolumeList(const char* what, dlist* vollist)
849 {
850 VolumeReservationItem* vol;
851
852 foreach_dlist (vol, vollist) {
853 if (vol->dev) {
854 Dmsg3(debuglevel, "free %s Volume=%s dev=%s\n", what, vol->vol_name,
855 vol->dev->print_name());
856 } else {
857 Dmsg2(debuglevel, "free %s Volume=%s No dev\n", what, vol->vol_name);
858 }
859 free(vol->vol_name);
860 vol->vol_name = NULL;
861 vol->DestroyMutex();
862 }
863 }
864
865 /**
866 * Release all Volumes from the list
867 */
FreeVolumeLists()868 void FreeVolumeLists()
869 {
870 if (vol_list) {
871 LockVolumes();
872 FreeVolumeList("vol_list", vol_list);
873 delete vol_list;
874 vol_list = NULL;
875 UnlockVolumes();
876 }
877
878 if (read_vol_list) {
879 LockReadVolumes();
880 FreeVolumeList("read_vol_list", read_vol_list);
881 delete read_vol_list;
882 read_vol_list = NULL;
883 UnlockReadVolumes();
884 }
885 }
886
887 /**
888 * Determine if caller can write on volume
889 */
Can_i_write_volume()890 bool DeviceControlRecord::Can_i_write_volume()
891 {
892 VolumeReservationItem* vol;
893
894 vol = find_read_volume(VolumeName);
895 if (vol) {
896 Dmsg1(100, "Found in read list; cannot write vol=%s\n", VolumeName);
897 return false;
898 }
899
900 return Can_i_use_volume();
901 }
902
903 /**
904 * Determine if caller can read or write volume
905 */
Can_i_use_volume()906 bool DeviceControlRecord::Can_i_use_volume()
907 {
908 bool rtn = true;
909 VolumeReservationItem* vol;
910
911 if (JobCanceled(jcr)) { return false; }
912 LockVolumes();
913 vol = find_volume(VolumeName);
914 if (!vol) {
915 Dmsg1(debuglevel, "Vol=%s not in use.\n", VolumeName);
916 goto get_out; /* vol not in list */
917 }
918 ASSERT(vol->dev != NULL);
919
920 if (dev == vol->dev) { /* same device OK */
921 Dmsg1(debuglevel, "Vol=%s on same dev.\n", VolumeName);
922 goto get_out;
923 } else {
924 Dmsg3(debuglevel, "Vol=%s on %s we have %s\n", VolumeName,
925 vol->dev->print_name(), dev->print_name());
926 }
927 /* ***FIXME*** check this ... */
928 if (!vol->dev->IsBusy()) {
929 Dmsg2(debuglevel, "Vol=%s dev=%s not busy.\n", VolumeName,
930 vol->dev->print_name());
931 goto get_out;
932 } else {
933 Dmsg2(debuglevel, "Vol=%s dev=%s busy.\n", VolumeName,
934 vol->dev->print_name());
935 }
936 Dmsg2(debuglevel, "Vol=%s in use by %s.\n", VolumeName,
937 vol->dev->print_name());
938 rtn = false;
939
940 get_out:
941 UnlockVolumes();
942 return rtn;
943 }
944
945 /**
946 * Create a temporary copy of the volume list. We do this,
947 * to avoid having the volume list locked during the
948 * call to ReserveDevice(), which would cause a deadlock.
949 *
950 * Note, we may want to add an update counter on the vol_list
951 * so that if it is modified while we are traversing the copy
952 * we can take note and act accordingly (probably redo the
953 * search at least a few times).
954 */
dup_vol_list(JobControlRecord * jcr)955 dlist* dup_vol_list(JobControlRecord* jcr)
956 {
957 dlist* temp_vol_list;
958 VolumeReservationItem* vol = NULL;
959
960 Dmsg0(debuglevel, "lock volumes\n");
961
962 Dmsg0(debuglevel, "duplicate vol list\n");
963 temp_vol_list = new dlist(vol, &vol->link);
964 foreach_vol (vol) {
965 VolumeReservationItem *nvol, *tvol;
966
967 tvol = new_vol_item(NULL, vol->vol_name);
968 tvol->dev = vol->dev;
969 nvol = (VolumeReservationItem*)temp_vol_list->binary_insert(
970 tvol, CompareByVolumename);
971 if (tvol != nvol) {
972 tvol->dev = NULL; /* don't zap dev entry */
973 FreeVolItem(tvol);
974 Pmsg0(000, "Logic error. Duplicating vol list hit duplicate.\n");
975 Jmsg(jcr, M_WARNING, 0,
976 "Logic error. Duplicating vol list hit duplicate.\n");
977 }
978 }
979 endeach_vol(vol);
980 Dmsg0(debuglevel, "unlock volumes\n");
981
982 return temp_vol_list;
983 }
984
985 /**
986 * Free the specified temp list.
987 */
FreeTempVolList(dlist * temp_vol_list)988 void FreeTempVolList(dlist* temp_vol_list)
989 {
990 FreeVolumeList("temp_vol_list", temp_vol_list);
991 delete temp_vol_list;
992 }
993
994 } /* namespace storagedaemon */
995