]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/stored/lock.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / stored / lock.c
index 647dd19dcf01f25f44b2e34c6ec072ebcdfc6efc..aebc31a787083dfe346fe375cfb91fb3a2e46727 100644 (file)
@@ -1,43 +1,33 @@
 /*
-   Bacula® - The Network Backup Solution
-
-   Copyright (C) 2000-2008 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
-   License as published by the Free Software Foundation and included
-   in the file LICENSE.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   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
-   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.
-   The licensor of Bacula is the Free Software Foundation Europe
-   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
-   Switzerland, email:ftf@fsfeurope.org.
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2017 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
 */
 /*
  * Collection of Bacula Storage daemon locking software
  *
- *  Kern Sibbald, 2000-2007.  June 2007
+ *  Kern Sibbald, June 2007
  *
- *   Version $Id$
  */
 
 #include "bacula.h"                   /* pull in global headers */
 #include "stored.h"                   /* pull in Storage Deamon headers */
 
 #ifdef SD_DEBUG_LOCK
-const int dbglvl = 0;
+const int dbglvl = 300;
 #else
 const int dbglvl = 500;
 #endif
@@ -52,63 +42,66 @@ const int dbglvl = 500;
  *               is "marked" in use.  When setting and removing the
                  block, the device is locked, but after dblock is
                  called the device is unlocked.
- *  2. dlock()   simple mutex that locks the device structure. A dlock
+ *  2. Lock()    simple mutex that locks the device structure. A Lock
  *               can be acquired while a device is blocked if it is not
- *               locked.      
- *  3. r_dlock   "recursive" dlock, when means that a dlock (mutex)
+ *               locked.
+ *  3. rLock(locked)  "recursive" Lock, when means that a Lock (mutex)
  *               will be acquired on the device if it is not blocked
  *               by some other thread. If the device was blocked by
  *               the current thread, it will acquire the lock.
  *               If some other thread has set a block on the device,
  *               this call will wait until the device is unblocked.
+ *               Can be called with locked true, which means the
+ *               Lock is already set
  *
  *  A lock is normally set when modifying the device structure.
- *  A r_lock is normally acquired when you want to block the device
+ *  A rLock is normally acquired when you want to block the device
  *    i.e. it will wait until the device is not blocked.
  *  A block is normally set during long operations like writing to
  *    the device.
- *  If you are writing the device, you will normally block and 
+ *  If you are writing the device, you will normally block and
  *    lock it.
  *  A lock cannot be violated. No other thread can touch the
- *    device while a lock is set.  
+ *    device while a lock is set.
  *  When a block is set, every thread accept the thread that set
- *    the block will block if r_dlock is called.
+ *    the block will block if rLock is called.
  *  A device can be blocked for multiple reasons, labeling, writing,
  *    acquiring (opening) the device, waiting for the operator, unmounted,
  *    ...
- *  Under certain conditions the block that is set on a device can be    
+ *  Under certain conditions the block that is set on a device can be
  *    stolen and the device can be used by another thread. For example,
- *    a device is blocked because it is waiting for the operator to  
+ *    a device is blocked because it is waiting for the operator to
  *    mount a tape.  The operator can then unmount the device, and label
  *    a tape, re-mount it, give back the block, and the job will continue.
  *
  *
  * Functions:
  *
- *   DEVICE::dlock()   does P(m_mutex)     (in dev.h)
- *   DEVICE::dunlock() does V(m_mutex)
+ *   DEVICE::Lock()   does P(m_mutex)     (in dev.h)
+ *   DEVICE::Unlock() does V(m_mutex)
  *
- *   DEVICE::r_dlock() allows locking the device when this thread
-                         already has the device blocked.
- *                    dlock()
+ *   DEVICE::rLock(locked) allows locking the device when this thread
+ *                     already has the device blocked.
+ *                    if (!locked)
+ *                       Lock()
  *                    if blocked and not same thread that locked
  *                       pthread_cond_wait
- *                    leaves device locked 
+ *                    leaves device locked
  *
- *   DEVICE::r_dunlock() unlocks but does not unblock
- *                    same as dunlock();
+ *   DEVICE::rUnlock() unlocks but does not unblock
+ *                    same as Unlock();
  *
