/*
Bacula® - The Network Backup Solution
- Copyright (C) 2000-2012 Free Software Foundation Europe e.V.
+ Copyright (C) 2000-2013 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.
rwl_destroy(&vol_list_lock);
}
-/*
+/*
* This allows a given thread to recursively call to lock_volumes()
*/
void _lock_volumes(const char *file, int line)
{
VOLRES *nvol, *vol;
- lock_read_volumes();
nvol = new_vol_item(NULL, VolumeName);
nvol->set_jobid(jcr->JobId);
+ lock_read_volumes();
vol = (VOLRES *)read_vol_list->binary_insert(nvol, read_compare);
+ unlock_read_volumes();
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);
}
- unlock_read_volumes();
}
/*
VOLRES *vol;
POOL_MEM msg(PM_MESSAGE);
- lock_volumes();
- foreach_dlist(vol, vol_list) {
+ foreach_vol(vol) {
if (vol->dev) {
Mmsg(msg, "List %s: %s in_use=%d swap=%d on device %s\n", imsg,
vol->vol_name, vol->is_in_use(), vol->is_swapping(), vol->dev->print_name());
}
Dmsg1(dbglvl, "%s", msg.c_str());
}
-
- unlock_volumes();
+ endeach_vol(vol);
}
POOL_MEM msg(PM_MESSAGE);
int len;
- lock_volumes();
- foreach_dlist(vol, vol_list) {
+ foreach_vol(vol) {
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(),
+ len = Mmsg(msg, " Reader=%d writers=%d reserves=%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,
+ len = Mmsg(msg, "Volume %s no device. volinuse= %d\n", vol->vol_name,
vol->is_in_use());
sendit(msg.c_str(), len, arg);
}
}
- unlock_volumes();
+ endeach_vol(vol);
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);
+ DEVICE *dev = vol->dev;
+ if (dev) {
+ len = Mmsg(msg, "Read volume: %s on device %s\n", vol->vol_name, dev->print_name());
+ sendit(msg.c_str(), len, arg);
+ len = Mmsg(msg, " Reader=%d writers=%d reserves=%d volinuse=%d JobId=%d\n",
+ dev->can_read()?1:0, dev->num_writers, dev->num_reserved(),
+ vol->is_in_use(), vol->get_jobid());
+ sendit(msg.c_str(), len, arg);
+ } else {
+ len = Mmsg(msg, "Volume: %s no device. volinuse= %d\n", vol->vol_name,
+ vol->is_in_use());
+ sendit(msg.c_str(), len, arg);
+ }
}
unlock_read_volumes();
-
}
/*
Dmsg3(dbglvl, "new Vol=%s at %p dev=%s\n",
VolumeName, vol->vol_name, vol->dev->print_name());
}
+ vol->init_mutex();
+ vol->inc_use_count();
return vol;
}
{
DEVICE *dev = NULL;
+ vol->dec_use_count();
+ vol->Lock();
+ if (vol->use_count() > 0) {
+ vol->Unlock();
+ return;
+ }
+ vol->Unlock();
free(vol->vol_name);
if (vol->dev) {
dev = vol->dev;
}
+ vol->destroy_mutex();
free(vol);
if (dev) {
dev->vol = NULL;
*
* 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"
+ * 1. The Volume list entry is attached to the drive (rather than
+ * attached to a job as it was previously. 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.
+ * 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
+ * volume name as it changes.
+ * This code keeps 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
+ * 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
+ * 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.
+ * 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.
*
* Return: VOLRES entry on success
* NULL volume busy on another drive
}
ASSERT(dev != NULL);
- Dmsg2(dbglvl, "enter reserve_volume=%s drive=%s\n", VolumeName,
+ 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 (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
return vol;
}
+/*
+ * Start walk of vol chain
+ * The proper way to walk the vol chain is:
+ * VOLRES *vol;
+ * foreach_vol(vol) {
+ * ...
+ * }
+ * endeach_vol(vol);
+ *
+ * It is possible to leave out the endeach_vol(vol), but
+ * in that case, the last vol referenced must be explicitly
+ * released with:
+ *
+ * free_vol_item(vol);
+ *
+ */
+VOLRES *vol_walk_start()
+{
+ VOLRES *vol;
+ lock_volumes();
+ vol = (VOLRES *)vol_list->first();
+ if (vol) {
+ vol->inc_use_count();
+ Dmsg2(dbglvl, "Inc walk_start use_count=%d volname=%s\n",
+ vol->use_count(), vol->vol_name);
+ }
+ unlock_volumes();
+ return vol;
+}
+
+/*
+ * Get next vol from chain, and release current one
+ */
+VOLRES *vol_walk_next(VOLRES *prev_vol)
+{
+ VOLRES *vol;
+
+ lock_volumes();
+ vol = (VOLRES *)vol_list->next(prev_vol);
+ if (vol) {
+ vol->inc_use_count();
+ Dmsg2(dbglvl, "Inc walk_next use_count=%d volname=%s\n",
+ vol->use_count(), vol->vol_name);
+ }
+ unlock_volumes();
+ if (prev_vol) {
+ free_vol_item(prev_vol);
+ }
+ return vol;
+}
+
+/*
+ * Release last vol referenced
+ */
+void vol_walk_end(VOLRES *vol)
+{
+ if (vol) {
+ Dmsg2(dbglvl, "Free walk_end use_count=%d volname=%s\n",
+ vol->use_count(), vol->vol_name);
+ free_vol_item(vol);
+ }
+}
+
/*
* Search for a Volume name in the Volume list.
*
}
-/*
+/*
* 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,
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
return true;
} else {
/*
- * Note, this frees the volume reservation entry, but the
+ * Note, this frees the volume reservation entry, but the
* file descriptor remains open with the OS.
*/
return free_volume(dev);
}
free(vol->vol_name);
vol->vol_name = NULL;
+ vol->destroy_mutex();
}
delete vol_list;
vol_list = NULL;
}
free(vol->vol_name);
vol->vol_name = NULL;
+ vol->destroy_mutex();
}
delete read_vol_list;
read_vol_list = NULL;
}
-/*
+/*
* 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
+ * 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, "lock volumes\n");
Dmsg0(dbglvl, "duplicate vol list\n");
temp_vol_list = New(dlist(vol, &vol->link));
- foreach_dlist(vol, vol_list) {
+ foreach_vol(vol) {
VOLRES *nvol;
VOLRES *tvol = (VOLRES *)malloc(sizeof(VOLRES));
memset(tvol, 0, sizeof(VOLRES));
Jmsg(jcr, M_WARNING, 0, "Logic error. Duplicating vol list hit duplicate.\n");
}
}
+ endeach_vol(vol);
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;