/*
Bacula® - The Network Backup Solution
- Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
+ Copyright (C) 2000-2011 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.
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.
*
* 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;
+const int dbglvl = 150;
static dlist *vol_list = NULL;
static brwlock_t vol_list_lock;
+static dlist *read_vol_list = NULL;
+static bthread_mutex_t read_vol_lock = BTHREAD_MUTEX_PRIORITY(PRIO_SD_READ_VOL_LIST);
/* Forward referenced functions */
static void free_vol_item(VOLRES *vol);
+static VOLRES *new_vol_item(DCR *dcr, const char *VolumeName);
+static void debug_list_volumes(const char *imsg);
-
+/*
+ * For append volumes the key is the VolumeName.
+ */
static int my_compare(void *item1, void *item2)
{
return strcmp(((VOLRES *)item1)->vol_name, ((VOLRES *)item2)->vol_name);
}
+/*
+ * For read volumes the key is JobId, VolumeName.
+ */
+static int read_compare(void *item1, void *item2)
+{
+ VOLRES *vol1 = (VOLRES *)item1;
+ VOLRES *vol2 = (VOLRES *)item2;
+
+ if (vol1->get_jobid() == vol2->get_jobid()) {
+ return strcmp(vol1->vol_name, vol2->vol_name);
+ }
+ if (vol1->get_jobid() < vol2->get_jobid()) {
+ return -1;
+ }
+ return 1;
+}
+
+
bool is_vol_list_empty()
{
return vol_list->empty();
int vol_list_lock_count = 0;
+/*
+ * Initialized the main volume list. Note, we are using a recursive lock.
+ */
void init_vol_list_lock()
{
int errstat;
- if ((errstat=rwl_init(&vol_list_lock)) != 0) {
+ if ((errstat=rwl_init(&vol_list_lock, PRIO_SD_VOL_LIST)) != 0) {
berrno be;
Emsg1(M_ABORT, 0, _("Unable to initialize volume list lock. ERR=%s\n"),
be.bstrerror(errstat));
rwl_destroy(&vol_list_lock);
}
-
-
/*
* This allows a given thread to recursively call to lock_volumes()
*/
-void _lock_volumes()
+void _lock_volumes(const char *file, int line)
{
int errstat;
vol_list_lock_count++;
- if ((errstat=rwl_writelock(&vol_list_lock)) != 0) {
+ if ((errstat=rwl_writelock_p(&vol_list_lock, file, line)) != 0) {
berrno be;
Emsg2(M_ABORT, 0, "rwl_writelock failure. stat=%d: ERR=%s\n",
errstat, be.bstrerror(errstat));
}
}
-dlist *dup_vol_list(JCR *jcr)
+void lock_read_volumes(const char *file="**Unknown", int line=0)
{
- dlist *temp_vol_list;
- VOLRES *vol = NULL;
+ bthread_mutex_lock_p(&read_vol_lock, file, line);
+}
- lock_volumes();
- Dmsg0(dbglvl, "lock volumes\n");
+void unlock_read_volumes()
+{
+ bthread_mutex_unlock(&read_vol_lock);
+}
- /*
- * 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");
- }
+/*
+ * Add a volume to the read list.
+ * Note, we use VOLRES because it simplifies the code
+ * even though, the only part of VOLRES that we need is
+ * the volume name. The same volume may be in the list
+ * multiple times, but each one is distinguished by the
+ * JobId. We use JobId, VolumeName as the key.
+ * We can get called multiple times for the same volume because
+ * when parsing the bsr, the volume name appears multiple times.
+ */
+void add_read_volume(JCR *jcr, const char *VolumeName)
+{
+ VOLRES *nvol, *vol;
+
+ lock_read_volumes();
+ nvol = new_vol_item(NULL, VolumeName);
+ nvol->set_jobid(jcr->JobId);
+ vol = (VOLRES *)read_vol_list->binary_insert(nvol, read_compare);
+ if (vol != nvol) {
+ free_vol_item(nvol);
+ Dmsg2(dbglvl, "read_vol=%s JobId=%d already in list.\n", VolumeName, jcr->JobId);
+ } else {
+ Dmsg2(dbglvl, "add read_vol=%s JobId=%d\n", VolumeName, jcr->JobId);
}
- Dmsg0(dbglvl, "unlock volumes\n");
- unlock_volumes();
- return temp_vol_list;
+ unlock_read_volumes();
}
-void free_temp_vol_list(dlist *temp_vol_list)
+/*
+ * Remove a given volume name from the read list.
+ */
+void remove_read_volume(JCR *jcr, const char *VolumeName)
{
- 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");
+ VOLRES vol, *fvol;
+ lock_read_volumes();
+ vol.vol_name = bstrdup(VolumeName);
+ vol.set_jobid(jcr->JobId);
+ fvol = (VOLRES *)read_vol_list->binary_search(&vol, read_compare);
+ free(vol.vol_name);
+ if (fvol) {
+ Dmsg3(dbglvl, "remove_read_vol=%s JobId=%d found=%d\n", VolumeName, jcr->JobId, fvol!=NULL);
+ }
+ if (fvol) {
+ read_vol_list->remove(fvol);
+ free_vol_item(fvol);
+ }
+ unlock_read_volumes();
}
-
/*
* List Volumes -- this should be moved to status.c
*/
debug_nolock = false
};
-void debug_list_volumes(const char *imsg)
+static void debug_list_volumes(const char *imsg)
{
VOLRES *vol;
POOL_MEM msg(PM_MESSAGE);
}
}
unlock_volumes();
+
+ lock_read_volumes();
+ foreach_dlist(vol, read_vol_list) {
+ len = Mmsg(msg, "%s read volume JobId=%d\n", vol->vol_name,
+ vol->get_jobid());
+ sendit(msg.c_str(), len, arg);
+ }
+ unlock_read_volumes();
+
}
/*
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());
+ if (dcr) {
+ vol->dev = dcr->dev;
+ Dmsg3(dbglvl, "new Vol=%s at %p dev=%s\n",
+ VolumeName, vol->vol_name, vol->dev->print_name());
+ }
return vol;
}
VOLRES *vol, *nvol;
DEVICE * volatile dev = dcr->dev;
+ if (job_canceled(dcr->jcr)) {
+ return NULL;
+ }
ASSERT(dev != NULL);
Dmsg2(dbglvl, "enter reserve_volume=%s drive=%s\n", VolumeName,
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 */
+ /* If old Volume is still mounted, must unload it */
+ if (strcmp(vol->vol_name, dev->VolHdr.VolumeName) == 0) {
+ Dmsg0(50, "set_unload\n");
+ dev->set_unload(); /* have to unload current volume */
+ }
+ free_volume(dev); /* Release old volume entry */
debug_list_volumes("reserve_vol free");
}
}
nvol->dev = NULL; /* don't zap dev entry */
free_vol_item(nvol);
+ if (vol->dev) {
+ Dmsg2(dbglvl, "dev=%s vol->dev=%s\n", dev->print_name(), vol->dev->print_name());
+ }
+
/*
* Check if we are trying to use the Volume on a different drive
* dev is our device
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",
+ Dmsg5(dbglvl, "==== Swap not possible Vol busy=%d swap=%d vol=%s from dev=%s to %s\n",
+ vol->dev->is_busy(), vol->is_swapping(),
VolumeName, vol->dev->print_name(), dev->print_name());
+ if (vol->is_swapping() && dev->swap_dev) {
+ Dmsg2(dbglvl, "Swap vol=%s dev=%s\n", vol->vol_name, dev->swap_dev->print_name());
+ } else {
+ Dmsg1(dbglvl, "swap_dev=%p\n", dev->swap_dev);
+ }
+ debug_list_volumes("failed swap");
vol = NULL; /* device busy */
goto get_out;
}
VOLRES *find_volume(const char *VolumeName)
{
VOLRES vol, *fvol;
+
+ if (vol_list->empty()) {
+ return NULL;
+ }
/* Do not lock reservations here */
lock_volumes();
vol.vol_name = bstrdup(VolumeName);
return fvol;
}
+/*
+ * Search for a Volume name in the read Volume list.
+ *
+ * Returns: VOLRES entry on success
+ * NULL if the Volume is not in the list
+ */
+static VOLRES *find_read_volume(const char *VolumeName)
+{
+ VOLRES vol, *fvol;
+
+ if (read_vol_list->empty()) {
+ Dmsg0(dbglvl, "find_read_vol: read_vol_list empty.\n");
+ return NULL;
+ }
+ /* Do not lock reservations here */
+ lock_read_volumes();
+ vol.vol_name = bstrdup(VolumeName);
+ /* Note, we do want a simple my_compare on volume name only here */
+ fvol = (VOLRES *)read_vol_list->binary_search(&vol, my_compare);
+ free(vol.vol_name);
+ Dmsg2(dbglvl, "find_read_vol=%s found=%d\n", VolumeName, fvol!=NULL);
+ unlock_read_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
{
VOLRES *vol;
- if (dev->vol == NULL) {
+ lock_volumes();
+ vol = dev->vol;
+ if (vol == NULL) {
Dmsg1(dbglvl, "No vol on dev %s\n", dev->print_name());
+ unlock_volumes();
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);
+ Dmsg1(dbglvl, "=== clear in_use vol=%s\n", 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");
+ } else {
+ Dmsg1(dbglvl, "=== cannot clear swapping vol=%s\n", vol->vol_name);
}
unlock_volumes();
return true;
/* Create the Volume list */
-void create_volume_list()
+void create_volume_lists()
{
VOLRES *vol = NULL;
if (vol_list == NULL) {
vol_list = New(dlist(vol, &vol->link));
}
+ if (read_vol_list == NULL) {
+ read_vol_list = New(dlist(vol, &vol->link));
+ }
}
-/* Release all Volumes from the list */
-void free_volume_list()
+/*
+ * Free normal append volumes list
+ */
+static void free_volume_list()
{
VOLRES *vol;
- if (!vol_list) {
- return;
+ if (vol_list) {
+ 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();
}
- 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);
+}
+
+/* Release all Volumes from the list */
+void free_volume_lists()
+{
+ VOLRES *vol;
+
+ free_volume_list(); /* normal append list */
+
+ if (read_vol_list) {
+ lock_read_volumes();
+ foreach_dlist(vol, read_vol_list) {
+ if (vol->dev) {
+ Dmsg2(dbglvl, "free read_vol_list Volume=%s dev=%s\n", vol->vol_name, vol->dev->print_name());
+ } else {
+ Dmsg1(dbglvl, "free read_vol_list Volume=%s No dev\n", vol->vol_name);
+ }
+ free(vol->vol_name);
+ vol->vol_name = NULL;
}
- free(vol->vol_name);
- vol->vol_name = NULL;
+ delete read_vol_list;
+ read_vol_list = NULL;
+ unlock_read_volumes();
}
- delete vol_list;
- vol_list = NULL;
- unlock_volumes();
}
+/*
+ * Determine if caller can write on volume
+ */
+bool DCR::can_i_write_volume()
+{
+ VOLRES *vol;
+
+ vol = find_read_volume(VolumeName);
+ if (vol) {
+ Dmsg1(100, "Found in read list; cannot write vol=%s\n", VolumeName);
+ return false;
+ }
+ return can_i_use_volume();
+}
+
+/*
+ * Determine if caller can read or write volume
+ */
bool DCR::can_i_use_volume()
{
bool rtn = true;
VOLRES *vol;
+ if (job_canceled(jcr)) {
+ return false;
+ }
lock_volumes();
vol = find_volume(VolumeName);
if (!vol) {
return rtn;
}
+
+/*
+ * 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).
+ */
+dlist *dup_vol_list(JCR *jcr)
+{
+ dlist *temp_vol_list;
+ VOLRES *vol = NULL;
+
+ lock_volumes();
+ Dmsg0(dbglvl, "lock volumes\n");
+
+ 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;
+}
+
+/*
+ * Free the specified temp 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");
+}