From b6a45ddaadf108e3aa7743958ed92389ca79fc4a Mon Sep 17 00:00:00 2001 From: Kern Sibbald Date: Thu, 18 Aug 2005 12:15:10 +0000 Subject: [PATCH] - Implement unloading a volume in a different drive if it is needed in the current drive. - Implement search for unused autochanger drive. git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@2327 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/kes-1.37 | 3 ++ bacula/src/stored/autochanger.c | 91 ++++++++++++++++++++++++++++++++- bacula/src/stored/reserve.c | 72 ++++++++++++++++++-------- 3 files changed, 143 insertions(+), 23 deletions(-) diff --git a/bacula/kes-1.37 b/bacula/kes-1.37 index b7534f6af3..8b8ea4dabf 100644 --- a/bacula/kes-1.37 +++ b/bacula/kes-1.37 @@ -5,6 +5,9 @@ General: Changes to 1.37.36: 18Aug05 +- Implement unloading a volume in a different drive if it + is needed in the current drive. +- Implement search for unused autochanger drive. - Implement search for exact Volume in reservation before other searches. - Fix picking up drive in Dir so that it is not done in diff --git a/bacula/src/stored/autochanger.c b/bacula/src/stored/autochanger.c index 97aee96e64..cf78a72c18 100644 --- a/bacula/src/stored/autochanger.c +++ b/bacula/src/stored/autochanger.c @@ -27,6 +27,7 @@ /* Forward referenced functions */ static void lock_changer(DCR *dcr); static void unlock_changer(DCR *dcr); +static bool unload_other_drive(DCR *dcr, int slot); /* * Called here to do an autoload using the autochanger, if @@ -75,11 +76,17 @@ int autoload_device(DCR *dcr, int writing, BSOCK *dir) loaded = get_autochanger_loaded_slot(dcr); - /* If tape we want is not loaded, load it. */ if (loaded != slot) { + /* Unload anything in our drive */ if (!unload_autochanger(dcr, loaded)) { goto bail_out; } + + /* Make sure desired slot is unloaded */ + if (!unload_other_drive(dcr, slot)) { + goto bail_out; + } + /* * Load the desired cassette */ @@ -248,6 +255,88 @@ bool unload_autochanger(DCR *dcr, int loaded) return ok; } +/* + * Unload the slot if mounted in a different drive + */ +static bool unload_other_drive(DCR *dcr, int slot) +{ + DEVICE *dev, *save_dev; + JCR *jcr = dcr->jcr; + int save_slot; + uint32_t timeout = dcr->device->max_changer_wait; + bool ok = true; + AUTOCHANGER *changer = dcr->dev->device->changer_res; + DEVRES *device; + bool found = false; + + if (!changer) { + return false; + } + if (changer->device->size() == 1) { + return true; + } + + foreach_alist(device, changer->device) { + if (device->dev && device->dev->Slot == slot) { + found = true; + dev = device->dev; + break; + } + } + if (!found) { + return true; + } + if (dev->is_busy()) { + Jmsg(jcr, M_WARNING, 0, _("Volume %s is in use by device %s\n"), + dcr->VolumeName, dev->print_name()); + Dmsg2(200, "Volume %s is in use by device %s\n", + dcr->VolumeName, dev->print_name()); + + return false; + } + + offline_or_rewind_dev(dev); + /* We are going to load a new tape, so close the device */ + force_close_device(dev); + + POOLMEM *changer_cmd = get_pool_memory(PM_FNAME); + Jmsg(jcr, M_INFO, 0, + _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"), + slot, dev->drive_index); + + Dmsg2(200, "Issuing autochanger \"unload slot %d, drive %d\" command.\n", + slot, dev->drive_index); + + save_slot = dcr->VolCatInfo.Slot; + save_dev = dcr->dev; + dcr->dev = dev; + dcr->VolCatInfo.Slot = slot; + changer_cmd = edit_device_codes(dcr, changer_cmd, + dcr->device->changer_command, "unload"); + lock_changer(dcr); + Dmsg1(200, "Run program=%s\n", changer_cmd); + int stat = run_program(changer_cmd, timeout, NULL); + unlock_changer(dcr); + dcr->VolCatInfo.Slot = save_slot; + dcr->dev = save_dev; + if (stat != 0) { + berrno be; + be.set_errno(stat); + Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"), + slot, dev->drive_index, be.strerror()); + + Dmsg3(200, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n", + slot, dev->drive_index, be.strerror()); + ok = false; + } else { + dev->Slot = 0; /* nothing loaded */ + Dmsg0(200, "Slot unloaded\n"); + } + free_pool_memory(changer_cmd); + return ok; +} + + /* * List the Volumes that are in the autoloader possibly diff --git a/bacula/src/stored/reserve.c b/bacula/src/stored/reserve.c index 50576144eb..d11a64c972 100644 --- a/bacula/src/stored/reserve.c +++ b/bacula/src/stored/reserve.c @@ -56,6 +56,8 @@ public: bool PreferMountedVols; bool exact_match; bool have_volume; + bool do_not_wait; + bool available_autochanger; char VolumeName[MAX_NAME_LENGTH]; }; @@ -327,25 +329,41 @@ static bool use_storage_cmd(JCR *jcr) */ if (ok) { /* - * Make up to two passes. The first with PreferMountedVols possibly - * set to true. In that case, we look only for an available - * drive with something mounted. If that fails, then we - * do a second pass with PerferMountedVols set false. + * First look for an exact match of Volume name as the + * tape may already be mounted. */ + rctx.do_not_wait = true; rctx.exact_match = true; if ((ok = find_suitable_device_for_job(jcr, rctx))) { goto done; } rctx.exact_match = false; + + /* Now search if an unused autochanger slot is available */ + rctx.available_autochanger = true; + if ((ok = find_suitable_device_for_job(jcr, rctx))) { + goto done; + } + rctx.available_autochanger = false; + + + /* + * Make up to two passes. The first with PreferMountedVols possibly + * set to true. In that case, we look only for an available + * drive with something mounted. If that fails, then we + * do a second pass with PerferMountedVols set false. + */ rctx.PreferMountedVols = jcr->PreferMountedVols; - ok = find_suitable_device_for_job(jcr, rctx); - if (ok) { + if (!rctx.PreferMountedVols) { + rctx.do_not_wait = false; + } + if ((ok = find_suitable_device_for_job(jcr, rctx))) { goto done; } if (rctx.PreferMountedVols) { rctx.PreferMountedVols = false; - ok = find_suitable_device_for_job(jcr, rctx); - if (ok) { + rctx.do_not_wait = false; + if ((ok = find_suitable_device_for_job(jcr, rctx))) { goto done; } } @@ -432,7 +450,7 @@ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx) * if there is some device for which we can wait, then * wait and try again until the wait time expires */ - if (!can_wait || !wait_for_device(jcr, first)) { + if (rctx.do_not_wait || !can_wait || !wait_for_device(jcr, first)) { break; } first = false; /* first wait complete */ @@ -447,7 +465,6 @@ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx) if (dcr) { free_dcr(dcr); } - return ok; } @@ -463,19 +480,21 @@ static int search_res_for_device(RCTX &rctx) int stat; Dmsg1(100, "Search res for %s\n", rctx.device_name); - foreach_res(rctx.device, R_DEVICE) { - Dmsg1(100, "Try res=%s\n", rctx.device->hdr.name); - /* Find resource, and make sure we were able to open it */ - if (fnmatch(rctx.device_name, rctx.device->hdr.name, 0) == 0) { - stat = reserve_device(rctx); - if (stat != 1) { - return stat; + if (!rctx.available_autochanger) { + foreach_res(rctx.device, R_DEVICE) { + Dmsg1(100, "Try res=%s\n", rctx.device->hdr.name); + /* Find resource, and make sure we were able to open it */ + if (fnmatch(rctx.device_name, rctx.device->hdr.name, 0) == 0) { + stat = reserve_device(rctx); + if (stat != 1) { + return stat; + } + Dmsg1(220, "Got: %s", dir->msg); + bash_spaces(rctx.device_name); + ok = bnet_fsend(dir, OK_device, rctx.device_name); + Dmsg1(100, ">dird dev: %s", dir->msg); + return ok ? 1 : -1; } - Dmsg1(220, "Got: %s", dir->msg); - bash_spaces(rctx.device_name); - ok = bnet_fsend(dir, OK_device, rctx.device_name); - Dmsg1(100, ">dird dev: %s", dir->msg); - return ok ? 1 : -1; } } foreach_res(changer, R_AUTOCHANGER) { @@ -695,6 +714,15 @@ static int can_reserve_drive(DCR *dcr, RCTX &rctx) return 0; } + /* Check for unused autochanger drive */ + if (rctx.available_autochanger && dev->num_writers == 0 && + dev->VolHdr.VolumeName[0] == 0) { + /* Device is available but not yet reserved, reserve it for us */ + bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name)); + bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type)); + return 1; /* reserve drive */ + } + /* * Handle the case that there are no writers */ -- 2.39.5