]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/vol_mgr.c
kes Split volume management code out of src/stored/reserve.c into
[bacula/bacula] / bacula / src / stored / vol_mgr.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *   Volume management functions for Storage Daemon
30  *
31  *   Kern Sibbald, MM
32  *
33  *   Split from reserve.c October 2008
34  *
35  *   Version $Id: reserve.c 7380 2008-07-14 10:42:59Z kerns $
36  *
37  */
38
39 #include "bacula.h"
40 #include "stored.h"
41
42 const int dbglvl =  50;
43
44 static dlist *vol_list = NULL;
45 static brwlock_t vol_list_lock;
46
47 /* Forward referenced functions */
48 static void free_vol_item(VOLRES *vol);
49
50
51 static int my_compare(void *item1, void *item2)
52 {
53    return strcmp(((VOLRES *)item1)->vol_name, ((VOLRES *)item2)->vol_name);
54 }
55
56 bool is_vol_list_empty() 
57 {
58    return vol_list->empty();
59 }
60
61 int vol_list_lock_count = 0;
62
63 void init_vol_list_lock()
64 {
65    int errstat;
66    if ((errstat=rwl_init(&vol_list_lock)) != 0) {
67       berrno be;
68       Emsg1(M_ABORT, 0, _("Unable to initialize volume list lock. ERR=%s\n"),
69             be.bstrerror(errstat));
70    }
71 }
72
73 void term_vol_list_lock()
74 {
75    rwl_destroy(&vol_list_lock);
76 }
77
78
79
80 /* 
81  * This allows a given thread to recursively call to lock_volumes()
82  */
83 void _lock_volumes()
84 {
85    int errstat;
86    vol_list_lock_count++;
87    if ((errstat=rwl_writelock(&vol_list_lock)) != 0) {
88       berrno be;
89       Emsg2(M_ABORT, 0, "rwl_writelock failure. stat=%d: ERR=%s\n",
90            errstat, be.bstrerror(errstat));
91    }
92 }
93
94 void _unlock_volumes()
95 {
96    int errstat;
97    vol_list_lock_count--;
98    if ((errstat=rwl_writeunlock(&vol_list_lock)) != 0) {
99       berrno be;
100       Emsg2(M_ABORT, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n",
101            errstat, be.bstrerror(errstat));
102    }
103 }
104
105 dlist *dup_vol_list(JCR *jcr)
106 {
107    dlist *temp_vol_list;
108    VOLRES *vol = NULL;
109
110    lock_volumes();
111    Dmsg0(dbglvl, "lock volumes\n");                           
112
113    /*  
114     * Create a temporary copy of the volume list.  We do this,
115     *   to avoid having the volume list locked during the
116     *   call to reserve_device(), which would cause a deadlock.
117     * Note, we may want to add an update counter on the vol_list
118     *   so that if it is modified while we are traversing the copy
119     *   we can take note and act accordingly (probably redo the 
120     *   search at least a few times).
121     */
122    Dmsg0(dbglvl, "duplicate vol list\n");
123    temp_vol_list = New(dlist(vol, &vol->link));
124    foreach_dlist(vol, vol_list) {
125       VOLRES *nvol;
126       VOLRES *tvol = (VOLRES *)malloc(sizeof(VOLRES));
127       memset(tvol, 0, sizeof(VOLRES));
128       tvol->vol_name = bstrdup(vol->vol_name);
129       tvol->dev = vol->dev;
130       nvol = (VOLRES *)temp_vol_list->binary_insert(tvol, my_compare);
131       if (tvol != nvol) {
132          tvol->dev = NULL;                   /* don't zap dev entry */
133          free_vol_item(tvol);
134          Pmsg0(000, "Logic error. Duplicating vol list hit duplicate.\n");
135          Jmsg(jcr, M_WARNING, 0, "Logic error. Duplicating vol list hit duplicate.\n");
136       }
137    }
138    Dmsg0(dbglvl, "unlock volumes\n");
139    unlock_volumes();
140    return temp_vol_list;
141 }
142
143 void free_temp_vol_list(dlist *temp_vol_list)
144 {
145    dlist *save_vol_list;
146    
147    lock_volumes();
148    save_vol_list = vol_list;
149    vol_list = temp_vol_list;
150    free_volume_list();                  /* release temp_vol_list */
151    vol_list = save_vol_list;
152    Dmsg0(dbglvl, "deleted temp vol list\n");
153    Dmsg0(dbglvl, "unlock volumes\n");
154    unlock_volumes();
155    debug_list_volumes("after free temp table");
156 }
157
158
159 /*
160  * List Volumes -- this should be moved to status.c
161  */
162 enum {
163    debug_lock = true,
164    debug_nolock = false
165 };
166
167 void debug_list_volumes(const char *imsg)
168 {
169    VOLRES *vol;
170    POOL_MEM msg(PM_MESSAGE);
171
172    lock_volumes();
173    foreach_dlist(vol, vol_list) {
174       if (vol->dev) {
175          Mmsg(msg, "List %s: %s in_use=%d on device %s\n", imsg, 
176               vol->vol_name, vol->is_in_use(), vol->dev->print_name());
177       } else {
178          Mmsg(msg, "List %s: %s in_use=%d no dev\n", imsg, vol->vol_name, 
179               vol->is_in_use());
180       }
181       Dmsg1(dbglvl, "%s", msg.c_str());
182    }
183
184    unlock_volumes();
185 }
186
187
188 /*
189  * List Volumes -- this should be moved to status.c
190  */
191 void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg)
192 {
193    VOLRES *vol;
194    POOL_MEM msg(PM_MESSAGE);
195    int len;
196
197    lock_volumes();
198    foreach_dlist(vol, vol_list) {
199       DEVICE *dev = vol->dev;
200       if (dev) {
201          len = Mmsg(msg, "%s on device %s\n", vol->vol_name, dev->print_name());
202          sendit(msg.c_str(), len, arg);
203          len = Mmsg(msg, "    Reader=%d writers=%d devres=%d volinuse=%d\n", 
204             dev->can_read()?1:0, dev->num_writers, dev->num_reserved(),   
205             vol->is_in_use());
206          sendit(msg.c_str(), len, arg);
207       } else {
208          len = Mmsg(msg, "%s no device. volinuse= %d\n", vol->vol_name, 
209             vol->is_in_use());
210          sendit(msg.c_str(), len, arg);
211       }
212    }
213    unlock_volumes();
214 }
215
216 /*
217  * Create a Volume item to put in the Volume list
218  *   Ensure that the device points to it.
219  */
220 static VOLRES *new_vol_item(DCR *dcr, const char *VolumeName)
221 {
222    VOLRES *vol;
223    vol = (VOLRES *)malloc(sizeof(VOLRES));
224    memset(vol, 0, sizeof(VOLRES));
225    vol->vol_name = bstrdup(VolumeName);
226    vol->dev = dcr->dev;
227    Dmsg3(dbglvl, "new Vol=%s at %p dev=%s\n",
228          VolumeName, vol->vol_name, vol->dev->print_name());
229    return vol;
230 }
231
232 static void free_vol_item(VOLRES *vol)
233 {
234    DEVICE *dev = NULL;
235
236    free(vol->vol_name);
237    if (vol->dev) {
238       dev = vol->dev;
239    }
240    free(vol);
241    if (dev) {
242       dev->vol = NULL;
243    }
244 }
245
246 /*
247  * Put a new Volume entry in the Volume list. This
248  *  effectively reserves the volume so that it will
249  *  not be mounted again.
250  *
251  * If the device has any current volume associated with it,
252  *  and it is a different Volume, and the device is not busy,
253  *  we release the old Volume item and insert the new one.
254  * 
255  * It is assumed that the device is free and locked so that
256  *  we can change the device structure.
257  *
258  * Some details of the Volume list handling:
259  *
260  *  1. The Volume list entry must be attached to the drive (rather than 
261  *       attached to a job as it currently is. I.e. the drive that "owns" 
262  *       the volume (in use, mounted)
263  *       must point to the volume (still to be maintained in a list).
264  *
265  *  2. The Volume is entered in the list when a drive is reserved.  
266  *
267  *  3. When a drive is in use, the device code must appropriately update the
268  *      volume name as it changes (currently the list is static -- an entry is
269  *      removed when the Volume is no longer reserved, in use or mounted).  
270  *      The new code must keep the same list entry as long as the drive
271  *       has any volume associated with it but the volume name in the list
272  *       must be updated when the drive has a different volume mounted.
273  *
274  *  4. A job that has reserved a volume, can un-reserve the volume, and if the 
275  *      volume is not mounted, and not reserved, and not in use, it will be
276  *      removed from the list.
277  *
278  *  5. If a job wants to reserve a drive with a different Volume from the one on
279  *      the drive, it can re-use the drive for the new Volume.
280  *
281  *  6. If a job wants a Volume that is in a different drive, it can either use the
282  *      other drive or take the volume, only if the other drive is not in use or
283  *      not reserved.
284  *
285  *  One nice aspect of this is that the reserve use count and the writer use count 
286  *  already exist and are correctly programmed and will need no changes -- use 
287  *  counts are always very tricky.
288  *
289  *  The old code had a concept of "reserving" a Volume, but was changed 
290  *  to reserving and using a drive.  A volume is must be attached to (owned by) a 
291  *  drive and can move from drive to drive or be unused given certain specific 
292  *  conditions of the drive.  The key is that the drive must "own" the Volume.  
293  *  The old code had the job (dcr) owning the volume (more or less).  The job was
294  *  to change the insertion and removal of the volumes from the list to be based 
295  *  on the drive rather than the job.  
296  *
297  *  Return: VOLRES entry on success
298  *          NULL volume busy on another drive
299  */
300 VOLRES *reserve_volume(DCR *dcr, const char *VolumeName)
301 {
302    VOLRES *vol, *nvol;
303    DEVICE * volatile dev = dcr->dev;
304
305    ASSERT(dev != NULL);
306
307    Dmsg2(dbglvl, "enter reserve_volume=%s drive=%s\n", VolumeName, 
308       dcr->dev->print_name());
309    /* 
310     * We lock the reservations system here to ensure
311     *  when adding a new volume that no newly scheduled
312     *  job can reserve it.
313     */
314    lock_volumes();
315    debug_list_volumes("begin reserve_volume");
316    /* 
317     * First, remove any old volume attached to this device as it
318     *  is no longer used.
319     */
320    if (dev->vol) {
321       vol = dev->vol;
322       Dmsg4(dbglvl, "Vol attached=%s, newvol=%s volinuse=%d on %s\n",
323          vol->vol_name, VolumeName, vol->is_in_use(), dev->print_name());
324       /*
325        * Make sure we don't remove the current volume we are inserting
326        *  because it was probably inserted by another job, or it
327        *  is not being used and is marked as not reserved.
328        */
329       if (strcmp(vol->vol_name, VolumeName) == 0) {
330          Dmsg2(dbglvl, "=== set reserved vol=%s dev=%s\n", VolumeName,
331                vol->dev->print_name());
332          goto get_out;                  /* Volume already on this device */
333       } else {
334          /* Don't release a volume if it was reserved by someone other than us */
335          if (vol->is_in_use() && !dcr->reserved_volume) { 
336             Dmsg1(dbglvl, "Cannot free vol=%s. It is reserved.\n", vol->vol_name);
337             vol = NULL;                  /* vol in use */
338             goto get_out;
339          }
340          Dmsg2(dbglvl, "reserve_vol free vol=%s at %p\n", vol->vol_name, vol->vol_name);
341          free_volume(dev);
342          Dmsg0(50, "set_unload\n");
343          dev->set_unload();             /* have to unload current volume */
344          debug_list_volumes("reserve_vol free");
345       }
346    }
347
348    /* Create a new Volume entry */
349    nvol = new_vol_item(dcr, VolumeName);
350
351    /*
352     * Now try to insert the new Volume
353     */
354    vol = (VOLRES *)vol_list->binary_insert(nvol, my_compare);
355    if (vol != nvol) {
356       Dmsg2(dbglvl, "Found vol=%s dev-same=%d\n", vol->vol_name, dev==vol->dev);
357       /*
358        * At this point, a Volume with this name already is in the list,
359        *   so we simply release our new Volume entry. Note, this should
360        *   only happen if we are moving the volume from one drive to another.
361        */
362       Dmsg2(dbglvl, "reserve_vol free-tmp vol=%s at %p\n", 
363             vol->vol_name, vol->vol_name);
364       /*
365        * Clear dev pointer so that free_vol_item() doesn't 
366        *  take away our volume. 
367        */
368       nvol->dev = NULL;                  /* don't zap dev entry */
369       free_vol_item(nvol);
370
371       /*
372        * Check if we are trying to use the Volume on a different drive
373        *  dev      is our device
374        *  vol->dev is where the Volume we want is
375        */
376       if (dev != vol->dev) {
377          /* Caller wants to switch Volume to another device */
378          if (!vol->dev->is_busy() && !vol->is_swapping()) {
379             int32_t slot;
380             Dmsg3(dbglvl, "==== Swap vol=%s from dev=%s to %s\n", 
381                VolumeName, vol->dev->print_name(), dev->print_name());
382             free_volume(dev);            /* free any volume attached to our drive */
383             Dmsg0(50, "set_unload\n");
384             dev->set_unload();           /* Unload any volume that is on our drive */
385             dcr->dev = vol->dev;         /* temp point to other dev */
386             slot = get_autochanger_loaded_slot(dcr);  /* get slot on other drive */
387             dcr->dev = dev;              /* restore dev */
388             vol->set_slot(slot);         /* save slot */
389             vol->dev->set_unload();      /* unload the other drive */
390             vol->set_swapping();         /* swap from other drive */
391             dev->swap_dev = vol->dev;    /* remember to get this vol */
392             dev->set_load();             /* then reload on our drive */
393             vol->dev->vol = NULL;        /* remove volume from other drive */
394             vol->dev = dev;              /* point the Volume at our drive */
395             dev->vol = vol;              /* point our drive at the Volume */
396          } else {
397             Dmsg3(dbglvl, "==== Swap not possible Vol busy vol=%s from dev=%s to %s\n", 
398                VolumeName, vol->dev->print_name(), dev->print_name());
399             vol = NULL;                  /* device busy */
400             goto get_out;
401          }
402       } else {
403          dev->vol = vol;
404       }
405    } else {
406       dev->vol = vol;                    /* point to newly inserted volume */
407    }
408
409 get_out:
410    if (vol) {
411       Dmsg2(dbglvl, "=== set in_use. vol=%s dev=%s\n", vol->vol_name,
412             vol->dev->print_name());
413       vol->set_in_use();
414       dcr->reserved_volume = true;
415       bstrncpy(dcr->VolumeName, vol->vol_name, sizeof(dcr->VolumeName));
416    }
417    debug_list_volumes("end new volume");
418    unlock_volumes();
419    return vol;
420 }
421
422 /* 
423  * Switch from current device to given device  
424  *   (not yet used) 
425  */
426 #ifdef xxx
427 void switch_device(DCR *dcr, DEVICE *dev)
428 {
429    DCR save_dcr;
430
431    dev->dlock();
432    memcpy(&save_dcr, dcr, sizeof(save_dcr));
433    clean_device(dcr);                  /* clean up the dcr */
434
435    dcr->dev = dev;                     /* get new device pointer */
436    Jmsg(dcr->jcr, M_INFO, 0, _("Device switch. New device %s chosen.\n"),
437       dcr->dev->print_name());
438
439    bstrncpy(dcr->VolumeName, save_dcr.VolumeName, sizeof(dcr->VolumeName));
440    bstrncpy(dcr->media_type, save_dcr.media_type, sizeof(dcr->media_type));
441    dcr->VolCatInfo.Slot = save_dcr.VolCatInfo.Slot;
442    bstrncpy(dcr->pool_name, save_dcr.pool_name, sizeof(dcr->pool_name));
443    bstrncpy(dcr->pool_type, save_dcr.pool_type, sizeof(dcr->pool_type));
444    bstrncpy(dcr->dev_name, dev->dev_name, sizeof(dcr->dev_name));
445
446 // dcr->set_reserved();
447
448    dev->dunlock();
449 }
450 #endif
451
452 /*
453  * Search for a Volume name in the Volume list.
454  *
455  *  Returns: VOLRES entry on success
456  *           NULL if the Volume is not in the list
457  */
458 VOLRES *find_volume(const char *VolumeName) 
459 {
460    VOLRES vol, *fvol;
461    /* Do not lock reservations here */
462    lock_volumes();
463    vol.vol_name = bstrdup(VolumeName);
464    fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
465    free(vol.vol_name);
466    Dmsg2(dbglvl, "find_vol=%s found=%d\n", VolumeName, fvol!=NULL);
467    debug_list_volumes("find_volume");
468    unlock_volumes();
469    return fvol;
470 }
471
472 /*  
473  * Free a Volume from the Volume list if it is no longer used
474  *   Note, for tape drives we want to remember where the Volume
475  *   was when last used, so rather than free the volume entry,
476  *   we simply mark it "not reserved" so when the drive is really
477  *   needed for another volume, we can reuse it.
478  *
479  *  Returns: true if the Volume found and "removed" from the list
480  *           false if the Volume is not in the list or is in use
481  */
482 bool volume_unused(DCR *dcr)
483 {
484    DEVICE *dev = dcr->dev;
485
486    if (!dev->vol) {
487       Dmsg1(dbglvl, "vol_unused: no vol on %s\n", dev->print_name());
488       debug_list_volumes("null vol cannot unreserve_volume");
489       return false;
490    }
491    if (dev->vol->is_swapping()) {
492       Dmsg1(dbglvl, "vol_unused: vol being swapped on %s\n", dev->print_name());
493       Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name);
494       dev->vol->clear_in_use();
495       debug_list_volumes("swapping vol cannot free_volume");
496       return false;
497    }
498
499    /*  
500     * If this is a tape, we do not free the volume, rather we wait
501     *  until the autoloader unloads it, or until another tape is
502     *  explicitly read in this drive. This allows the SD to remember
503     *  where the tapes are or last were.
504     */
505    Dmsg4(dbglvl, "=== set not reserved vol=%s num_writers=%d dev_reserved=%d dev=%s\n",
506       dev->vol->vol_name, dev->num_writers, dev->num_reserved(), dev->print_name());
507    Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name);
508    dev->vol->clear_in_use();
509    if (dev->is_tape() || dev->is_autochanger()) {
510       return true;
511    } else {
512       /*
513        * Note, this frees the volume reservation entry, but the 
514        *   file descriptor remains open with the OS.
515        */
516       return free_volume(dev);
517    }
518 }
519
520 /*
521  * Unconditionally release the volume entry
522  */
523 bool free_volume(DEVICE *dev)
524 {
525    VOLRES *vol;
526
527    if (dev->vol == NULL) {
528       Dmsg1(dbglvl, "No vol on dev %s\n", dev->print_name());
529       return false;
530    }
531    lock_volumes();
532    vol = dev->vol;
533    /* Don't free a volume while it is being swapped */
534    if (!vol->is_swapping()) {
535       Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name);
536       dev->vol = NULL;
537       vol_list->remove(vol);
538       Dmsg2(dbglvl, "=== remove volume %s dev=%s\n", vol->vol_name, dev->print_name());
539       free_vol_item(vol);
540       debug_list_volumes("free_volume");
541    }
542    unlock_volumes();
543    return true;
544 }
545
546       
547 /* Create the Volume list */
548 void create_volume_list()
549 {
550    VOLRES *vol = NULL;
551    if (vol_list == NULL) {
552       vol_list = New(dlist(vol, &vol->link));
553    }
554 }
555
556 /* Release all Volumes from the list */
557 void free_volume_list()
558 {
559    VOLRES *vol;
560    if (!vol_list) {
561       return;
562    }
563    lock_volumes();
564    foreach_dlist(vol, vol_list) {
565       if (vol->dev) {
566          Dmsg2(dbglvl, "free vol_list Volume=%s dev=%s\n", vol->vol_name, vol->dev->print_name());
567       } else {
568          Dmsg1(dbglvl, "free vol_list Volume=%s No dev\n", vol->vol_name);
569       }
570       free(vol->vol_name);
571       vol->vol_name = NULL;
572    }
573    delete vol_list;
574    vol_list = NULL;
575    unlock_volumes();
576 }
577
578 bool DCR::can_i_use_volume()
579 {
580    bool rtn = true;
581    VOLRES *vol;
582
583    lock_volumes();
584    vol = find_volume(VolumeName);
585    if (!vol) {
586       Dmsg1(dbglvl, "Vol=%s not in use.\n", VolumeName);
587       goto get_out;                   /* vol not in list */
588    }
589    ASSERT(vol->dev != NULL);
590
591    if (dev == vol->dev) {        /* same device OK */
592       Dmsg1(dbglvl, "Vol=%s on same dev.\n", VolumeName);
593       goto get_out;
594    } else {
595       Dmsg3(dbglvl, "Vol=%s on %s we have %s\n", VolumeName,
596             vol->dev->print_name(), dev->print_name());
597    }
598    /* ***FIXME*** check this ... */
599    if (!vol->dev->is_busy()) {
600       Dmsg2(dbglvl, "Vol=%s dev=%s not busy.\n", VolumeName, vol->dev->print_name());
601       goto get_out;
602    } else {
603       Dmsg2(dbglvl, "Vol=%s dev=%s busy.\n", VolumeName, vol->dev->print_name());
604    }
605    Dmsg2(dbglvl, "Vol=%s in use by %s.\n", VolumeName, vol->dev->print_name());
606    rtn = false;
607
608 get_out:
609    unlock_volumes();
610    return rtn;
611
612 }