From 04db93706a8d12316b188137b66eaadcbaea2941 Mon Sep 17 00:00:00 2001 From: Kern Sibbald Date: Tue, 21 Oct 2008 14:24:10 +0000 Subject: [PATCH] kes Split volume management code out of src/stored/reserve.c into a new file vol_mgr.c kes Modify configure to do an automatic make clean. This ensures that any changes to ./configure options are handled correctly. git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@7866 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/autoconf/configure.in | 6 + bacula/configure | 6 + bacula/src/stored/Makefile.in | 41 +-- bacula/src/stored/protos.h | 26 +- bacula/src/stored/reserve.c | 544 +----------------------------- bacula/src/stored/vol_mgr.c | 612 ++++++++++++++++++++++++++++++++++ bacula/technotes-2.5 | 6 + 7 files changed, 674 insertions(+), 567 deletions(-) create mode 100644 bacula/src/stored/vol_mgr.c diff --git a/bacula/autoconf/configure.in b/bacula/autoconf/configure.in index 29bc8c5b28..9510443bd3 100644 --- a/bacula/autoconf/configure.in +++ b/bacula/autoconf/configure.in @@ -2813,6 +2813,12 @@ if test X"$GCC" = "Xyes" ; then fi fi +# clean up any old junk +echo " " +echo "Cleaning up" +echo " " +make clean + if test "x${db_type}" = "xInternal" ; then echo " " echo " " diff --git a/bacula/configure b/bacula/configure index d4721c2327..b8daeb6dc5 100755 --- a/bacula/configure +++ b/bacula/configure @@ -44139,6 +44139,12 @@ if test X"$GCC" = "Xyes" ; then fi fi +# clean up any old junk +echo " " +echo "Cleaning up" +echo " " +make clean + if test "x${db_type}" = "xInternal" ; then echo " " echo " " diff --git a/bacula/src/stored/Makefile.in b/bacula/src/stored/Makefile.in index aa56eb3d4a..d9a74e41d1 100644 --- a/bacula/src/stored/Makefile.in +++ b/bacula/src/stored/Makefile.in @@ -28,39 +28,42 @@ SDOBJS = stored.o ansi_label.o vtape.o \ label.o lock.o mac.o match_bsr.o mount.o parse_bsr.o \ pythonsd.o read.o read_record.o record.o \ reserve.o scan.o sd_plugins.o \ - spool.o status.o stored_conf.o wait.o + spool.o status.o stored_conf.o vol_mgr.o wait.o # btape TAPEOBJS = btape.o block.o butil.o dev.o device.o label.o vtape.o \ lock.o ansi_label.o dvd.o ebcdic.o \ autochanger.o acquire.o mount.o record.o read_record.o \ - reserve.o \ - stored_conf.o match_bsr.o parse_bsr.o scan.o spool.o wait.o + reserve.o stored_conf.o match_bsr.o parse_bsr.o scan.o \ + spool.o vol_mgr.o wait.o # bls BLSOBJS = bls.o block.o butil.o device.o dev.o label.o match_bsr.o vtape.o \ ansi_label.o dvd.o ebcdic.o lock.o \ autochanger.o acquire.o mount.o parse_bsr.o record.o \ - read_record.o reserve.o scan.o stored_conf.o spool.o wait.o + read_record.o reserve.o scan.o stored_conf.o spool.o \ + vol_mgr.o wait.o # bextract BEXTOBJS = bextract.o block.o device.o dev.o label.o record.o vtape.o \ ansi_label.o dvd.o ebcdic.o lock.o \ autochanger.o acquire.o mount.o match_bsr.o parse_bsr.o butil.o \ - read_record.o reserve.o scan.o stored_conf.o spool.o wait.o + read_record.o reserve.o scan.o stored_conf.o spool.o \ + vol_mgr.o wait.o # bscan SCNOBJS = bscan.o block.o device.o dev.o label.o vtape.o \ ansi_label.o dvd.o ebcdic.o lock.o \ autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \ - butil.o read_record.o scan.o reserve.o stored_conf.o spool.o wait.o + butil.o read_record.o scan.o reserve.o stored_conf.o spool.o \ + vol_mgr.o wait.o # bcopy COPYOBJS = bcopy.o block.o device.o dev.o label.o vtape.o \ ansi_label.o dvd.o ebcdic.o lock.o \ autochanger.o acquire.o mount.o record.o match_bsr.o parse_bsr.o \ butil.o read_record.o reserve.o \ - scan.o stored_conf.o spool.o wait.o + scan.o stored_conf.o spool.o vol_mgr.o wait.o @@ -87,61 +90,61 @@ all: Makefile bacula-sd @STATIC_SD@ bls bextract bscan btape bcopy bacula-sd: Makefile $(SDOBJS) ../lib/libbacpy$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE) @echo "Linking $@ ..." $(LIBTOOL_LINK) $(CXX) $(WLDFLAGS) $(LDFLAGS) -L../lib -o $@ $(SDOBJS) $(FDLIBS) \ - -lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(DLIB) $(LIBS) $(WRAPLIBS) \ + -lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(DLIB) $(LIBS) $(WRAPLIBS) \ $(GETTEXT_LIBS) $(OPENSSL_LIBS) static-bacula-sd: Makefile $(SDOBJS) ../lib/libbacpy$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE) $(LIBTOOL_LINK) $(CXX) $(WLDFLAGS) $(LDFLAGS) -static -L../lib -o $@ $(SDOBJS) $(FDLIBS) \ - -lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(DLIB) $(LIBS) $(WRAPLIBS) \ + -lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(DLIB) $(LIBS) $(WRAPLIBS) \ $(GETTEXT_LIBS) $(OPENSSL_LIBS) strip $@ btape.o: btape.c @echo "Compiling $<" $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) \ - -I$(basedir) $(DINCLUDE) $(CFLAGS) $< + -I$(basedir) $(DINCLUDE) $(CFLAGS) $< btape: Makefile $(TAPEOBJS) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../cats/libbacsql$(DEFAULT_ARCHIVE_TYPE) $(LIBTOOL_LINK) $(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -L../cats -o $@ $(TAPEOBJS) \ - -lbacsql -lbaccfg -lbac $(DLIB) -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) + -lbacsql -lbaccfg -lbac $(DLIB) -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) bls.o: bls.c @echo "Compiling $<" $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) \ - -I$(basedir) $(DINCLUDE) $(CFLAGS) $< + -I$(basedir) $(DINCLUDE) $(CFLAGS) $< bls: Makefile $(BLSOBJS) ../findlib/libbacfind$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE) @echo "Compiling $<" $(LIBTOOL_LINK) $(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -L../findlib -o $@ $(BLSOBJS) $(DLIB) \ - -lbacfind -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) + -lbacfind -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) bextract.o: bextract.c @echo "Compiling $<" $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) \ - -I$(basedir) $(DINCLUDE) $(CFLAGS) $< + -I$(basedir) $(DINCLUDE) $(CFLAGS) $< bextract: Makefile $(BEXTOBJS) ../findlib/libbacfind$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE) @echo "Compiling $<" $(LIBTOOL_LINK) $(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -L../findlib -o $@ $(BEXTOBJS) $(DLIB) $(FDLIBS) \ - -lbacfind -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) + -lbacfind -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) bscan.o: bscan.c @echo "Compiling $<" $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) \ - -I$(basedir) $(DINCLUDE) $(CFLAGS) $< + -I$(basedir) $(DINCLUDE) $(CFLAGS) $< bscan: Makefile $(SCNOBJS) ../findlib/libbacfind$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../cats/libbacsql$(DEFAULT_ARCHIVE_TYPE) $(LIBTOOL_LINK) $(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -L../cats -L../findlib -o $@ $(SCNOBJS) \ - -lbacsql $(DB_LIBS) $(FDLIBS) -lbacfind -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) + -lbacsql $(DB_LIBS) $(FDLIBS) -lbacfind -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) bcopy.o: bcopy.c @echo "Compiling $<" $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) \ - -I$(basedir) $(DINCLUDE) $(CFLAGS) $< + -I$(basedir) $(DINCLUDE) $(CFLAGS) $< bcopy: Makefile $(COPYOBJS) ../findlib/libbacfind$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE) $(LIBTOOL_LINK) $(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -o $@ $(COPYOBJS) \ - -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) + -lbaccfg -lbac -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) Makefile: $(srcdir)/Makefile.in $(topdir)/config.status cd $(topdir) \ diff --git a/bacula/src/stored/protos.h b/bacula/src/stored/protos.h index af8e667f89..4ed9ef281f 100644 --- a/bacula/src/stored/protos.h +++ b/bacula/src/stored/protos.h @@ -216,23 +216,13 @@ void _lock_reservations(); void _unlock_reservations(); void _lock_volumes(); void _unlock_volumes(); -VOLRES *reserve_volume(DCR *dcr, const char *VolumeName); -VOLRES *find_volume(const char *VolumeName); -bool free_volume(DEVICE *dev); void unreserve_device(DCR *dcr); -bool volume_unused(DCR *dcr); -void create_volume_list(); -void free_volume_list(); -void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg); -bool is_volume_in_use(DCR *dcr); void send_drive_reserve_messages(JCR *jcr, void sendit(const char *msg, int len, void *sarg), void *arg); bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx); int search_res_for_device(RCTX &rctx); void release_reserve_messages(JCR *jcr); -void debug_list_volumes(const char *imsg); extern int reservations_lock_count; -extern int vol_list_lock_count; #ifdef SD_DEBUG_LOCK @@ -271,9 +261,25 @@ extern int vol_list_lock_count; #define unlock_reservations() _unlock_reservations() #define lock_volumes() _lock_volumes() #define unlock_volumes() _unlock_volumes() +bool volume_unused(DCR *dcr); +void create_volume_list(); +void free_volume_list(); +void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg); +bool is_volume_in_use(DCR *dcr); +void debug_list_volumes(const char *imsg); +extern int vol_list_lock_count; #endif +/* From vol_mgr.c */ +void init_vol_list_lock(); +void term_vol_list_lock(); +VOLRES *reserve_volume(DCR *dcr, const char *VolumeName); +VOLRES *find_volume(const char *VolumeName); +bool free_volume(DEVICE *dev); +bool is_vol_list_empty(); +dlist *dup_vol_list(JCR *jcr); +void free_temp_vol_list(dlist *temp_vol_list); /* From spool.c */ diff --git a/bacula/src/stored/reserve.c b/bacula/src/stored/reserve.c index b262b76b21..b55da00227 100644 --- a/bacula/src/stored/reserve.c +++ b/bacula/src/stored/reserve.c @@ -41,9 +41,7 @@ const int dbglvl = 50; -static dlist *vol_list = NULL; static brwlock_t reservation_lock; -static brwlock_t vol_list_lock; /* Forward referenced functions */ static int can_reserve_drive(DCR *dcr, RCTX &rctx); @@ -78,11 +76,6 @@ bool use_cmd(JCR *jcr) return true; } -static int my_compare(void *item1, void *item2) -{ - return strcmp(((VOLRES *)item1)->vol_name, ((VOLRES *)item2)->vol_name); -} - /* * This allows a given thread to recursively call lock_reservations. * It must, of course, call unlock_... the same number of times. @@ -96,17 +89,13 @@ void init_reservations_lock() be.bstrerror(errstat)); } - if ((errstat=rwl_init(&vol_list_lock)) != 0) { - berrno be; - Emsg1(M_ABORT, 0, _("Unable to initialize volume list lock. ERR=%s\n"), - be.bstrerror(errstat)); - } + init_vol_list_lock(); } void term_reservations_lock() { rwl_destroy(&reservation_lock); - rwl_destroy(&vol_list_lock); + term_vol_list_lock(); } int reservations_lock_count = 0; @@ -134,347 +123,6 @@ void _unlock_reservations() } } -int vol_list_lock_count = 0; - -/* - * This allows a given thread to recursively call to lock_volumes() - */ -void _lock_volumes() -{ - int errstat; - vol_list_lock_count++; - if ((errstat=rwl_writelock(&vol_list_lock)) != 0) { - berrno be; - Emsg2(M_ABORT, 0, "rwl_writelock failure. stat=%d: ERR=%s\n", - errstat, be.bstrerror(errstat)); - } -} - -void _unlock_volumes() -{ - int errstat; - vol_list_lock_count--; - if ((errstat=rwl_writeunlock(&vol_list_lock)) != 0) { - berrno be; - Emsg2(M_ABORT, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n", - errstat, be.bstrerror(errstat)); - } -} - - -/* - * List Volumes -- this should be moved to status.c - */ -enum { - debug_lock = true, - debug_nolock = false -}; - -void debug_list_volumes(const char *imsg) -{ - VOLRES *vol; - POOL_MEM msg(PM_MESSAGE); - - lock_volumes(); - foreach_dlist(vol, vol_list) { - if (vol->dev) { - Mmsg(msg, "List %s: %s in_use=%d on device %s\n", imsg, - vol->vol_name, vol->is_in_use(), vol->dev->print_name()); - } else { - Mmsg(msg, "List %s: %s in_use=%d no dev\n", imsg, vol->vol_name, - vol->is_in_use()); - } - Dmsg1(dbglvl, "%s", msg.c_str()); - } - - unlock_volumes(); -} - - -/* - * List Volumes -- this should be moved to status.c - */ -void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg) -{ - VOLRES *vol; - POOL_MEM msg(PM_MESSAGE); - int len; - - lock_volumes(); - foreach_dlist(vol, vol_list) { - DEVICE *dev = vol->dev; - if (dev) { - len = Mmsg(msg, "%s on device %s\n", vol->vol_name, dev->print_name()); - sendit(msg.c_str(), len, arg); - len = Mmsg(msg, " Reader=%d writers=%d devres=%d volinuse=%d\n", - dev->can_read()?1:0, dev->num_writers, dev->num_reserved(), - vol->is_in_use()); - sendit(msg.c_str(), len, arg); - } else { - len = Mmsg(msg, "%s no device. volinuse= %d\n", vol->vol_name, - vol->is_in_use()); - sendit(msg.c_str(), len, arg); - } - } - unlock_volumes(); -} - -/* - * Create a Volume item to put in the Volume list - * Ensure that the device points to it. - */ -static VOLRES *new_vol_item(DCR *dcr, const char *VolumeName) -{ - VOLRES *vol; - vol = (VOLRES *)malloc(sizeof(VOLRES)); - memset(vol, 0, sizeof(VOLRES)); - vol->vol_name = bstrdup(VolumeName); - vol->dev = dcr->dev; - Dmsg3(dbglvl, "new Vol=%s at %p dev=%s\n", - VolumeName, vol->vol_name, vol->dev->print_name()); - return vol; -} - -static void free_vol_item(VOLRES *vol) -{ - DEVICE *dev = NULL; - - free(vol->vol_name); - if (vol->dev) { - dev = vol->dev; - } - free(vol); - if (dev) { - dev->vol = NULL; - } -} - -/* - * Put a new Volume entry in the Volume list. This - * effectively reserves the volume so that it will - * not be mounted again. - * - * If the device has any current volume associated with it, - * and it is a different Volume, and the device is not busy, - * we release the old Volume item and insert the new one. - * - * It is assumed that the device is free and locked so that - * we can change the device structure. - * - * Some details of the Volume list handling: - * - * 1. The Volume list entry must be attached to the drive (rather than - * attached to a job as it currently is. I.e. the drive that "owns" - * the volume (in use, mounted) - * must point to the volume (still to be maintained in a list). - * - * 2. The Volume is entered in the list when a drive is reserved. - * - * 3. When a drive is in use, the device code must appropriately update the - * volume name as it changes (currently the list is static -- an entry is - * removed when the Volume is no longer reserved, in use or mounted). - * The new code must keep the same list entry as long as the drive - * has any volume associated with it but the volume name in the list - * must be updated when the drive has a different volume mounted. - * - * 4. A job that has reserved a volume, can un-reserve the volume, and if the - * volume is not mounted, and not reserved, and not in use, it will be - * removed from the list. - * - * 5. If a job wants to reserve a drive with a different Volume from the one on - * the drive, it can re-use the drive for the new Volume. - * - * 6. If a job wants a Volume that is in a different drive, it can either use the - * other drive or take the volume, only if the other drive is not in use or - * not reserved. - * - * One nice aspect of this is that the reserve use count and the writer use count - * already exist and are correctly programmed and will need no changes -- use - * counts are always very tricky. - * - * The old code had a concept of "reserving" a Volume, but was changed - * to reserving and using a drive. A volume is must be attached to (owned by) a - * drive and can move from drive to drive or be unused given certain specific - * conditions of the drive. The key is that the drive must "own" the Volume. - * The old code had the job (dcr) owning the volume (more or less). The job was - * to change the insertion and removal of the volumes from the list to be based - * on the drive rather than the job. - * - * Return: VOLRES entry on success - * NULL volume busy on another drive - */ -VOLRES *reserve_volume(DCR *dcr, const char *VolumeName) -{ - VOLRES *vol, *nvol; - DEVICE * volatile dev = dcr->dev; - - ASSERT(dev != NULL); - - Dmsg2(dbglvl, "enter reserve_volume=%s drive=%s\n", VolumeName, - dcr->dev->print_name()); - /* - * We lock the reservations system here to ensure - * when adding a new volume that no newly scheduled - * job can reserve it. - */ - lock_volumes(); - debug_list_volumes("begin reserve_volume"); - /* - * First, remove any old volume attached to this device as it - * is no longer used. - */ - if (dev->vol) { - vol = dev->vol; - Dmsg4(dbglvl, "Vol attached=%s, newvol=%s volinuse=%d on %s\n", - vol->vol_name, VolumeName, vol->is_in_use(), dev->print_name()); - /* - * Make sure we don't remove the current volume we are inserting - * because it was probably inserted by another job, or it - * is not being used and is marked as not reserved. - */ - if (strcmp(vol->vol_name, VolumeName) == 0) { - Dmsg2(dbglvl, "=== set reserved vol=%s dev=%s\n", VolumeName, - vol->dev->print_name()); - goto get_out; /* Volume already on this device */ - } else { - /* Don't release a volume if it was reserved by someone other than us */ - if (vol->is_in_use() && !dcr->reserved_volume) { - Dmsg1(dbglvl, "Cannot free vol=%s. It is reserved.\n", vol->vol_name); - vol = NULL; /* vol in use */ - goto get_out; - } - Dmsg2(dbglvl, "reserve_vol free vol=%s at %p\n", vol->vol_name, vol->vol_name); - free_volume(dev); - Dmsg0(50, "set_unload\n"); - dev->set_unload(); /* have to unload current volume */ - debug_list_volumes("reserve_vol free"); - } - } - - /* Create a new Volume entry */ - nvol = new_vol_item(dcr, VolumeName); - - /* - * Now try to insert the new Volume - */ - vol = (VOLRES *)vol_list->binary_insert(nvol, my_compare); - if (vol != nvol) { - Dmsg2(dbglvl, "Found vol=%s dev-same=%d\n", vol->vol_name, dev==vol->dev); - /* - * At this point, a Volume with this name already is in the list, - * so we simply release our new Volume entry. Note, this should - * only happen if we are moving the volume from one drive to another. - */ - Dmsg2(dbglvl, "reserve_vol free-tmp vol=%s at %p\n", - vol->vol_name, vol->vol_name); - /* - * Clear dev pointer so that free_vol_item() doesn't - * take away our volume. - */ - nvol->dev = NULL; /* don't zap dev entry */ - free_vol_item(nvol); - - /* - * Check if we are trying to use the Volume on a different drive - * dev is our device - * vol->dev is where the Volume we want is - */ - if (dev != vol->dev) { - /* Caller wants to switch Volume to another device */ - if (!vol->dev->is_busy() && !vol->is_swapping()) { - int32_t slot; - Dmsg3(dbglvl, "==== Swap vol=%s from dev=%s to %s\n", - VolumeName, vol->dev->print_name(), dev->print_name()); - free_volume(dev); /* free any volume attached to our drive */ - Dmsg0(50, "set_unload\n"); - dev->set_unload(); /* Unload any volume that is on our drive */ - dcr->dev = vol->dev; /* temp point to other dev */ - slot = get_autochanger_loaded_slot(dcr); /* get slot on other drive */ - dcr->dev = dev; /* restore dev */ - vol->set_slot(slot); /* save slot */ - vol->dev->set_unload(); /* unload the other drive */ - vol->set_swapping(); /* swap from other drive */ - dev->swap_dev = vol->dev; /* remember to get this vol */ - dev->set_load(); /* then reload on our drive */ - vol->dev->vol = NULL; /* remove volume from other drive */ - vol->dev = dev; /* point the Volume at our drive */ - dev->vol = vol; /* point our drive at the Volume */ - } else { - Dmsg3(dbglvl, "==== Swap not possible Vol busy vol=%s from dev=%s to %s\n", - VolumeName, vol->dev->print_name(), dev->print_name()); - vol = NULL; /* device busy */ - goto get_out; - } - } else { - dev->vol = vol; - } - } else { - dev->vol = vol; /* point to newly inserted volume */ - } - -get_out: - if (vol) { - Dmsg2(dbglvl, "=== set in_use. vol=%s dev=%s\n", vol->vol_name, - vol->dev->print_name()); - vol->set_in_use(); - dcr->reserved_volume = true; - bstrncpy(dcr->VolumeName, vol->vol_name, sizeof(dcr->VolumeName)); - } - debug_list_volumes("end new volume"); - unlock_volumes(); - return vol; -} - -/* - * Switch from current device to given device - * (not yet used) - */ -#ifdef xxx -void switch_device(DCR *dcr, DEVICE *dev) -{ - DCR save_dcr; - - dev->dlock(); - memcpy(&save_dcr, dcr, sizeof(save_dcr)); - clean_device(dcr); /* clean up the dcr */ - - dcr->dev = dev; /* get new device pointer */ - Jmsg(dcr->jcr, M_INFO, 0, _("Device switch. New device %s chosen.\n"), - dcr->dev->print_name()); - - bstrncpy(dcr->VolumeName, save_dcr.VolumeName, sizeof(dcr->VolumeName)); - bstrncpy(dcr->media_type, save_dcr.media_type, sizeof(dcr->media_type)); - dcr->VolCatInfo.Slot = save_dcr.VolCatInfo.Slot; - bstrncpy(dcr->pool_name, save_dcr.pool_name, sizeof(dcr->pool_name)); - bstrncpy(dcr->pool_type, save_dcr.pool_type, sizeof(dcr->pool_type)); - bstrncpy(dcr->dev_name, dev->dev_name, sizeof(dcr->dev_name)); - -// dcr->set_reserved(); - - dev->dunlock(); -} -#endif - -/* - * Search for a Volume name in the Volume list. - * - * Returns: VOLRES entry on success - * NULL if the Volume is not in the list - */ -VOLRES *find_volume(const char *VolumeName) -{ - VOLRES vol, *fvol; - /* Do not lock reservations here */ - lock_volumes(); - vol.vol_name = bstrdup(VolumeName); - fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare); - free(vol.vol_name); - Dmsg2(dbglvl, "find_vol=%s found=%d\n", VolumeName, fvol!=NULL); - debug_list_volumes("find_volume"); - unlock_volumes(); - return fvol; -} - void DCR::set_reserved() { m_reserved = true; @@ -516,149 +164,6 @@ void DCR::unreserve_device() unlock_volumes(); } -/* - * Free a Volume from the Volume list if it is no longer used - * Note, for tape drives we want to remember where the Volume - * was when last used, so rather than free the volume entry, - * we simply mark it "not reserved" so when the drive is really - * needed for another volume, we can reuse it. - * - * Returns: true if the Volume found and "removed" from the list - * false if the Volume is not in the list or is in use - */ -bool volume_unused(DCR *dcr) -{ - DEVICE *dev = dcr->dev; - - if (!dev->vol) { - Dmsg1(dbglvl, "vol_unused: no vol on %s\n", dev->print_name()); - debug_list_volumes("null vol cannot unreserve_volume"); - return false; - } - if (dev->vol->is_swapping()) { - Dmsg1(dbglvl, "vol_unused: vol being swapped on %s\n", dev->print_name()); - Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name); - dev->vol->clear_in_use(); - debug_list_volumes("swapping vol cannot free_volume"); - return false; - } - - /* - * If this is a tape, we do not free the volume, rather we wait - * until the autoloader unloads it, or until another tape is - * explicitly read in this drive. This allows the SD to remember - * where the tapes are or last were. - */ - Dmsg4(dbglvl, "=== set not reserved vol=%s num_writers=%d dev_reserved=%d dev=%s\n", - dev->vol->vol_name, dev->num_writers, dev->num_reserved(), dev->print_name()); - Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name); - dev->vol->clear_in_use(); - if (dev->is_tape() || dev->is_autochanger()) { - return true; - } else { - /* - * Note, this frees the volume reservation entry, but the - * file descriptor remains open with the OS. - */ - return free_volume(dev); - } -} - -/* - * Unconditionally release the volume entry - */ -bool free_volume(DEVICE *dev) -{ - VOLRES *vol; - - if (dev->vol == NULL) { - Dmsg1(dbglvl, "No vol on dev %s\n", dev->print_name()); - return false; - } - lock_volumes(); - vol = dev->vol; - /* Don't free a volume while it is being swapped */ - if (!vol->is_swapping()) { - Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name); - dev->vol = NULL; - vol_list->remove(vol); - Dmsg2(dbglvl, "=== remove volume %s dev=%s\n", vol->vol_name, dev->print_name()); - free_vol_item(vol); - debug_list_volumes("free_volume"); - } - unlock_volumes(); - return true; -} - - -/* Create the Volume list */ -void create_volume_list() -{ - VOLRES *vol = NULL; - if (vol_list == NULL) { - vol_list = New(dlist(vol, &vol->link)); - } -} - -/* Release all Volumes from the list */ -void free_volume_list() -{ - VOLRES *vol; - if (!vol_list) { - return; - } - lock_volumes(); - foreach_dlist(vol, vol_list) { - if (vol->dev) { - Dmsg2(dbglvl, "free vol_list Volume=%s dev=%s\n", vol->vol_name, vol->dev->print_name()); - } else { - Dmsg1(dbglvl, "free vol_list Volume=%s No dev\n", vol->vol_name); - } - free(vol->vol_name); - vol->vol_name = NULL; - } - delete vol_list; - vol_list = NULL; - unlock_volumes(); -} - -bool DCR::can_i_use_volume() -{ - bool rtn = true; - VOLRES *vol; - - lock_volumes(); - vol = find_volume(VolumeName); - if (!vol) { - Dmsg1(dbglvl, "Vol=%s not in use.\n", VolumeName); - goto get_out; /* vol not in list */ - } - ASSERT(vol->dev != NULL); - - if (dev == vol->dev) { /* same device OK */ - Dmsg1(dbglvl, "Vol=%s on same dev.\n", VolumeName); - goto get_out; - } else { - Dmsg3(dbglvl, "Vol=%s on %s we have %s\n", VolumeName, - vol->dev->print_name(), dev->print_name()); - } - /* ***FIXME*** check this ... */ - if (!vol->dev->is_busy()) { - Dmsg2(dbglvl, "Vol=%s dev=%s not busy.\n", VolumeName, vol->dev->print_name()); - goto get_out; - } else { - Dmsg2(dbglvl, "Vol=%s dev=%s busy.\n", VolumeName, vol->dev->print_name()); - } - Dmsg2(dbglvl, "Vol=%s in use by %s.\n", VolumeName, vol->dev->print_name()); - rtn = false; - -get_out: - unlock_volumes(); - return rtn; - -} - - /* * We get the following type of information: * @@ -919,39 +424,10 @@ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx) * force try a mounted drive because they are all busy), we * start by looking at all the Volumes in the volume list. */ - if (!vol_list->empty() && rctx.append && rctx.PreferMountedVols) { - dlist *temp_vol_list, *save_vol_list; + if (!is_vol_list_empty() && rctx.append && rctx.PreferMountedVols) { + dlist *temp_vol_list; VOLRES *vol = NULL; - lock_volumes(); - Dmsg0(dbglvl, "lock volumes\n"); - - /* - * Create a temporary copy of the volume list. We do this, - * to avoid having the volume list locked during the - * call to reserve_device(), which would cause a deadlock. - * Note, we may want to add an update counter on the vol_list - * so that if it is modified while we are traversing the copy - * we can take note and act accordingly (probably redo the - * search at least a few times). - */ - Dmsg0(dbglvl, "duplicate vol list\n"); - temp_vol_list = New(dlist(vol, &vol->link)); - foreach_dlist(vol, vol_list) { - VOLRES *nvol; - VOLRES *tvol = (VOLRES *)malloc(sizeof(VOLRES)); - memset(tvol, 0, sizeof(VOLRES)); - tvol->vol_name = bstrdup(vol->vol_name); - tvol->dev = vol->dev; - nvol = (VOLRES *)temp_vol_list->binary_insert(tvol, my_compare); - if (tvol != nvol) { - tvol->dev = NULL; /* don't zap dev entry */ - free_vol_item(tvol); - Pmsg0(000, "Logic error. Duplicating vol list hit duplicate.\n"); - Jmsg(jcr, M_WARNING, 0, "Logic error. Duplicating vol list hit duplicate.\n"); - } - } - Dmsg0(dbglvl, "unlock volumes\n"); - unlock_volumes(); + temp_vol_list = dup_vol_list(jcr); /* Look through reserved volumes for one we can use */ Dmsg0(dbglvl, "look for vol in vol list\n"); @@ -1014,15 +490,7 @@ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx) } /* end for loop over reserved volumes */ Dmsg0(dbglvl, "lock volumes\n"); - lock_volumes(); - save_vol_list = vol_list; - vol_list = temp_vol_list; - free_volume_list(); /* release temp_vol_list */ - vol_list = save_vol_list; - Dmsg0(dbglvl, "deleted temp vol list\n"); - Dmsg0(dbglvl, "unlock volumes\n"); - unlock_volumes(); - debug_list_volumes("after free temp table"); + free_temp_vol_list(temp_vol_list); } if (ok) { Dmsg1(dbglvl, "OK dev found. Vol=%s from in-use vols list\n", rctx.VolumeName); diff --git a/bacula/src/stored/vol_mgr.c b/bacula/src/stored/vol_mgr.c new file mode 100644 index 0000000000..17dba76950 --- /dev/null +++ b/bacula/src/stored/vol_mgr.c @@ -0,0 +1,612 @@ +/* + Bacula® - The Network Backup Solution + + Copyright (C) 2000-2008 Free Software Foundation Europe e.V. + + The main author of Bacula is Kern Sibbald, with contributions from + many others, a complete list can be found in the file AUTHORS. + This program is Free Software; you can redistribute it and/or + modify it under the terms of version two of the GNU General Public + License as published by the Free Software Foundation and included + in the file LICENSE. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Bacula® is a registered trademark of Kern Sibbald. + The licensor of Bacula is the Free Software Foundation Europe + (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, + Switzerland, email:ftf@fsfeurope.org. +*/ +/* + * Volume management functions for Storage Daemon + * + * Kern Sibbald, MM + * + * Split from reserve.c October 2008 + * + * Version $Id: reserve.c 7380 2008-07-14 10:42:59Z kerns $ + * + */ + +#include "bacula.h" +#include "stored.h" + +const int dbglvl = 50; + +static dlist *vol_list = NULL; +static brwlock_t vol_list_lock; + +/* Forward referenced functions */ +static void free_vol_item(VOLRES *vol); + + +static int my_compare(void *item1, void *item2) +{ + return strcmp(((VOLRES *)item1)->vol_name, ((VOLRES *)item2)->vol_name); +} + +bool is_vol_list_empty() +{ + return vol_list->empty(); +} + +int vol_list_lock_count = 0; + +void init_vol_list_lock() +{ + int errstat; + if ((errstat=rwl_init(&vol_list_lock)) != 0) { + berrno be; + Emsg1(M_ABORT, 0, _("Unable to initialize volume list lock. ERR=%s\n"), + be.bstrerror(errstat)); + } +} + +void term_vol_list_lock() +{ + rwl_destroy(&vol_list_lock); +} + + + +/* + * This allows a given thread to recursively call to lock_volumes() + */ +void _lock_volumes() +{ + int errstat; + vol_list_lock_count++; + if ((errstat=rwl_writelock(&vol_list_lock)) != 0) { + berrno be; + Emsg2(M_ABORT, 0, "rwl_writelock failure. stat=%d: ERR=%s\n", + errstat, be.bstrerror(errstat)); + } +} + +void _unlock_volumes() +{ + int errstat; + vol_list_lock_count--; + if ((errstat=rwl_writeunlock(&vol_list_lock)) != 0) { + berrno be; + Emsg2(M_ABORT, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n", + errstat, be.bstrerror(errstat)); + } +} + +dlist *dup_vol_list(JCR *jcr) +{ + dlist *temp_vol_list; + VOLRES *vol = NULL; + + lock_volumes(); + Dmsg0(dbglvl, "lock volumes\n"); + + /* + * Create a temporary copy of the volume list. We do this, + * to avoid having the volume list locked during the + * call to reserve_device(), which would cause a deadlock. + * Note, we may want to add an update counter on the vol_list + * so that if it is modified while we are traversing the copy + * we can take note and act accordingly (probably redo the + * search at least a few times). + */ + Dmsg0(dbglvl, "duplicate vol list\n"); + temp_vol_list = New(dlist(vol, &vol->link)); + foreach_dlist(vol, vol_list) { + VOLRES *nvol; + VOLRES *tvol = (VOLRES *)malloc(sizeof(VOLRES)); + memset(tvol, 0, sizeof(VOLRES)); + tvol->vol_name = bstrdup(vol->vol_name); + tvol->dev = vol->dev; + nvol = (VOLRES *)temp_vol_list->binary_insert(tvol, my_compare); + if (tvol != nvol) { + tvol->dev = NULL; /* don't zap dev entry */ + free_vol_item(tvol); + Pmsg0(000, "Logic error. Duplicating vol list hit duplicate.\n"); + Jmsg(jcr, M_WARNING, 0, "Logic error. Duplicating vol list hit duplicate.\n"); + } + } + Dmsg0(dbglvl, "unlock volumes\n"); + unlock_volumes(); + return temp_vol_list; +} + +void free_temp_vol_list(dlist *temp_vol_list) +{ + dlist *save_vol_list; + + lock_volumes(); + save_vol_list = vol_list; + vol_list = temp_vol_list; + free_volume_list(); /* release temp_vol_list */ + vol_list = save_vol_list; + Dmsg0(dbglvl, "deleted temp vol list\n"); + Dmsg0(dbglvl, "unlock volumes\n"); + unlock_volumes(); + debug_list_volumes("after free temp table"); +} + + +/* + * List Volumes -- this should be moved to status.c + */ +enum { + debug_lock = true, + debug_nolock = false +}; + +void debug_list_volumes(const char *imsg) +{ + VOLRES *vol; + POOL_MEM msg(PM_MESSAGE); + + lock_volumes(); + foreach_dlist(vol, vol_list) { + if (vol->dev) { + Mmsg(msg, "List %s: %s in_use=%d on device %s\n", imsg, + vol->vol_name, vol->is_in_use(), vol->dev->print_name()); + } else { + Mmsg(msg, "List %s: %s in_use=%d no dev\n", imsg, vol->vol_name, + vol->is_in_use()); + } + Dmsg1(dbglvl, "%s", msg.c_str()); + } + + unlock_volumes(); +} + + +/* + * List Volumes -- this should be moved to status.c + */ +void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg) +{ + VOLRES *vol; + POOL_MEM msg(PM_MESSAGE); + int len; + + lock_volumes(); + foreach_dlist(vol, vol_list) { + DEVICE *dev = vol->dev; + if (dev) { + len = Mmsg(msg, "%s on device %s\n", vol->vol_name, dev->print_name()); + sendit(msg.c_str(), len, arg); + len = Mmsg(msg, " Reader=%d writers=%d devres=%d volinuse=%d\n", + dev->can_read()?1:0, dev->num_writers, dev->num_reserved(), + vol->is_in_use()); + sendit(msg.c_str(), len, arg); + } else { + len = Mmsg(msg, "%s no device. volinuse= %d\n", vol->vol_name, + vol->is_in_use()); + sendit(msg.c_str(), len, arg); + } + } + unlock_volumes(); +} + +/* + * Create a Volume item to put in the Volume list + * Ensure that the device points to it. + */ +static VOLRES *new_vol_item(DCR *dcr, const char *VolumeName) +{ + VOLRES *vol; + vol = (VOLRES *)malloc(sizeof(VOLRES)); + memset(vol, 0, sizeof(VOLRES)); + vol->vol_name = bstrdup(VolumeName); + vol->dev = dcr->dev; + Dmsg3(dbglvl, "new Vol=%s at %p dev=%s\n", + VolumeName, vol->vol_name, vol->dev->print_name()); + return vol; +} + +static void free_vol_item(VOLRES *vol) +{ + DEVICE *dev = NULL; + + free(vol->vol_name); + if (vol->dev) { + dev = vol->dev; + } + free(vol); + if (dev) { + dev->vol = NULL; + } +} + +/* + * Put a new Volume entry in the Volume list. This + * effectively reserves the volume so that it will + * not be mounted again. + * + * If the device has any current volume associated with it, + * and it is a different Volume, and the device is not busy, + * we release the old Volume item and insert the new one. + * + * It is assumed that the device is free and locked so that + * we can change the device structure. + * + * Some details of the Volume list handling: + * + * 1. The Volume list entry must be attached to the drive (rather than + * attached to a job as it currently is. I.e. the drive that "owns" + * the volume (in use, mounted) + * must point to the volume (still to be maintained in a list). + * + * 2. The Volume is entered in the list when a drive is reserved. + * + * 3. When a drive is in use, the device code must appropriately update the + * volume name as it changes (currently the list is static -- an entry is + * removed when the Volume is no longer reserved, in use or mounted). + * The new code must keep the same list entry as long as the drive + * has any volume associated with it but the volume name in the list + * must be updated when the drive has a different volume mounted. + * + * 4. A job that has reserved a volume, can un-reserve the volume, and if the + * volume is not mounted, and not reserved, and not in use, it will be + * removed from the list. + * + * 5. If a job wants to reserve a drive with a different Volume from the one on + * the drive, it can re-use the drive for the new Volume. + * + * 6. If a job wants a Volume that is in a different drive, it can either use the + * other drive or take the volume, only if the other drive is not in use or + * not reserved. + * + * One nice aspect of this is that the reserve use count and the writer use count + * already exist and are correctly programmed and will need no changes -- use + * counts are always very tricky. + * + * The old code had a concept of "reserving" a Volume, but was changed + * to reserving and using a drive. A volume is must be attached to (owned by) a + * drive and can move from drive to drive or be unused given certain specific + * conditions of the drive. The key is that the drive must "own" the Volume. + * The old code had the job (dcr) owning the volume (more or less). The job was + * to change the insertion and removal of the volumes from the list to be based + * on the drive rather than the job. + * + * Return: VOLRES entry on success + * NULL volume busy on another drive + */ +VOLRES *reserve_volume(DCR *dcr, const char *VolumeName) +{ + VOLRES *vol, *nvol; + DEVICE * volatile dev = dcr->dev; + + ASSERT(dev != NULL); + + Dmsg2(dbglvl, "enter reserve_volume=%s drive=%s\n", VolumeName, + dcr->dev->print_name()); + /* + * We lock the reservations system here to ensure + * when adding a new volume that no newly scheduled + * job can reserve it. + */ + lock_volumes(); + debug_list_volumes("begin reserve_volume"); + /* + * First, remove any old volume attached to this device as it + * is no longer used. + */ + if (dev->vol) { + vol = dev->vol; + Dmsg4(dbglvl, "Vol attached=%s, newvol=%s volinuse=%d on %s\n", + vol->vol_name, VolumeName, vol->is_in_use(), dev->print_name()); + /* + * Make sure we don't remove the current volume we are inserting + * because it was probably inserted by another job, or it + * is not being used and is marked as not reserved. + */ + if (strcmp(vol->vol_name, VolumeName) == 0) { + Dmsg2(dbglvl, "=== set reserved vol=%s dev=%s\n", VolumeName, + vol->dev->print_name()); + goto get_out; /* Volume already on this device */ + } else { + /* Don't release a volume if it was reserved by someone other than us */ + if (vol->is_in_use() && !dcr->reserved_volume) { + Dmsg1(dbglvl, "Cannot free vol=%s. It is reserved.\n", vol->vol_name); + vol = NULL; /* vol in use */ + goto get_out; + } + Dmsg2(dbglvl, "reserve_vol free vol=%s at %p\n", vol->vol_name, vol->vol_name); + free_volume(dev); + Dmsg0(50, "set_unload\n"); + dev->set_unload(); /* have to unload current volume */ + debug_list_volumes("reserve_vol free"); + } + } + + /* Create a new Volume entry */ + nvol = new_vol_item(dcr, VolumeName); + + /* + * Now try to insert the new Volume + */ + vol = (VOLRES *)vol_list->binary_insert(nvol, my_compare); + if (vol != nvol) { + Dmsg2(dbglvl, "Found vol=%s dev-same=%d\n", vol->vol_name, dev==vol->dev); + /* + * At this point, a Volume with this name already is in the list, + * so we simply release our new Volume entry. Note, this should + * only happen if we are moving the volume from one drive to another. + */ + Dmsg2(dbglvl, "reserve_vol free-tmp vol=%s at %p\n", + vol->vol_name, vol->vol_name); + /* + * Clear dev pointer so that free_vol_item() doesn't + * take away our volume. + */ + nvol->dev = NULL; /* don't zap dev entry */ + free_vol_item(nvol); + + /* + * Check if we are trying to use the Volume on a different drive + * dev is our device + * vol->dev is where the Volume we want is + */ + if (dev != vol->dev) { + /* Caller wants to switch Volume to another device */ + if (!vol->dev->is_busy() && !vol->is_swapping()) { + int32_t slot; + Dmsg3(dbglvl, "==== Swap vol=%s from dev=%s to %s\n", + VolumeName, vol->dev->print_name(), dev->print_name()); + free_volume(dev); /* free any volume attached to our drive */ + Dmsg0(50, "set_unload\n"); + dev->set_unload(); /* Unload any volume that is on our drive */ + dcr->dev = vol->dev; /* temp point to other dev */ + slot = get_autochanger_loaded_slot(dcr); /* get slot on other drive */ + dcr->dev = dev; /* restore dev */ + vol->set_slot(slot); /* save slot */ + vol->dev->set_unload(); /* unload the other drive */ + vol->set_swapping(); /* swap from other drive */ + dev->swap_dev = vol->dev; /* remember to get this vol */ + dev->set_load(); /* then reload on our drive */ + vol->dev->vol = NULL; /* remove volume from other drive */ + vol->dev = dev; /* point the Volume at our drive */ + dev->vol = vol; /* point our drive at the Volume */ + } else { + Dmsg3(dbglvl, "==== Swap not possible Vol busy vol=%s from dev=%s to %s\n", + VolumeName, vol->dev->print_name(), dev->print_name()); + vol = NULL; /* device busy */ + goto get_out; + } + } else { + dev->vol = vol; + } + } else { + dev->vol = vol; /* point to newly inserted volume */ + } + +get_out: + if (vol) { + Dmsg2(dbglvl, "=== set in_use. vol=%s dev=%s\n", vol->vol_name, + vol->dev->print_name()); + vol->set_in_use(); + dcr->reserved_volume = true; + bstrncpy(dcr->VolumeName, vol->vol_name, sizeof(dcr->VolumeName)); + } + debug_list_volumes("end new volume"); + unlock_volumes(); + return vol; +} + +/* + * Switch from current device to given device + * (not yet used) + */ +#ifdef xxx +void switch_device(DCR *dcr, DEVICE *dev) +{ + DCR save_dcr; + + dev->dlock(); + memcpy(&save_dcr, dcr, sizeof(save_dcr)); + clean_device(dcr); /* clean up the dcr */ + + dcr->dev = dev; /* get new device pointer */ + Jmsg(dcr->jcr, M_INFO, 0, _("Device switch. New device %s chosen.\n"), + dcr->dev->print_name()); + + bstrncpy(dcr->VolumeName, save_dcr.VolumeName, sizeof(dcr->VolumeName)); + bstrncpy(dcr->media_type, save_dcr.media_type, sizeof(dcr->media_type)); + dcr->VolCatInfo.Slot = save_dcr.VolCatInfo.Slot; + bstrncpy(dcr->pool_name, save_dcr.pool_name, sizeof(dcr->pool_name)); + bstrncpy(dcr->pool_type, save_dcr.pool_type, sizeof(dcr->pool_type)); + bstrncpy(dcr->dev_name, dev->dev_name, sizeof(dcr->dev_name)); + +// dcr->set_reserved(); + + dev->dunlock(); +} +#endif + +/* + * Search for a Volume name in the Volume list. + * + * Returns: VOLRES entry on success + * NULL if the Volume is not in the list + */ +VOLRES *find_volume(const char *VolumeName) +{ + VOLRES vol, *fvol; + /* Do not lock reservations here */ + lock_volumes(); + vol.vol_name = bstrdup(VolumeName); + fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare); + free(vol.vol_name); + Dmsg2(dbglvl, "find_vol=%s found=%d\n", VolumeName, fvol!=NULL); + debug_list_volumes("find_volume"); + unlock_volumes(); + return fvol; +} + +/* + * Free a Volume from the Volume list if it is no longer used + * Note, for tape drives we want to remember where the Volume + * was when last used, so rather than free the volume entry, + * we simply mark it "not reserved" so when the drive is really + * needed for another volume, we can reuse it. + * + * Returns: true if the Volume found and "removed" from the list + * false if the Volume is not in the list or is in use + */ +bool volume_unused(DCR *dcr) +{ + DEVICE *dev = dcr->dev; + + if (!dev->vol) { + Dmsg1(dbglvl, "vol_unused: no vol on %s\n", dev->print_name()); + debug_list_volumes("null vol cannot unreserve_volume"); + return false; + } + if (dev->vol->is_swapping()) { + Dmsg1(dbglvl, "vol_unused: vol being swapped on %s\n", dev->print_name()); + Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name); + dev->vol->clear_in_use(); + debug_list_volumes("swapping vol cannot free_volume"); + return false; + } + + /* + * If this is a tape, we do not free the volume, rather we wait + * until the autoloader unloads it, or until another tape is + * explicitly read in this drive. This allows the SD to remember + * where the tapes are or last were. + */ + Dmsg4(dbglvl, "=== set not reserved vol=%s num_writers=%d dev_reserved=%d dev=%s\n", + dev->vol->vol_name, dev->num_writers, dev->num_reserved(), dev->print_name()); + Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name); + dev->vol->clear_in_use(); + if (dev->is_tape() || dev->is_autochanger()) { + return true; + } else { + /* + * Note, this frees the volume reservation entry, but the + * file descriptor remains open with the OS. + */ + return free_volume(dev); + } +} + +/* + * Unconditionally release the volume entry + */ +bool free_volume(DEVICE *dev) +{ + VOLRES *vol; + + if (dev->vol == NULL) { + Dmsg1(dbglvl, "No vol on dev %s\n", dev->print_name()); + return false; + } + lock_volumes(); + vol = dev->vol; + /* Don't free a volume while it is being swapped */ + if (!vol->is_swapping()) { + Dmsg1(dbglvl, "=== clear in_use vol=%s\n", dev->vol->vol_name); + dev->vol = NULL; + vol_list->remove(vol); + Dmsg2(dbglvl, "=== remove volume %s dev=%s\n", vol->vol_name, dev->print_name()); + free_vol_item(vol); + debug_list_volumes("free_volume"); + } + unlock_volumes(); + return true; +} + + +/* Create the Volume list */ +void create_volume_list() +{ + VOLRES *vol = NULL; + if (vol_list == NULL) { + vol_list = New(dlist(vol, &vol->link)); + } +} + +/* Release all Volumes from the list */ +void free_volume_list() +{ + VOLRES *vol; + if (!vol_list) { + return; + } + lock_volumes(); + foreach_dlist(vol, vol_list) { + if (vol->dev) { + Dmsg2(dbglvl, "free vol_list Volume=%s dev=%s\n", vol->vol_name, vol->dev->print_name()); + } else { + Dmsg1(dbglvl, "free vol_list Volume=%s No dev\n", vol->vol_name); + } + free(vol->vol_name); + vol->vol_name = NULL; + } + delete vol_list; + vol_list = NULL; + unlock_volumes(); +} + +bool DCR::can_i_use_volume() +{ + bool rtn = true; + VOLRES *vol; + + lock_volumes(); + vol = find_volume(VolumeName); + if (!vol) { + Dmsg1(dbglvl, "Vol=%s not in use.\n", VolumeName); + goto get_out; /* vol not in list */ + } + ASSERT(vol->dev != NULL); + + if (dev == vol->dev) { /* same device OK */ + Dmsg1(dbglvl, "Vol=%s on same dev.\n", VolumeName); + goto get_out; + } else { + Dmsg3(dbglvl, "Vol=%s on %s we have %s\n", VolumeName, + vol->dev->print_name(), dev->print_name()); + } + /* ***FIXME*** check this ... */ + if (!vol->dev->is_busy()) { + Dmsg2(dbglvl, "Vol=%s dev=%s not busy.\n", VolumeName, vol->dev->print_name()); + goto get_out; + } else { + Dmsg2(dbglvl, "Vol=%s dev=%s busy.\n", VolumeName, vol->dev->print_name()); + } + Dmsg2(dbglvl, "Vol=%s in use by %s.\n", VolumeName, vol->dev->print_name()); + rtn = false; + +get_out: + unlock_volumes(); + return rtn; + +} diff --git a/bacula/technotes-2.5 b/bacula/technotes-2.5 index fc59596de4..157c241f17 100644 --- a/bacula/technotes-2.5 +++ b/bacula/technotes-2.5 @@ -50,6 +50,12 @@ libtool on the configure command line with: General: +21Oct08 +kes Split volume management code out of src/stored/reserve.c into + a new file vol_mgr.c +kes Modify configure to do an automatic make clean. This ensures + that any changes to ./configure options are handled correctly. + Beta version 2.5.16 release: 20Oct08 ebl Rename JobStat table to JobHistory -- 2.39.5