- *   DEVICE::dblock(why)  does 
- *                    r_dlock();         (recursive device lock)
- *                    block_device(this, why) 
- *                    r_dunlock()
+ *   DEVICE::dblock(why)  does
+ *                    rLock();         (recursive device lock)
+ *                    block_device(this, why)
+ *                    rUnlock()
  *
  *   DEVICE::dunblock does
- *                    dlock()
+ *                    Lock()
  *                    unblock_device()
- *                    dunlock()
+ *                    Unlock()
  *
- *   block_device() does  (must be locked and not blocked at entry)  
+ *   block_device() does  (must be locked and not blocked at entry)
  *                    set blocked status
  *                    set our pid
  *
@@ -124,10 +117,10 @@ const int dbglvl = 500;
  *                    save status
  *                    set new blocked status
  *                    set new pid
- *                    unlock()
+ *                    Unlock()
  *
  *   give_back_device_lock() does (must be blocked but not locked)
- *                    dlock()
+ *                    Lock()
  *                    reset blocked status
  *                    save previous blocked
  *                    reset pid
@@ -136,57 +129,77 @@ const int dbglvl = 500;
  *
  */
 
-
 void DEVICE::dblock(int why)
 {
-   r_dlock();              /* need recursive lock to block */
+   rLock(false);              /* need recursive lock to block */
    block_device(this, why);
-   r_dunlock();
+   rUnlock();
 }
 
 void DEVICE::dunblock(bool locked)
 {
    if (!locked) {
-      dlock();
+      Lock();
    }
    unblock_device(this);
-   dunlock();
+   Unlock();
 }
 
 
