Exemplo n.º 1
0
/*
 * Acquire device for writing. We permit multiple writers.
 *  If this is the first one, we read the label.
 *
 *  Returns: NULL if failed for any reason
 *           dcr if successful.
 *   Note, normally reserve_device_for_append() is called
 *   before this routine.
 */
DCR *acquire_device_for_append(DCR *dcr)
{
   DEVICE *dev = dcr->dev;
   JCR *jcr = dcr->jcr;
   bool ok = false;
   bool have_vol = false;

   Enter(200);
   init_device_wait_timers(dcr);

   dev->Lock_acquire();             /* only one job at a time */
   dev->Lock();
   Dmsg1(100, "acquire_append device is %s\n", dev->is_tape() ? "tape" : "disk");

   /*
    * With the reservation system, this should not happen
    */
   if (dev->can_read()) {
      Jmsg1(jcr, M_FATAL, 0, _("Want to append, but device %s is busy reading.\n"), dev->print_name());
      Dmsg1(200, "Want to append but device %s is busy reading.\n", dev->print_name());
      goto get_out;
   }

   dev->clear_unload();

   /*
    * have_vol defines whether or not mount_next_write_volume should
    *   ask the Director again about what Volume to use.
    */
   if (dev->can_append() && dcr->is_suitable_volume_mounted() &&
       !bstrcmp(dcr->VolCatInfo.VolCatStatus, "Recycle")) {
      Dmsg0(190, "device already in append.\n");
      /*
       * At this point, the correct tape is already mounted, so
       *   we do not need to do mount_next_write_volume(), unless
       *   we need to recycle the tape.
       */
       if (dev->num_writers == 0) {
          dev->VolCatInfo = dcr->VolCatInfo;   /* structure assignment */
       }
       have_vol = dcr->is_tape_position_ok();
   }

   if (!have_vol) {
      dev->rLock(true);
      block_device(dev, BST_DOING_ACQUIRE);
      dev->Unlock();
      Dmsg1(190, "jid=%u Do mount_next_write_vol\n", (uint32_t)jcr->JobId);
      if (!dcr->mount_next_write_volume()) {
         if (!job_canceled(jcr)) {
            /* Reduce "noise" -- don't print if job canceled */
            Jmsg(jcr, M_FATAL, 0, _("Could not ready device %s for append.\n"),
               dev->print_name());
            Dmsg1(200, "Could not ready device %s for append.\n",
               dev->print_name());
         }
         dev->Lock();
         unblock_device(dev);
         goto get_out;
      }
      Dmsg2(190, "Output pos=%u:%u\n", dcr->dev->file, dcr->dev->block_num);
      dev->Lock();
      unblock_device(dev);
   }

   dev->num_writers++;                /* we are now a writer */
   if (jcr->NumWriteVolumes == 0) {
      jcr->NumWriteVolumes = 1;
   }
   dev->VolCatInfo.VolCatJobs++;              /* increment number of jobs on vol */
   Dmsg4(100, "=== nwriters=%d nres=%d vcatjob=%d dev=%s\n",
         dev->num_writers, dev->num_reserved(), dev->VolCatInfo.VolCatJobs, dev->print_name());
   dcr->dir_update_volume_info(false, false); /* send Volume info to Director */
   ok = true;

get_out:
   /* Don't plugin close here, we might have multiple writers */
   dcr->clear_reserved();
   dev->Unlock();
   dev->Unlock_acquire();
   Leave(200);
   return ok ? dcr : NULL;
}
Exemplo n.º 2
0
/*
 * NB! This routine locks the device, but if committing will
 *     not unlock it. If not committing, it will be unlocked.
 */
