X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fstored%2Freserve.c;h=4e9754ad3a78405c0b998990b11739eabcbb872e;hb=5c78438f2d9ea83837f262f9234c6c74cadd44d4;hp=06d55b348c5d7e896f38bc319bcaf27b3242c2f3;hpb=a83fa5ed6bc4868d267aedfc93141645ff7896eb;p=bacula%2Fbacula diff --git a/bacula/src/stored/reserve.c b/bacula/src/stored/reserve.c index 06d55b348c..4e9754ad3a 100644 --- a/bacula/src/stored/reserve.c +++ b/bacula/src/stored/reserve.c @@ -1,12 +1,12 @@ /* Bacula® - The Network Backup Solution - Copyright (C) 2000-2008 Free Software Foundation Europe e.V. + Copyright (C) 2000-2012 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 + modify it under the terms of version three of the GNU Affero General Public License as published by the Free Software Foundation and included in the file LICENSE. @@ -15,12 +15,12 @@ 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 + You should have received a copy of the GNU Affero 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 John Walker. + 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. @@ -32,28 +32,24 @@ * * Split from job.c and acquire.c June 2005 * - * Version $Id$ - * */ #include "bacula.h" #include "stored.h" -const int dbglvl = 50; +const int dbglvl = 150; -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); static int reserve_device(RCTX &rctx); static bool reserve_device_for_read(DCR *dcr); static bool reserve_device_for_append(DCR *dcr, RCTX &rctx); -static bool use_storage_cmd(JCR *jcr); +static bool use_device_cmd(JCR *jcr); static void queue_reserve_message(JCR *jcr); static void pop_reserve_messages(JCR *jcr); -void switch_device(DCR *dcr, DEVICE *dev); +//void switch_device(DCR *dcr, DEVICE *dev); /* Requests from the Director daemon */ static char use_storage[] = "use storage=%127s media_type=%127s " @@ -62,7 +58,8 @@ static char use_device[] = "use device=%127s\n"; /* Responses sent to Director daemon */ static char OK_device[] = "3000 OK use device device=%s\n"; -static char NO_device[] = "3924 Device \"%s\" not in SD Device resources.\n"; +static char NO_device[] = "3924 Device \"%s\" not in SD Device" + " resources or no matching Media Type.\n"; static char BAD_use[] = "3913 Bad use command: %s\n"; bool use_cmd(JCR *jcr) @@ -70,19 +67,14 @@ bool use_cmd(JCR *jcr) /* * Get the device, media, and pool information */ - if (!use_storage_cmd(jcr)) { - set_jcr_job_status(jcr, JS_ErrorTerminated); + if (!use_device_cmd(jcr)) { + jcr->setJobStatus(JS_ErrorTerminated); memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key)); return false; } 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,27 +88,23 @@ 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; /* This applies to a drive and to Volumes */ -void _lock_reservations() +void _lock_reservations(const char *file, int line) { int errstat; reservations_lock_count++; - if ((errstat=rwl_writelock(&reservation_lock)) != 0) { + if ((errstat=rwl_writelock_p(&reservation_lock, file, line)) != 0) { berrno be; Emsg2(M_ABORT, 0, "rwl_writelock failure. stat=%d: ERR=%s\n", errstat, be.bstrerror(errstat)); @@ -134,495 +122,50 @@ 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 -}; - -static 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 rel=%d on device %s\n", imsg, - vol->vol_name, vol->released, vol->dev->print_name()); - } else { - Mmsg(msg, "List %s: %s rel=%d no dev\n", imsg, vol->vol_name, vol->released); - } - Dmsg1(dbglvl, "%s", msg.c_str()); - } - -#ifdef xxx - DEVICE *dev = NULL; - foreach_dlist(vol, vol_list) { - if (vol->dev == dev) { - Dmsg0(dbglvl, "Two Volumes on same device.\n"); - ASSERT(0); - dev = vol->dev; - } - } -#endif - -// Dmsg2(dbglvl, "List from %s: %d volumes\n", imsg, count); - 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 reserved=%d released=%d\n", - dev->can_read()?1:0, dev->num_writers, dev->reserved_device, vol->released); - sendit(msg.c_str(), len, arg); - } else { - len = Mmsg(msg, "%s no device. released=%d\n", vol->vol_name, vol->released); - 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) +void DCR::set_reserved() { - 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; + m_reserved = true; + Dmsg2(dbglvl, "Inc reserve=%d dev=%s\n", dev->num_reserved(), dev->print_name()); + dev->inc_reserved(); } -static void free_vol_item(VOLRES *vol) -{ - free(vol->vol_name); - if (vol->dev) { - vol->dev->vol = NULL; - } - free(vol); -} - - -/* - * 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 (reserved, 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) +void DCR::clear_reserved() { - VOLRES *vol, *nvol; - DEVICE *dev = dcr->dev; - - ASSERT(dev != NULL); - - Dmsg1(dbglvl, "enter reserve_volume %s\n", VolumeName); - /* - * 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 release=%d on %s\n", - vol->vol_name, VolumeName, vol->released, 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 released. - */ - if (strcmp(vol->vol_name, VolumeName) == 0) { - Dmsg1(dbglvl, "=== OK, vol=%s on device. set not released.\n", VolumeName); - vol->released = false; /* retake vol if released previously */ - goto get_out; /* Volume already on this device */ - } else { - /* Don't release a volume if it is in use */ - if (!vol->released) { - Dmsg1(dbglvl, "Cannot free vol=%s. It is not released.\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); - unload_autochanger(dcr, -1); /* unload the volume */ - free_volume(dev); - 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 */ - if (dev != vol->dev) { - /* Caller wants to switch Volume to another device */ - switch_device(dcr, vol->dev); - dev = vol->dev; -#ifdef xxx - if (!vol->dev->is_busy()) { - /* OK to move it -- I'm not sure this will work */ - Dmsg3(dbglvl, "==== Swap vol=%s from dev=%s to %s\n", VolumeName, - vol->dev->print_name(), dev->print_name()); - vol->dev->vol = NULL; /* take vol from old drive */ - vol->dev->VolHdr.VolumeName[0] = 0; - vol->dev = dev; /* point vol at new drive */ - dev->vol = vol; /* point dev at vol */ - dev->VolHdr.VolumeName[0] = 0; - } 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; - } -#endif - } - } - dev->vol = vol; - -get_out: - if (vol) { - Dmsg1(dbglvl, "=== set not released. vol=%s\n", vol->vol_name); - vol->released = false; + if (m_reserved) { + m_reserved = false; + dev->dec_reserved(); + Dmsg2(dbglvl, "Dec reserve=%d dev=%s\n", dev->num_reserved(), dev->print_name()); } - debug_list_volumes("end new volume"); - unlock_volumes(); - return vol; -} - -/* - * Switch from current device to given device - * (not yet used) - */ -void switch_device(DCR *dcr, DEVICE *dev) -{ - // lock_reservations(); - 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, save_dcr.dev_name, sizeof(dcr->dev_name)); - - dev->reserved_device++; - dcr->reserved_device = true; - - dev->dunlock(); -} - -/* - * 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(DCR *dcr) -{ - VOLRES vol, *fvol; - /* Do not lock reservations here */ - lock_volumes(); - vol.vol_name = bstrdup(dcr->VolumeName); - fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare); - free(vol.vol_name); - Dmsg2(dbglvl, "find_vol=%s found=%d\n", dcr->VolumeName, fvol!=NULL); - debug_list_volumes("find_volume"); - unlock_volumes(); - return fvol; } /* * Remove any reservation from a drive and tell the system * that the volume is unused at least by us. */ -void unreserve_device(DCR *dcr) +void DCR::unreserve_device() { - DEVICE *dev = dcr->dev; - if (dcr->reserved_device) { - dcr->reserved_device = false; - dev->reserved_device--; - Dmsg2(dbglvl, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name()); - dcr->reserved_device = false; + dev->Lock(); + lock_volumes(); + if (is_reserved()) { + clear_reserved(); + reserved_volume = false; /* If we set read mode in reserving, remove it */ if (dev->can_read()) { dev->clear_read(); } if (dev->num_writers < 0) { - Jmsg1(dcr->jcr, M_ERROR, 0, _("Hey! num_writers=%d!!!!\n"), dev->num_writers); + Jmsg1(jcr, M_ERROR, 0, _("Hey! num_writers=%d!!!!\n"), dev->num_writers); dev->num_writers = 0; } - if (dev->reserved_device == 0 && dev->num_writers == 0) { - volume_unused(dcr); + if (dev->num_reserved() == 0 && dev->num_writers == 0) { + generate_plugin_event(jcr, bsdEventDeviceClose, this); + volume_unused(this); } } -} - -/* - * 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 "released" 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 == NULL) { - Dmsg1(dbglvl, "vol_unused: no vol on %s\n", dev->print_name()); - debug_list_volumes("null vol cannot unreserve_volume"); - return false; - } - -#ifdef xxx - if (dev->is_busy()) { - Dmsg1(dbglvl, "vol_unused: busy on %s\n", dev->print_name()); - debug_list_volumes("dev busy cannot unreserve_volume"); - return false; - } -#endif -#ifdef xxx - if (dev->num_writers > 0 || dev->reserved_device > 0) { - ASSERT(0); - } -#endif - - /* - * 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. - */ - Dmsg3(dbglvl, "=== mark released vol=%s num_writers=%d reserved=%d\n", - dev->vol->vol_name, dev->num_writers, dev->reserved_device); - dev->vol->released = true; - 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; - dev->vol = NULL; - vol_list->remove(vol); - Dmsg2(dbglvl, "=== free_volume %s dev=%s\n", vol->vol_name, dev->print_name()); - free_vol_item(vol); - debug_list_volumes("free_volume"); unlock_volumes(); - return vol != NULL; -} - - -/* Create the Volume list */ -void create_volume_list() -{ - VOLRES *vol = NULL; - if (vol_list == NULL) { - vol_list = New(dlist(vol, &vol->link)); - } + dev->Unlock(); } -/* 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 is_volume_in_use(DCR *dcr) -{ - VOLRES *vol = find_volume(dcr); - if (!vol) { - Dmsg1(dbglvl, "Vol=%s not in use.\n", dcr->VolumeName); - return false; /* vol not in list */ - } - ASSERT(vol->dev != NULL); - - if (dcr->dev == vol->dev) { /* same device OK */ - Dmsg1(dbglvl, "Vol=%s on same dev.\n", dcr->VolumeName); - return false; - } else { - Dmsg3(dbglvl, "Vol=%s on %s we have %s\n", dcr->VolumeName, - vol->dev->print_name(), dcr->dev->print_name()); - } - if (!vol->dev->is_busy()) { - Dmsg2(dbglvl, "Vol=%s dev=%s not busy.\n", dcr->VolumeName, vol->dev->print_name()); - return false; - } else { - Dmsg2(dbglvl, "Vol=%s dev=%s busy.\n", dcr->VolumeName, vol->dev->print_name()); - } - Dmsg2(dbglvl, "Vol=%s in use by %s.\n", dcr->VolumeName, vol->dev->print_name()); - return true; -} - - /* * We get the following type of information: * @@ -634,13 +177,13 @@ bool is_volume_in_use(DCR *dcr) * use device=bbb * */ -static bool use_storage_cmd(JCR *jcr) +static bool use_device_cmd(JCR *jcr) { POOL_MEM store_name, dev_name, media_type, pool_name, pool_type; BSOCK *dir = jcr->dir_bsock; - int append; + int32_t append; bool ok; - int Copy, Stripe; + int32_t Copy, Stripe; DIRSTORE *store; RCTX rctx; alist *dirstore; @@ -693,6 +236,7 @@ static bool use_storage_cmd(JCR *jcr) } } while (ok && dir->recv() >= 0); +#ifdef xxxx /* Developer debug code */ char *device_name; if (debug_level >= dbglvl) { @@ -705,6 +249,7 @@ static bool use_storage_cmd(JCR *jcr) } } } +#endif init_jcr_device_wait_timers(jcr); jcr->dcr = new_dcr(jcr, NULL, NULL); /* get a dcr */ @@ -729,6 +274,12 @@ static bool use_storage_cmd(JCR *jcr) bool fail = false; rctx.notify_dir = true; + /* Put new dcr in proper location */ + if (rctx.append) { + rctx.jcr->dcr = jcr->dcr; + } else { + rctx.jcr->read_dcr = jcr->dcr; + } lock_reservations(); for ( ; !fail && !job_canceled(jcr); ) { pop_reserve_messages(jcr); @@ -747,9 +298,6 @@ static bool use_storage_cmd(JCR *jcr) rctx.PreferMountedVols = false; rctx.exact_match = false; rctx.autochanger_only = true; - Dmsg5(dbglvl, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n", - rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device, - rctx.autochanger_only, rctx.any_drive); if ((ok = find_suitable_device_for_job(jcr, rctx))) { break; } @@ -762,9 +310,6 @@ static bool use_storage_cmd(JCR *jcr) rctx.try_low_use_drive = false; } rctx.autochanger_only = false; - Dmsg5(dbglvl, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n", - rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device, - rctx.autochanger_only, rctx.any_drive); if ((ok = find_suitable_device_for_job(jcr, rctx))) { break; } @@ -777,25 +322,16 @@ static bool use_storage_cmd(JCR *jcr) rctx.PreferMountedVols = true; rctx.exact_match = true; rctx.autochanger_only = false; - Dmsg5(dbglvl, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n", - rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device, - rctx.autochanger_only, rctx.any_drive); if ((ok = find_suitable_device_for_job(jcr, rctx))) { break; } /* Look for any mounted drive */ rctx.exact_match = false; - Dmsg5(dbglvl, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n", - rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device, - rctx.autochanger_only, rctx.any_drive); if ((ok = find_suitable_device_for_job(jcr, rctx))) { break; } /* Try any drive */ rctx.any_drive = true; - Dmsg5(dbglvl, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n", - rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device, - rctx.autochanger_only, rctx.any_drive); if ((ok = find_suitable_device_for_job(jcr, rctx))) { break; } @@ -827,10 +363,8 @@ static bool use_storage_cmd(JCR *jcr) */ unbash_spaces(dir->msg); pm_strcpy(jcr->errmsg, dir->msg); - Jmsg(jcr, M_INFO, 0, _("Failed command: %s\n"), jcr->errmsg); - Jmsg(jcr, M_FATAL, 0, _("\n" - " Device \"%s\" with MediaType \"%s\" requested by DIR not found in SD Device resources.\n"), - dev_name.c_str(), media_type.c_str()); + Jmsg(jcr, M_FATAL, 0, _("Device reservation failed for JobId=%d: %s\n"), + jcr->JobId, jcr->errmsg); dir->fsend(NO_device, dev_name.c_str()); Dmsg1(dbglvl, ">dird: %s", dir->msg); @@ -870,6 +404,10 @@ static bool is_vol_in_autochanger(RCTX &rctx, VOLRES *vol) /* * Search for a device suitable for this job. + * Note, this routine sets sets rctx.suitable_device if any + * device exists within the SD. The device may not be actually + * useable. + * It also returns if it finds a useable device. */ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx) { @@ -884,9 +422,9 @@ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx) } else { dirstore = jcr->read_store; } - Dmsg4(dbglvl, "PrefMnt=%d exact=%d suitable=%d chgronly=%d\n", - rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device, - rctx.autochanger_only); + Dmsg5(dbglvl, "Start find_suit_dev PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n", + rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device, + rctx.autochanger_only, rctx.any_drive); /* * If the appropriate conditions of this if are met, namely that @@ -894,39 +432,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"); @@ -952,7 +461,7 @@ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx) if (vol->dev->is_autochanger()) { Dmsg1(dbglvl, "vol=%s is in changer\n", vol->vol_name); - if (!is_vol_in_autochanger(rctx, vol)) { + if (!is_vol_in_autochanger(rctx, vol) || !vol->dev->autoselect) { continue; } } else if (strcmp(device_name, vol->dev->device->hdr.name) != 0) { @@ -989,18 +498,10 @@ 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\n"); + free_temp_vol_list(temp_vol_list); } if (ok) { - Dmsg1(dbglvl, "got vol %s from in-use vols list\n", rctx.VolumeName); + Dmsg1(dbglvl, "OK dev found. Vol=%s from in-use vols list\n", rctx.VolumeName); return true; } @@ -1021,16 +522,21 @@ bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx) ok = true; break; } else if (stat == 0) { /* device busy */ - Dmsg1(dbglvl, "Suitable device=%s, busy: not use\n", device_name); + Dmsg1(dbglvl, "No usable device=%s, busy: not use\n", device_name); } else { /* otherwise error */ - Dmsg0(dbglvl, "No suitable device found.\n"); + Dmsg0(dbglvl, "No usable device found.\n"); } } if (ok) { break; } } + if (ok) { + Dmsg1(dbglvl, "OK dev found. Vol=%s\n", rctx.VolumeName); + } else { + Dmsg0(dbglvl, "Leave find_suit_dev: no dev found.\n"); + } return ok; } @@ -1052,6 +558,11 @@ int search_res_for_device(RCTX &rctx) /* Try each device in this AutoChanger */ foreach_alist(rctx.device, changer->device) { Dmsg1(dbglvl, "Try changer device %s\n", rctx.device->hdr.name); + if (!rctx.device->autoselect) { + Dmsg1(100, "Device %s not autoselect skipped.\n", + rctx.device->hdr.name); + continue; /* device is not available */ + } stat = reserve_device(rctx); if (stat != 1) { /* try another device */ continue; @@ -1059,10 +570,10 @@ int search_res_for_device(RCTX &rctx) /* Debug code */ if (rctx.store->append == SD_APPEND) { Dmsg2(dbglvl, "Device %s reserved=%d for append.\n", - rctx.device->hdr.name, rctx.jcr->dcr->dev->reserved_device); + rctx.device->hdr.name, rctx.jcr->dcr->dev->num_reserved()); } else { Dmsg2(dbglvl, "Device %s reserved=%d for read.\n", - rctx.device->hdr.name, rctx.jcr->read_dcr->dev->reserved_device); + rctx.device->hdr.name, rctx.jcr->read_dcr->dev->num_reserved()); } return stat; } @@ -1082,10 +593,10 @@ int search_res_for_device(RCTX &rctx) /* Debug code */ if (rctx.store->append == SD_APPEND) { Dmsg2(dbglvl, "Device %s reserved=%d for append.\n", - rctx.device->hdr.name, rctx.jcr->dcr->dev->reserved_device); + rctx.device->hdr.name, rctx.jcr->dcr->dev->num_reserved()); } else { Dmsg2(dbglvl, "Device %s reserved=%d for read.\n", - rctx.device->hdr.name, rctx.jcr->read_dcr->dev->reserved_device); + rctx.device->hdr.name, rctx.jcr->read_dcr->dev->num_reserved()); } return stat; } @@ -1133,7 +644,11 @@ static int reserve_device(RCTX &rctx) rctx.suitable_device = true; Dmsg1(dbglvl, "try reserve %s\n", rctx.device->hdr.name); - rctx.jcr->dcr = dcr = new_dcr(rctx.jcr, rctx.jcr->dcr, rctx.device->dev); + if (rctx.store->append) { + dcr = new_dcr(rctx.jcr, rctx.jcr->dcr, rctx.device->dev); + } else { + dcr = new_dcr(rctx.jcr, rctx.jcr->read_dcr, rctx.device->dev); + } if (!dcr) { BSOCK *dir = rctx.jcr->dir_bsock; dir->fsend(_("3926 Could not get dcr for device: %s\n"), rctx.device_name); @@ -1153,11 +668,12 @@ static int reserve_device(RCTX &rctx) rctx.jcr->dcr = dcr; Dmsg5(dbglvl, "Reserved=%d dev_name=%s mediatype=%s pool=%s ok=%d\n", - dcr->dev->reserved_device, + dcr->dev->num_reserved(), dcr->dev_name, dcr->media_type, dcr->pool_name, ok); Dmsg3(dbglvl, "Vol=%s num_writers=%d, have_vol=%d\n", rctx.VolumeName, dcr->dev->num_writers, rctx.have_volume); if (rctx.have_volume) { + Dmsg0(dbglvl, "Call reserve_volume for append.\n"); if (reserve_volume(dcr, rctx.VolumeName)) { Dmsg1(dbglvl, "Reserved vol=%s\n", rctx.VolumeName); } else { @@ -1182,10 +698,10 @@ static int reserve_device(RCTX &rctx) * non-used drive and our one and only volume is mounted * elsewhere, so we bail out and retry using that drive. */ - if (dcr->volume_in_use && !rctx.PreferMountedVols) { + if (dcr->found_in_use() && !rctx.PreferMountedVols) { rctx.PreferMountedVols = true; if (dcr->VolumeName[0]) { - unreserve_device(dcr); + dcr->unreserve_device(); } goto bail_out; } @@ -1202,7 +718,7 @@ static int reserve_device(RCTX &rctx) */ if (dcr->dev->num_writers != 0) { if (dcr->VolumeName[0]) { - unreserve_device(dcr); + dcr->unreserve_device(); } goto bail_out; } @@ -1213,7 +729,7 @@ static int reserve_device(RCTX &rctx) if (ok) { rctx.jcr->read_dcr = dcr; Dmsg5(dbglvl, "Read reserved=%d dev_name=%s mediatype=%s pool=%s ok=%d\n", - dcr->dev->reserved_device, + dcr->dev->num_reserved(), dcr->dev_name, dcr->media_type, dcr->pool_name, ok); } } @@ -1253,10 +769,13 @@ static bool reserve_device_for_read(DCR *dcr) bool ok = false; ASSERT(dcr); + if (job_canceled(jcr)) { + return false; + } - dev->dlock(); + dev->Lock(); - if (is_device_unmounted(dev)) { + if (dev->is_device_unmounted()) { Dmsg1(dbglvl, "Device %s is BLOCKED due to user unmount.\n", dev->print_name()); Mmsg(jcr->errmsg, _("3601 JobId=%u device %s is BLOCKED due to user unmount.\n"), jcr->JobId, dev->print_name()); @@ -1267,29 +786,32 @@ static bool reserve_device_for_read(DCR *dcr) if (dev->is_busy()) { Dmsg4(dbglvl, "Device %s is busy ST_READ=%d num_writers=%d reserved=%d.\n", dev->print_name(), - dev->state & ST_READ?1:0, dev->num_writers, dev->reserved_device); + dev->state & ST_READ?1:0, dev->num_writers, dev->num_reserved()); Mmsg(jcr->errmsg, _("3602 JobId=%u device %s is busy (already reading/writing).\n"), jcr->JobId, dev->print_name()); queue_reserve_message(jcr); goto bail_out; } + /* Note: on failure this returns jcr->errmsg properly edited */ + if (generate_plugin_event(jcr, bsdEventDeviceTryOpen, dcr) != bRC_OK) { + queue_reserve_message(jcr); + goto bail_out; + } dev->clear_append(); dev->set_read(); + dcr->set_reserved(); ok = true; - dev->reserved_device++; - Dmsg3(dbglvl, "Inc reserve=%d dev=%s %p\n", dev->reserved_device, dev->print_name(), dev); - dcr->reserved_device = true; bail_out: - dev->dunlock(); + dev->Unlock(); return ok; } /* - * We reserve the device for appending by incrementing the - * reserved_device. We do virtually all the same work that + * We reserve the device for appending by incrementing + * num_reserved(). We do virtually all the same work that * is done in acquire_device_for_append(), but we do * not attempt to mount the device. This routine allows * the DIR to reserve multiple devices before *really* @@ -1309,23 +831,26 @@ static bool reserve_device_for_append(DCR *dcr, RCTX &rctx) bool ok = false; ASSERT(dcr); + if (job_canceled(jcr)) { + return false; + } - dev->dlock(); + dev->Lock(); /* If device is being read, we cannot write it */ if (dev->can_read()) { Mmsg(jcr->errmsg, _("3603 JobId=%u device %s is busy reading.\n"), jcr->JobId, dev->print_name()); - Dmsg1(dbglvl, "%s", jcr->errmsg); + Dmsg1(dbglvl, "Failed: %s", jcr->errmsg); queue_reserve_message(jcr); goto bail_out; } /* If device is unmounted, we are out of luck */ - if (is_device_unmounted(dev)) { + if (dev->is_device_unmounted()) { Mmsg(jcr->errmsg, _("3604 JobId=%u device %s is BLOCKED due to user unmount.\n"), jcr->JobId, dev->print_name()); - Dmsg1(dbglvl, "%s", jcr->errmsg); + Dmsg1(dbglvl, "Failed: %s", jcr->errmsg); queue_reserve_message(jcr); goto bail_out; } @@ -1338,14 +863,16 @@ static bool reserve_device_for_append(DCR *dcr, RCTX &rctx) goto bail_out; } - dev->reserved_device++; - Dmsg3(dbglvl, "Inc reserve=%d dev=%s %p\n", dev->reserved_device, - dev->print_name(), dev); - dcr->reserved_device = true; + /* Note: on failure this returns jcr->errmsg properly edited */ + if (generate_plugin_event(jcr, bsdEventDeviceTryOpen, dcr) != bRC_OK) { + queue_reserve_message(jcr); + goto bail_out; + } + dcr->set_reserved(); ok = true; bail_out: - dev->dunlock(); + dev->Unlock(); return ok; } @@ -1365,10 +892,9 @@ static int is_pool_ok(DCR *dcr) Mmsg(jcr->errmsg, _( "3608 JobId=%u wants Pool=\"%s\" but have Pool=\"%s\" nreserve=%d on drive %s.\n"), (uint32_t)jcr->JobId, dcr->pool_name, dev->pool_name, - dev->reserved_device, dev->print_name()); + dev->num_reserved(), dev->print_name()); + Dmsg1(dbglvl, "Failed: %s", jcr->errmsg); queue_reserve_message(jcr); - Dmsg2(dbglvl, "failed: busy num_writers=0, reserved, pool=%s wanted=%s\n", - dev->pool_name, dcr->pool_name); } return 0; } @@ -1378,17 +904,31 @@ static bool is_max_jobs_ok(DCR *dcr) DEVICE *dev = dcr->dev; JCR *jcr = dcr->jcr; - Dmsg4(dbglvl, "MaxJobs=%d Jobs=%d reserves=%d Vol=%s\n", + Dmsg5(dbglvl, "MaxJobs=%d Jobs=%d reserves=%d Status=%s Vol=%s\n", dcr->VolCatInfo.VolCatMaxJobs, - dcr->VolCatInfo.VolCatJobs, dev->reserved_device, + dcr->VolCatInfo.VolCatJobs, dev->num_reserved(), + dcr->VolCatInfo.VolCatStatus, dcr->VolumeName); + /* Limit max concurrent jobs on this drive */ + if (dev->max_concurrent_jobs > 0 && dev->max_concurrent_jobs <= + (uint32_t)(dev->num_writers + dev->num_reserved())) { + /* Max Concurrent Jobs depassed or already reserved */ + Mmsg(jcr->errmsg, _("3609 JobId=%u Max concurrent jobs exceeded on drive %s.\n"), + (uint32_t)jcr->JobId, dev->print_name()); + Dmsg1(dbglvl, "Failed: %s", jcr->errmsg); + queue_reserve_message(jcr); + return false; + } + if (strcmp(dcr->VolCatInfo.VolCatStatus, "Recycle") == 0) { + return true; + } if (dcr->VolCatInfo.VolCatMaxJobs > 0 && dcr->VolCatInfo.VolCatMaxJobs <= - (dcr->VolCatInfo.VolCatJobs + dev->reserved_device)) { + (dcr->VolCatInfo.VolCatJobs + dev->num_reserved())) { /* Max Job Vols depassed or already reserved */ Mmsg(jcr->errmsg, _("3610 JobId=%u Volume max jobs exceeded on drive %s.\n"), (uint32_t)jcr->JobId, dev->print_name()); - queue_reserve_message(jcr); Dmsg1(dbglvl, "reserve dev failed: %s", jcr->errmsg); + queue_reserve_message(jcr); return false; /* wait */ } return true; @@ -1429,17 +969,17 @@ static int can_reserve_drive(DCR *dcr, RCTX &rctx) /* If he wants a free drive, but this one is busy, no go */ if (!rctx.PreferMountedVols && dev->is_busy()) { /* Save least used drive */ - if ((dev->num_writers + dev->reserved_device) < rctx.num_writers) { - rctx.num_writers = dev->num_writers + dev->reserved_device; + if ((dev->num_writers + dev->num_reserved()) < rctx.num_writers) { + rctx.num_writers = dev->num_writers + dev->num_reserved(); rctx.low_use_drive = dev; Dmsg2(dbglvl, "set low use drive=%s num_writers=%d\n", dev->print_name(), rctx.num_writers); } else { - Dmsg1(dbglvl, "not low use num_writers=%d\n", dev->num_writers+dev->reserved_device); + Dmsg1(dbglvl, "not low use num_writers=%d\n", dev->num_writers+dev->num_reserved()); } - Dmsg0(dbglvl, "failed: !prefMnt && busy.\n"); Mmsg(jcr->errmsg, _("3605 JobId=%u wants free drive but device %s is busy.\n"), jcr->JobId, dev->print_name()); + Dmsg1(dbglvl, "Failed: %s", jcr->errmsg); queue_reserve_message(jcr); return 0; } @@ -1448,8 +988,8 @@ static int can_reserve_drive(DCR *dcr, RCTX &rctx) if (rctx.PreferMountedVols && !dev->vol && dev->is_tape()) { Mmsg(jcr->errmsg, _("3606 JobId=%u prefers mounted drives, but drive %s has no Volume.\n"), jcr->JobId, dev->print_name()); + Dmsg1(dbglvl, "Failed: %s", jcr->errmsg); queue_reserve_message(jcr); - Dmsg0(dbglvl, "failed: want mounted -- no vol\n"); return 0; /* No volume mounted */ } @@ -1476,7 +1016,7 @@ static int can_reserve_drive(DCR *dcr, RCTX &rctx) dev->VolHdr.VolumeName, dev->vol?dev->vol->vol_name:"*none*", rctx.VolumeName); return 0; } - if (is_volume_in_use(dcr)) { + if (!dcr->can_i_use_volume()) { return 0; /* fail if volume on another drive */ } } @@ -1497,7 +1037,7 @@ static int can_reserve_drive(DCR *dcr, RCTX &rctx) */ if (dev->num_writers == 0) { /* Now check if there are any reservations on the drive */ - if (dev->reserved_device) { + if (dev->num_reserved()) { return is_pool_ok(dcr); } else if (dev->can_append()) { if (is_pool_ok(dcr)) { @@ -1505,7 +1045,8 @@ static int can_reserve_drive(DCR *dcr, RCTX &rctx) } else { /* Changing pool, unload old tape if any in drive */ Dmsg0(dbglvl, "OK dev: num_writers=0, not reserved, pool change, unload changer\n"); - unload_autochanger(dcr, 0); + /* ***FIXME*** use set_unload() */ + unload_autochanger(dcr, -1); } } /* Device is available but not yet reserved, reserve it for us */ @@ -1532,7 +1073,7 @@ static int can_reserve_drive(DCR *dcr, RCTX &rctx) Mmsg(jcr->errmsg, _("3911 JobId=%u failed reserve drive %s.\n"), jcr->JobId, dev->print_name()); queue_reserve_message(jcr); - Dmsg1(dbglvl, "failed: No reserve %s\n", dev->print_name()); + Dmsg1(dbglvl, "Failed: No reserve %s\n", dev->print_name()); return 0; }