-#ifdef SD_DEBUG_LOCK
-void DEVICE::_dlock(const char *file, int line)
-{
-   Dmsg4(sd_dbglvl, "dlock from %s:%d precnt=%d JobId=%u\n", file, line,
-         m_count, get_jobid_from_tid()); 
-   /* Note, this *really* should be protected by a mutex, but
-    *  since it is only debug code we don't worry too much.  
-    */
-   if (m_count > 0 && pthread_equal(m_pid, pthread_self())) {
-      Dmsg5(sd_dbglvl, "Possible DEADLOCK!! lock held by JobId=%u from %s:%d m_count=%d JobId=%u\n", 
-            get_jobid_from_tid(m_pid),
-            file, line, m_count, get_jobid_from_tid());
-   }
-   P(m_mutex);
+
+/*
+ * Debug DEVICE locks  N.B.
+ *
+ */
+
+#ifdef DEV_DEBUG_LOCK
+
+void DEVICE::dbg_Lock(const char *file, int line)
+{
+   Dmsg4(sd_dbglvl, "Lock %s from %s:%d precnt=%d\n", device->hdr.name, file, line, m_count);
+   bthread_mutex_lock_p(&m_mutex, file, line);
    m_pid = pthread_self();
-   m_count++; 
+   m_count++;
 }
 
-void DEVICE::_dunlock(const char *file, int line)
+void DEVICE::dbg_Unlock(const char *file, int line)
 {
-   m_count--; 
-   Dmsg4(sd_dbglvl+1, "dunlock from %s:%d postcnt=%d JobId=%u\n", file, line,
-         m_count, get_jobid_from_tid()); 
-   V(m_mutex);   
+   m_count--;
+   clear_thread_id(m_pid);
+   Dmsg4(sd_dbglvl, "Unlock %s from %s:%d postcnt=%d\n", device->hdr.name, file, line, m_count);
+   bthread_mutex_unlock_p(&m_mutex, file, line);
 }
 
-void DEVICE::_r_dunlock(const char *file, int line)
+void DEVICE::dbg_rUnlock(const char *file, int line)
 {
-   this->_dunlock(file, line);
+   Dmsg2(sd_dbglvl, "rUnlock from %s:%d\n", file, line);
+   dbg_Unlock(file, line);
 }
 
-#endif
+#else
+
+/*
+ * DEVICE locks  N.B.
+ *
+ */
+
+
+void DEVICE::rUnlock()
+{
+   Unlock();
+}
+
+void DEVICE::Lock()
+{
+   P(m_mutex);
+}
+
+void DEVICE::Unlock()
+{
+   V(m_mutex);
+}
 
+#endif /* DEV_DEBUG_LOCK */
 
 /*
  * This is a recursive lock that checks if the device is blocked.
@@ -195,47 +208,208 @@ void DEVICE::_r_dunlock(const char *file, int line)
  * must wait. The no_wait_id thread is out obtaining a new volume
  * and preparing the label.
  */
-#ifdef SD_DEBUG_LOCK
-void DEVICE::_r_dlock(const char *file, int line)
+#ifdef DEV_DEBUG_LOCK
+void DEVICE::dbg_rLock(const char *file, int line, bool locked)
 {
-   Dmsg4(sd_dbglvl+1, "r_dlock blked=%s from %s:%d JobId=%u\n", this->print_blocked(),
-         file, line, get_jobid_from_tid());
-#else
-void DEVICE::r_dlock()
+   Dmsg3(sd_dbglvl, "Enter rLock blked=%s from %s:%d\n", print_blocked(),
+         file, line);
+   if (!locked) {
+      /* lockmgr version of P(m_mutex) */
+      Dmsg4(sd_dbglvl, "Lock %s in rLock %s from %s:%d\n",
+         device->hdr.name, print_blocked(), file, line);
+      bthread_mutex_lock_p(&m_mutex, file, line);
+      m_count++;
+   }
+
+   if (blocked() && !pthread_equal(no_wait_id, pthread_self())) {
+      num_waiting++;             /* indicate that I am waiting */
+      while (blocked()) {
+         int stat;
+#ifndef HAVE_WIN32
+         /* thread id on Win32 may be a struct */
+         Dmsg5(sd_dbglvl, "Blocked by %d %s in rLock blked=%s no_wait=%p me=%p\n",
+            blocked_by, device->hdr.name, print_blocked(), no_wait_id, pthread_self());
+#endif
+         if ((stat = bthread_cond_wait_p(&this->wait, &m_mutex, file, line)) != 0) {
+            berrno be;
+            this->dbg_Unlock(file, line);
+            Emsg1(M_ABORT, 0, _("pthread_cond_wait failure. ERR=%s\n"),
+               be.bstrerror(stat));
+         }
+      }
+      num_waiting--;             /* no longer waiting */
+   }
+}
+#else /* DEV_DEBUG_LOCK */
+
+void DEVICE::rLock(bool locked)
 {
+   if (!locked) {
+      Lock();
+      m_count++;
+   }
+
+   if (blocked() && !pthread_equal(no_wait_id, pthread_self())) {
+      num_waiting++;             /* indicate that I am waiting */
+      while (blocked()) {
+         int stat;
+#ifndef HAVE_WIN32
+         /* thread id on Win32 may be a struct */
+         Dmsg5(sd_dbglvl, "Blocked by %d rLock %s blked=%s no_wait=%p me=%p\n",
+            blocked_by, device->hdr.name, print_blocked(), no_wait_id, pthread_self());
 #endif
-   int stat;
-   this->dlock();   
-   if (this->blocked() && !pthread_equal(this->no_wait_id, pthread_self())) {
-      this->num_waiting++;             /* indicate that I am waiting */
-      while (this->blocked()) {
-         Dmsg3(sd_dbglvl, "r_dlock blked=%s no_wait=%p me=%p\n", this->print_blocked(),
-               this->no_wait_id, pthread_self());
          if ((stat = pthread_cond_wait(&this->wait, &m_mutex)) != 0) {
             berrno be;
-            this->dunlock();
+            this->Unlock();
             Emsg1(M_ABORT, 0, _("pthread_cond_wait failure. ERR=%s\n"),
                be.bstrerror(stat));
          }
       }
-      this->num_waiting--;             /* no longer waiting */
+      num_waiting--;             /* no longer waiting */
    }
 }
 
+#endif  /* DEV_DEBUG_LOCK */
+
+#ifdef SD_DEBUG_LOCK
+
+void DEVICE::dbg_Lock_acquire(const char *file, int line)
+{
+   Dmsg2(sd_dbglvl, "Lock_acquire from %s:%d\n", file, line);
+   bthread_mutex_lock_p(&acquire_mutex, file, line);
+}
+
+void DEVICE::dbg_Unlock_acquire(const char *file, int line)
+{
+   Dmsg2(sd_dbglvl, "Unlock_acquire from %s:%d\n", file, line);
+   bthread_mutex_unlock_p(&acquire_mutex, file, line);
+}
+
+void DEVICE::dbg_Lock_read_acquire(const char *file, int line)
+{
+   Dmsg2(sd_dbglvl, "Lock_read_acquire from %s:%d\n", file, line);
+   bthread_mutex_lock_p(&read_acquire_mutex, file, line);
+}
+
+void DEVICE::dbg_Unlock_read_acquire(const char *file, int line)
+{
+   Dmsg2(sd_dbglvl, "Unlock_read_acquire from %s:%d\n", file, line);
+   bthread_mutex_unlock_p(&read_acquire_mutex, file, line);
+}
+
+void DEVICE::dbg_Lock_VolCatInfo(const char *file, int line)
+{
+   bthread_mutex_lock_p(&volcat_mutex, file, line);
+}
+
+void DEVICE::dbg_Unlock_VolCatInfo(const char *file, int line)
+{
+   bthread_mutex_unlock_p(&volcat_mutex, file, line);
+}
+
+#else
+
+void DEVICE::Lock_acquire()
+{
+   P(acquire_mutex);
+}
+
+void DEVICE::Unlock_acquire()
+{
+   V(acquire_mutex);
+}
+
+void DEVICE::Lock_read_acquire()
+{
+   P(read_acquire_mutex);
+}
+
+void DEVICE::Unlock_read_acquire()
+{
+   V(read_acquire_mutex);
+}
+
+void DEVICE::Lock_VolCatInfo()
+{
+   P(volcat_mutex);
+}
+
+void DEVICE::Unlock_VolCatInfo()
+{
+   V(volcat_mutex);
+}
+
+
+
+#endif
+
+/* Main device access control */
+int DEVICE::init_mutex()
+{
+   return pthread_mutex_init(&m_mutex, NULL);
+}
+
+/* Mutex around the freespace command */
+int DEVICE::init_freespace_mutex()
+{
+   return pthread_mutex_init(&freespace_mutex, NULL);
+}
+
+/* Write device acquire mutex */
+int DEVICE::init_acquire_mutex()
+{
+   return pthread_mutex_init(&acquire_mutex, NULL);
+}
+
+/* Read device acquire mutex */
+int DEVICE::init_read_acquire_mutex()
+{
+   return pthread_mutex_init(&read_acquire_mutex, NULL);
+}
+
+/* VolCatInfo mutex */
+int DEVICE::init_volcat_mutex()
+{
+   return pthread_mutex_init(&volcat_mutex, NULL);
+}
+
+/* dcrs mutex */
+int DEVICE::init_dcrs_mutex()
+{
+   return pthread_mutex_init(&dcrs_mutex, NULL);
+}
+
+/* Set order in which device locks must be acquired */
+void DEVICE::set_mutex_priorities()
+{
+   /* Ensure that we respect this order in P/V operations */
+   bthread_mutex_set_priority(&m_mutex,       PRIO_SD_DEV_ACCESS);
+   bthread_mutex_set_priority(&spool_mutex,   PRIO_SD_DEV_SPOOL);
+   bthread_mutex_set_priority(&acquire_mutex, PRIO_SD_DEV_ACQUIRE);
+}
+
+int DEVICE::next_vol_timedwait(const struct timespec *timeout)
+{
+   return pthread_cond_timedwait(&wait_next_vol, &m_mutex, timeout);
+}
+
+
 /*
  * Block all other threads from using the device
  *  Device must already be locked.  After this call,
- *  the device is blocked to any thread calling dev->r_lock(),
+ *  the device is blocked to any thread calling dev->rLock(),
  *  but the device is not locked (i.e. no P on device).  Also,
- *  the current thread can do slip through the dev->r_lock()
+ *  the current thread can do slip through the dev->rLock()
  *  calls without blocking.
  */
 void _block_device(const char *file, int line, DEVICE *dev, int state)
 {
-   ASSERT(dev->blocked() == BST_NOT_BLOCKED);
+   ASSERT2(dev->blocked() == BST_NOT_BLOCKED, "Block request of device already blocked");
    dev->set_blocked(state);           /* make other threads wait */
    dev->no_wait_id = pthread_self();  /* allow us to continue */
-   Dmsg3(sd_dbglvl, "set blocked=%s from %s:%d\n", dev->print_blocked(), file, line);
+   dev->blocked_by = get_jobid_from_tsd();
+   Dmsg4(sd_dbglvl, "Blocked %s %s from %s:%d\n",
+      dev->device->hdr.name, dev->print_blocked(), file, line);
 }
 
 /*
@@ -245,10 +419,12 @@ void _block_device(const char *file, int line, DEVICE *dev, int state)
  */
 void _unblock_device(const char *file, int line, DEVICE *dev)
 {
-   Dmsg3(sd_dbglvl, "unblock %s from %s:%d\n", dev->print_blocked(), file, line);
-   ASSERT(dev->blocked());
+   Dmsg4(sd_dbglvl, "Unblocked %s %s from %s:%d\n", dev->device->hdr.name,
+      dev->print_blocked(), file, line);
+   ASSERT2(dev->blocked(), "Unblock request of device not blocked");
    dev->set_blocked(BST_NOT_BLOCKED);
-   dev->no_wait_id = 0;
+   dev->blocked_by = 0;
+   clear_thread_id(dev->no_wait_id);
    if (dev->num_waiting > 0) {
       pthread_cond_broadcast(&dev->wait); /* wake them up */
    }
@@ -260,16 +436,17 @@ void _unblock_device(const char *file, int line, DEVICE *dev)
  */
 void _steal_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *hold, int state)
 {
-
-   Dmsg3(sd_dbglvl, "steal lock. old=%s from %s:%d\n", dev->print_blocked(),
-      file, line);
+   Dmsg4(sd_dbglvl, "Steal lock %s old=%s from %s:%d\n",
+      dev->device->hdr.name, dev->print_blocked(), file, line);
    hold->dev_blocked = dev->blocked();
    hold->dev_prev_blocked = dev->dev_prev_blocked;
    hold->no_wait_id = dev->no_wait_id;
+   hold->blocked_by = dev->blocked_by;
    dev->set_blocked(state);
    Dmsg1(sd_dbglvl, "steal lock. new=%s\n", dev->print_blocked());
    dev->no_wait_id = pthread_self();
-   dev->dunlock();
+   dev->blocked_by = get_jobid_from_tsd();
+   dev->Unlock();
 }
 
 /*
@@ -278,19 +455,20 @@ void _steal_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *
  */
 void _give_back_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *hold)
 {
-   Dmsg3(sd_dbglvl, "return lock. old=%s from %s:%d\n",
-      dev->print_blocked(), file, line);
-   dev->dlock();
+   Dmsg4(sd_dbglvl, "Return lock %s old=%s from %s:%d\n",
+      dev->device->hdr.name, dev->print_blocked(), file, line);
+   dev->Lock();
    dev->set_blocked(hold->dev_blocked);
    dev->dev_prev_blocked = hold->dev_prev_blocked;
    dev->no_wait_id = hold->no_wait_id;
+   dev->blocked_by = hold->blocked_by;
    Dmsg1(sd_dbglvl, "return lock. new=%s\n", dev->print_blocked());
    if (dev->num_waiting > 0) {
       pthread_cond_broadcast(&dev->wait); /* wake them up */
    }
 }
 
-const char *DEVICE::print_blocked() const 
+const char *DEVICE::print_blocked() const
 {
    switch (m_blocked) {
    case BST_NOT_BLOCKED:
@@ -309,6 +487,8 @@ const char *DEVICE::print_blocked() const
       return "BST_MOUNT";
    case BST_DESPOOLING:
       return "BST_DESPOOLING";
+   case BST_RELEASING:
+      return "BST_RELEASING";
    default:
       return _("unknown blocked code");
    }
@@ -318,11 +498,12 @@ const char *DEVICE::print_blocked() const
 /*
  * Check if the device is blocked or not
  */
-bool is_device_unmounted(DEVICE *dev)
+bool DEVICE::is_device_unmounted()
 {
    bool stat;
-   int blocked = dev->blocked();
-   stat = (blocked == BST_UNMOUNTED) ||
-          (blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
+
+   int blk = blocked();
+   stat = (blk == BST_UNMOUNTED) ||
+          (blk == BST_UNMOUNTED_WAITING_FOR_SYSOP);
    return stat;
 }