static bool despool_data(DCR *dcr, bool commit)
{
   DEVICE *rdev;
   DCR *rdcr;
   bool ok = true;
   DEV_BLOCK *block;
   JCR *jcr = dcr->jcr;
   int stat;
   char ec1[50];

   Dmsg0(100, "Despooling data\n");
   /*
    * Commit means that the job is done, so we commit, otherwise, we
    *  are despooling because of user spool size max or some error  
    *  (e.g. filesystem full).
    */
   if (commit) {
      Jmsg(jcr, M_INFO, 0, _("Committing spooled data to Volume \"%s\". Despooling %s bytes ...\n"),
         jcr->dcr->VolumeName,
         edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
      set_jcr_job_status(jcr, JS_DataCommitting);
   } else {
      Jmsg(jcr, M_INFO, 0, _("Writing spooled data to Volume. Despooling %s bytes ...\n"),
         edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
      set_jcr_job_status(jcr, JS_DataDespooling);
   }
   set_jcr_job_status(jcr, JS_DataDespooling);
   dir_send_job_status(jcr);
   dcr->despool_wait = true;
   dcr->spooling = false;
   /*
    * We work with device blocked, but not locked so that
    *  other threads -- e.g. reservations can lock the device
    *  structure.
    */
   dcr->dblock(BST_DESPOOLING);
   dcr->despool_wait = false;
   dcr->despooling = true;

   /*
    * This is really quite kludgy and should be fixed some time.
    * We create a dev structure to read from the spool file
    * in rdev and rdcr.
    */
   rdev = (DEVICE *)malloc(sizeof(DEVICE));
   memset(rdev, 0, sizeof(DEVICE));
   rdev->dev_name = get_memory(strlen(spool_name)+1);
   bstrncpy(rdev->dev_name, spool_name, sizeof(rdev->dev_name));
   rdev->errmsg = get_pool_memory(PM_EMSG);
   *rdev->errmsg = 0;
   rdev->max_block_size = dcr->dev->max_block_size;
   rdev->min_block_size = dcr->dev->min_block_size;
   rdev->device = dcr->dev->device;
   rdcr = new_dcr(jcr, NULL, rdev);
   rdcr->spool_fd = dcr->spool_fd;
   block = dcr->block;                /* save block */
   dcr->block = rdcr->block;          /* make read and write block the same */

   Dmsg1(800, "read/write block size = %d\n", block->buf_len);
   lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */

#if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
   posix_fadvise(rdcr->spool_fd, 0, 0, POSIX_FADV_WILLNEED);
#endif

   /* Add run time, to get current wait time */
   int32_t despool_start = time(NULL) - jcr->run_time;

   set_new_file_parameters(dcr);

   for ( ; ok; ) {
      if (job_canceled(jcr)) {
         ok = false;
         break;
      }
      stat = read_block_from_spool_file(rdcr);
      if (stat == RB_EOT) {
         break;
      } else if (stat == RB_ERROR) {
         ok = false;
         break;
      }
      ok = write_block_to_device(dcr);
      if (!ok) {
         Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
               dcr->dev->print_name(), dcr->dev->bstrerror());
         Dmsg2(000, "Fatal append error on device %s: ERR=%s\n",
               dcr->dev->print_name(), dcr->dev->bstrerror());
      }
      Dmsg3(800, "Write block ok=%d FI=%d LI=%d\n", ok, block->FirstIndex, block->LastIndex);
   }

   if (!dir_create_jobmedia_record(dcr)) {
      Jmsg2(jcr, M_FATAL, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
         dcr->VolCatInfo.VolCatName, jcr->Job);
   }
   /* Set new file/block parameters for current dcr */
   set_new_file_parameters(dcr);

   /*
    * Subtracting run_time give us elapsed time - wait_time since 
    * we started despooling. Note, don't use time_t as it is 32 or 64
    * bits depending on the OS and doesn't edit with %d
    */
   int32_t despool_elapsed = time(NULL) - despool_start - jcr->run_time;

   if (despool_elapsed <= 0) {
      despool_elapsed = 1;
   }

   Jmsg(dcr->jcr, M_INFO, 0, _("Despooling elapsed time = %02d:%02d:%02d, Transfer rate = %s bytes/second\n"),
         despool_elapsed / 3600, despool_elapsed % 3600 / 60, despool_elapsed % 60,
         edit_uint64_with_suffix(jcr->dcr->job_spool_size / despool_elapsed, ec1));

   dcr->block = block;                /* reset block */

   lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
   if (ftruncate(rdcr->spool_fd, 0) != 0) {
      berrno be;
      Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
         be.bstrerror());
      /* Note, try continuing despite ftruncate problem */
   }

   P(mutex);
   if (spool_stats.data_size < dcr->job_spool_size) {
      spool_stats.data_size = 0;
   } else {
      spool_stats.data_size -= dcr->job_spool_size;
   }
   V(mutex);
   P(dcr->dev->spool_mutex);
   dcr->dev->spool_size -= dcr->job_spool_size;
   dcr->job_spool_size = 0;            /* zap size in input dcr */
   V(dcr->dev->spool_mutex);
   free_memory(rdev->dev_name);
   free_pool_memory(rdev->errmsg);
   /* Be careful to NULL the jcr and free rdev after free_dcr() */
   rdcr->jcr = NULL;
   rdcr->dev = NULL;
   free_dcr(rdcr);
   free(rdev);
   dcr->spooling = true;           /* turn on spooling again */
   dcr->despooling = false;

   /* 
    * We are done, so unblock the device, but if we have done a 
    *  commit, leave it locked so that the job cleanup does not
    *  need to wait to release the device (no re-acquire of the lock).
    */
   dcr->dlock();
   unblock_device(dcr->dev);
   /* If doing a commit, leave the device locked -- unlocked in release_device() */
   if (!commit) {
      dcr->dunlock();
   }
   set_jcr_job_status(jcr, JS_Running);
   dir_send_job_status(jcr);
   return ok;
}