]> git.sur5r.net Git - bacula/bacula/commitdiff
Fix SD DCR race condition that causes seg faults
authorKern Sibbald <kern@sibbald.com>
Sun, 1 Nov 2009 14:42:11 +0000 (15:42 +0100)
committerKern Sibbald <kern@sibbald.com>
Sun, 1 Nov 2009 14:42:11 +0000 (15:42 +0100)
bacula/src/stored/acquire.c
bacula/src/stored/bscan.c
bacula/src/stored/dev.h
bacula/src/stored/protos.h

index 1458fb67c2397ad93bf8b6aba0c895cc7e205395..a63fcc0b16eebce3889491c7c2a8b74654c4821d 100644 (file)
@@ -30,7 +30,6 @@
  *
  *   Kern Sibbald, August MMII
  *
- *   Version $Id$
  */
 
 #include "bacula.h"                   /* pull in global headers */
@@ -38,6 +37,7 @@
 
 /* Forward referenced functions */
 static void attach_dcr_to_dev(DCR *dcr);
+static void detach_dcr_from_dev(DCR *dcr);
 static void set_dcr_from_vol(DCR *dcr, VOL_LIST *vol);
 
 
@@ -555,12 +555,6 @@ bool release_device(DCR *dcr)
    if (dcr->keep_dcr) {
       detach_dcr_from_dev(dcr);
    } else {
-      if (jcr->read_dcr == dcr) {
-         jcr->read_dcr = NULL;
-      }
-      if (jcr->dcr == dcr) {
-         jcr->dcr = NULL;
-      }
       free_dcr(dcr);
    }
    Dmsg2(100, "===== Device %s released by JobId=%u\n", dev->print_name(),
@@ -596,10 +590,17 @@ bool clean_device(DCR *dcr)
 DCR *new_dcr(JCR *jcr, DCR *dcr, DEVICE *dev)
 {
    if (!dcr) {
+      int errstat;
       dcr = (DCR *)malloc(sizeof(DCR));
       memset(dcr, 0, sizeof(DCR));
       dcr->tid = pthread_self();
       dcr->spool_fd = -1;
+      if ((errstat = pthread_mutex_init(&dcr->m_mutex, NULL)) != 0) {
+         berrno be;
+         dev->dev_errno = errstat;
+         Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.bstrerror(errstat));
+         Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
+      }
    }
    dcr->jcr = jcr;                 /* point back to jcr */
    /* Set device information, possibly change device */
@@ -659,18 +660,25 @@ static void remove_dcr_from_dcrs(DCR *dcr)
 
 static void attach_dcr_to_dev(DCR *dcr)
 {
-   DEVICE *dev = dcr->dev;
-   JCR *jcr = dcr->jcr;
+   DEVICE *dev;
+   JCR *jcr;
 
+   P(dcr->m_mutex);
+   dev = dcr->dev;
+   jcr = dcr->jcr;
    if (jcr) Dmsg1(500, "JobId=%u enter attach_dcr_to_dev\n", (uint32_t)jcr->JobId);
    if (!dcr->attached_to_dev && dev->initiated && jcr && jcr->get_JobType() != JT_SYSTEM) {
       dev->attached_dcrs->append(dcr);  /* attach dcr to device */
       dcr->attached_to_dev = true;
       Dmsg1(500, "JobId=%u attach_dcr_to_dev\n", (uint32_t)jcr->JobId);
    }
+   V(dcr->m_mutex);
 }
 
-void detach_dcr_from_dev(DCR *dcr)
+/* 
+ * DCR is locked before calling this routine
+ */
+static void locked_detach_dcr_from_dev(DCR *dcr)
 {
    DEVICE *dev = dcr->dev;
    Dmsg0(500, "Enter detach_dcr_from_dev\n"); /* jcr is NULL in some cases */
@@ -680,10 +688,18 @@ void detach_dcr_from_dev(DCR *dcr)
       dev->dlock();
       dcr->unreserve_device();
       dcr->dev->attached_dcrs->remove(dcr);  /* detach dcr from device */
-      dcr->attached_to_dev = false;
 //    remove_dcr_from_dcrs(dcr);      /* remove dcr from jcr list */
       dev->dunlock();
    }
+   dcr->attached_to_dev = false;
+}
+
+
+static void detach_dcr_from_dev(DCR *dcr)
+{
+   P(dcr->m_mutex);
+   locked_detach_dcr_from_dev(dcr);
+   V(dcr->m_mutex);
 }
 
 /*
@@ -692,9 +708,11 @@ void detach_dcr_from_dev(DCR *dcr)
  */
 void free_dcr(DCR *dcr)
 {
-   JCR *jcr = dcr->jcr;
+   JCR *jcr;
 
-   detach_dcr_from_dev(dcr);
+   P(dcr->m_mutex);
+   jcr = dcr->jcr;
+   locked_detach_dcr_from_dev(dcr);
 
    if (dcr->block) {
       free_block(dcr->block);
@@ -705,6 +723,11 @@ void free_dcr(DCR *dcr)
    if (jcr && jcr->dcr == dcr) {
       jcr->dcr = NULL;
    }
+   if (jcr && jcr->read_dcr == dcr) {
+      jcr->read_dcr = NULL;
+   }
+   V(dcr->m_mutex);
+   pthread_mutex_destroy(&dcr->m_mutex);
    free(dcr);
 }
 
index 113915f5720508e184237b68d56af074069e69a0..55c1fe13f3f068aea0f9c95ee9776ba382c16997 100644 (file)
@@ -594,7 +594,7 @@ static bool record_cb(DCR *dcr, DEV_RECORD *rec)
          /* Create JobMedia record */
          mjcr->read_dcr->VolLastIndex = dcr->VolLastIndex;
          create_jobmedia_record(db, mjcr);
-         detach_dcr_from_dev(mjcr->read_dcr);
+         free_dcr(mjcr->read_dcr);
          free_jcr(mjcr);
 
          break;
index b2c6793456260bb6aebcedcc6102d4aef7c14029..8a5a6f1344603fe1553e246589a50c00f803004f 100644 (file)
@@ -483,6 +483,11 @@ inline const char *DEVICE::print_name() const { return prt_name; }
  *  the device. Items in this record are "local" to the Job and
  *  do not affect other Jobs. Note, a job can have multiple
  *  DCRs open, each pointing to a different device. 
+ * Normally, there is only one JCR thread per DCR. However, the
+ *  big and important exception to this is when a Job is being
+ *  canceled. At that time, there may be two threads using the 
+ *  same DCR. Consequently, when creating/attaching/detaching
+ *  and freeing the DCR we must lock it (m_mutex).
  */
 class DCR {
 private:
@@ -493,6 +498,7 @@ private:
 public:
    dlink dev_link;                    /* link to attach to dev */
    JCR *jcr;                          /* pointer to JCR */
+   pthread_mutex_t m_mutex;           /* access control */
    DEVICE * volatile dev;             /* pointer to device */
    DEVRES *device;                    /* pointer to device resource */
    DEV_BLOCK *block;                  /* pointer to block */
index fd51df7f7255668d477f7570b5ac1e6a8b57b27e..862a5523a5aceb7b9746633f472d7bb4b72e150a 100644 (file)
@@ -41,7 +41,6 @@ bool     release_device(DCR *dcr);
 bool     clean_device(DCR *dcr);
 DCR     *new_dcr(JCR *jcr, DCR *dcr, DEVICE *dev);
 void     free_dcr(DCR *dcr);
-void     detach_dcr_from_dev(DCR *dcr);
 
 /* From askdir.c */
 enum get_vol_info_rw {