/*
Bacula® - The Network Backup Solution
- Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
+ Copyright (C) 2000-2009 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.
#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 pthread_mutex_t read_vol_lock = PTHREAD_MUTEX_INITIALIZER;
/* Forward referenced functions */
static void free_vol_item(VOLRES *vol);
+static VOLRES *new_vol_item(DCR *dcr, const char *VolumeName);
-
+/*
+ * 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;
}
}
-dlist *dup_vol_list(JCR *jcr)
+void lock_read_volumes()
{
- dlist *temp_vol_list;
- VOLRES *vol = NULL;
+ P(read_vol_lock);
+}
- lock_volumes();
- Dmsg0(dbglvl, "lock volumes\n");
+void unlock_read_volumes()
+{
+ V(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);
+ }
+ debug_list_volumes("remove_read_volume");
+ if (fvol) {
+ read_vol_list->remove(fvol);
+ free_vol_item(fvol);
+ }
+ unlock_read_volumes();
}
-
/*
* List Volumes -- this should be moved to status.c
*/
}
}
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
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");
+}