Imported Upstream version 3.3.3
[debian/amanda] / server-src / driver.c
index af5095ab93a8715c3a50fee552bcb1c1aca19b89..38ebb331538a02842760ae6ae9a7016d3d695767 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
  * Copyright (c) 1991-1998 University of Maryland at College Park
+ * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
  * All Rights Reserved.
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
@@ -142,6 +143,7 @@ static void start_some_dumps(disklist_t *rq);
 static void continue_port_dumps(void);
 static void update_failed_dump(disk_t *);
 static int no_taper_flushing(void);
+static int active_dumper(void);
 
 typedef enum {
     TAPE_ACTION_NO_ACTION         = 0,
@@ -187,7 +189,6 @@ main(
     identlist_t    il;
     unsigned long reserve = 100;
     char *conf_diskfile;
-    char **result_argv = NULL;
     char *taper_program;
     char *conf_tapetype;
     tapetype_t *tape;
@@ -585,8 +586,6 @@ main(
 
     amfree(dumper_program);
     amfree(taper_program);
-    if (result_argv)
-       g_strfreev(result_argv);
 
     dbclose();
 
@@ -1063,7 +1062,7 @@ allow_dump_dle(
                enqueue_disk(&directq, diskp);
                diskp->to_holdingdisk = HOLD_NEVER;
            }
-           if (empty(*rq)) force_flush = 1;
+           if (empty(*rq) && active_dumper() == 0) { force_flush = 1;}
        }
     } else if (client_constrained(diskp)) {
        free_assignedhd(holdp);
@@ -1346,8 +1345,8 @@ start_some_dumps(
                                   sched(diskp)->level);
                dumper_cmd(dumper, PORT_DUMP, diskp, NULL);
            }
-           diskp->host->start_t = now + 15;
-           if (empty(*rq)) force_flush = 1;
+           diskp->host->start_t = now + 5;
+           if (empty(*rq) && active_dumper() == 0) { force_flush = 1;}
 
            if (result_argv)
                g_strfreev(result_argv);
@@ -1390,7 +1389,7 @@ start_some_dumps(
            taper_nb_wait_reply++;
            taper_cmd(PORT_WRITE, diskp, NULL, sched(diskp)->level,
                      sched(diskp)->datestamp);
-           diskp->host->start_t = now + 15;
+           diskp->host->start_t = now + 5;
 
            state_changed = TRUE;
        }
@@ -1445,6 +1444,31 @@ start_degraded_mode(
     disklist_t newq;
     off_t est_full_size;
     char *qname;
+    taper_t *taper;
+
+    if (need_degraded == 0) {
+       for(taper = tapetable;
+           taper < tapetable+conf_taper_parallel_write;
+           taper++) {
+           if (!(taper->state & TAPER_STATE_DONE))
+               return;
+       }
+       need_degraded = 1;
+    }
+
+    if (!schedule_done || degraded_mode) {
+       return;
+    }
+
+    if (need_degraded == 0) {
+       for(taper = tapetable;
+           taper < tapetable+conf_taper_parallel_write;
+           taper++) {
+           if (!(taper->state & TAPER_STATE_DONE))
+               return;
+       }
+       need_degraded = 1;
+    }
 
     newq.head = newq.tail = 0;
 
@@ -1649,7 +1673,8 @@ handle_taper_result(
                amfree(qname);
                break;
            }
-           if (strcmp(result_argv[3], "TAPE-ERROR") == 0) {
+           if (strcmp(result_argv[3], "TAPE-ERROR") == 0 ||
+               strcmp(result_argv[3], "TAPE-CONFIG") == 0) {
                taper->state &= ~TAPER_STATE_TAPE_STARTED;
                taper->tape_error = newstralloc(taper->tape_error, result_argv[5]);
                taper->result = FAILED;
@@ -1705,7 +1730,8 @@ handle_taper_result(
                amfree(qname);
                break;
            }
-           if (strcmp(result_argv[3], "TAPE-ERROR") == 0) {
+           if (strcmp(result_argv[3], "TAPE-ERROR") == 0 ||
+               strcmp(result_argv[3], "TAPE-CONFIG") == 0) {
                taper->state &= ~TAPER_STATE_TAPE_STARTED;
                taper->tape_error = newstralloc(taper->tape_error, result_argv[6]);
                taper->result = FAILED;
@@ -1726,12 +1752,12 @@ handle_taper_result(
            s = strstr(result_argv[4], " kb ");
            if (s) {
                s += 4;
-               sched(dp)->dumpsize = atol(s);
+               sched(dp)->dumpsize = OFF_T_ATOI(s);
            } else {
                s = strstr(result_argv[4], " bytes ");
                if (s) {
                    s += 7;
-                   sched(dp)->dumpsize = atol(s)/1024;
+                   sched(dp)->dumpsize = OFF_T_ATOI(s)/1024;
                }
            }
 
@@ -1762,12 +1788,12 @@ handle_taper_result(
            s = strstr(result_argv[5], " kb ");
            if (s) {
                s += 4;
-               partsize = atol(s);
+               partsize = OFF_T_ATOI(s);
            } else {
                s = strstr(result_argv[5], " bytes ");
                if (s) {
                    s += 7;
-                   partsize = atol(s)/1024;
+                   partsize = OFF_T_ATOI(s)/1024;
                }
            }
            taper->left -= partsize;
@@ -1871,6 +1897,7 @@ handle_taper_result(
            if (strcmp(result_argv[1], "SETUP") == 0) {
                taper_nb_wait_reply = 0;
                taper_nb_scan_volume = 0;
+               need_degraded = 1;
            } else {
                taper = taper_from_name(result_argv[1]);
                taper->state = TAPER_STATE_DONE;
@@ -1887,13 +1914,11 @@ handle_taper_result(
                taper_nb_scan_volume--;
            }
            if (taper_nb_wait_reply == 0) {
+               need_degraded = 1;
                event_release(taper_ev_read);
                taper_ev_read = NULL;
            }
-           need_degraded = 1;
-           if (schedule_done && !degraded_mode) {
-               start_degraded_mode(&runq);
-           }
+           start_degraded_mode(&runq);
            start_some_dumps(&runq);
            break;
 
@@ -1921,7 +1946,7 @@ handle_taper_result(
                               sched(dp)->level);
            /* tell the dumper to dump to a port */
            dumper_cmd(dumper, PORT_DUMP, dp, NULL);
-           dp->host->start_t = time(NULL) + 15;
+           dp->host->start_t = time(NULL) + 5;
            amfree(dp->dataport_list);
 
            taper->state |= TAPER_STATE_DUMP_TO_TAPE;
@@ -1961,7 +1986,6 @@ handle_taper_result(
                        file_taper_result(taper->disk);
                    }
                }
-
            }
            taper = NULL;
 
@@ -1970,6 +1994,7 @@ handle_taper_result(
                 taper_ev_read = NULL;
                taper_nb_wait_reply = 0;
             }
+           need_degraded = 1;
            start_degraded_mode(&runq);
             tapeq.head = tapeq.tail = NULL;
             aclose(taper_fd);
@@ -3192,8 +3217,8 @@ read_schedule(
     amfree(inpline);
     if(line == 0)
        log_add(L_WARNING, _("WARNING: got empty schedule from planner"));
-    if(need_degraded==1) start_degraded_mode(&runq);
     schedule_done = 1;
+    start_degraded_mode(&runq);
     run_server_global_scripts(EXECUTE_ON_PRE_BACKUP, get_config_name());
     if (empty(runq)) force_flush = 1;
     start_some_dumps(&runq);
@@ -3742,13 +3767,17 @@ tape_action(
     int   dump_to_disk_terminated;
     int   nb_taper_active = nb_sent_new_tape;
     int   nb_taper_flushing = 0;
-    int   dle_free = 0;
+    int   dle_free = 0;                /* number of dle that fit on started tape */
+    int   new_dle = 0;         /* number of dle that doesn't fit on started tape */
+    off_t new_data = 0;                /* size of dle that doesn't fit on started tape */
     off_t data_next_tape = 0;
     off_t data_free = 0;
     off_t data_lost = 0;
     off_t data_lost_next_tape = 0;
-    gboolean allow_size_or_number;
+    gboolean taperflush_criteria;
+    gboolean flush_criteria;
 
+    driver_debug(2, "tape_action: ENTER %p\n", taper);
     dumpers_size = 0;
     for(dumper = dmptable; dumper < (dmptable+inparallel); dumper++) {
        if (dumper->busy && !sched(dumper->dp)->taper)
@@ -3782,35 +3811,68 @@ tape_action(
     }
 
     /* Add what is currently written to tape and in the go. */
+    new_data = 0;
     for (taper1 = tapetable; taper1 < tapetable+conf_taper_parallel_write;
         taper1++) {
        if (taper1->state & TAPER_STATE_TAPE_STARTED) {
-           tapeq_size -= taper1->left;
+           if (taper1->nb_dle < conf_max_dle_by_volume) {
+               tapeq_size -= taper1->left;
+           }
            dle_free += (conf_max_dle_by_volume - taper1->nb_dle);
        }
        if (taper1->disk) {
            off_t data_to_go;
+           off_t t_size;
            if (taper1->dumper) {
-               data_to_go = sched(taper1->disk)->est_size - taper1->written;
+               t_size = sched(taper1->disk)->est_size;
            } else {
-               data_to_go = sched(taper1->disk)->act_size - taper1->written;
+               t_size = sched(taper1->disk)->act_size;
            }
+           data_to_go =  t_size - taper1->written;
            if (data_to_go > taper1->left) {
                data_next_tape += data_to_go - taper1->left;
                data_lost += taper1->written + taper1->left;
                if (taper1->state & TAPER_STATE_TAPE_STARTED) {
-                   dle_free--;
+                   dle_free -= (conf_max_dle_by_volume - taper1->nb_dle) + 1;
                } else {
-                   dle_free += conf_max_dle_by_volume - 2;
+                   dle_free -= 2;
+                   new_data += t_size;
                }
            } else {
+               if (!(taper1->state & TAPER_STATE_TAPE_STARTED)) {
+                   dle_free--;
+                   new_data += t_size;
+               }
                data_free += taper1->left - data_to_go;
            }
            tapeq_size += data_to_go;
        }
     }
-    data_lost_next_tape = tape_length + data_free - data_next_tape - runq_size - directq_size - tapeq_size;
+
+    new_dle = queue_length(tapeq) - dle_free;
     driver_debug(2, _("dle_free: %d\n"), dle_free);
+    driver_debug(2, _("new_dle: %d\n"), new_dle);
+    if (new_dle > 0) {
+       if (taperflush == 0 &&
+           flush_threshold_dumped == 0 &&
+           flush_threshold_scheduled == 0) {
+           /* shortcut, will trigger taperflush_criteria and/or flush_criteria */
+           new_data += 1;
+       } else {
+           /* sum the size of the first new-dle in tapeq */
+           /* they should be the reverse taperalgo       */
+           for (dp = tapeq.head;
+                dp != NULL && new_dle > 0;
+                dp = dp->next, new_dle--) {
+               new_data += sched(dp)->act_size;
+           }
+       }
+       if (tapeq_size < new_data) {
+           tapeq_size = new_data;
+       }
+    }
+    driver_debug(2, _("new_data: %lld\n"), (long long)new_data);
+    data_lost_next_tape = tape_length + data_free - data_next_tape - runq_size - directq_size - tapeq_size;
     driver_debug(2, _("data_lost: %lld\n"), (long long)data_lost);
     driver_debug(2, _("data_free: %lld\n"), (long long)data_free);
     driver_debug(2, _("data_next_tape: %lld\n"), (long long)data_next_tape);
@@ -3833,18 +3895,25 @@ tape_action(
        }
     }
 
-    allow_size_or_number = (flush_threshold_dumped < tapeq_size &&
-                           flush_threshold_scheduled < sched_size) ||
-                          (dle_free < (queue_length(runq) +
-                                       queue_length(directq) +
-                                       queue_length(tapeq)));
+    taperflush_criteria = (taperflush < tapeq_size &&
+                          (force_flush == 1 || dump_to_disk_terminated));
+    flush_criteria = (flush_threshold_dumped < tapeq_size &&
+                     flush_threshold_scheduled < sched_size) ||
+                    taperflush_criteria;
+
+    driver_debug(2, "taperflush %lld\n", (long long)taperflush);
+    driver_debug(2, "flush_threshold_dumped %lld\n", (long long)flush_threshold_dumped);
+    driver_debug(2, "flush_threshold_scheduled %lld\n", (long long)flush_threshold_scheduled);
+    driver_debug(2, "force_flush %d\n", force_flush);
+    driver_debug(2, "dump_to_disk_terminated %d\n", dump_to_disk_terminated);
     driver_debug(2, "queue_length(runq) %d\n", queue_length(runq));
     driver_debug(2, "queue_length(directq) %d\n", queue_length(directq));
     driver_debug(2, "queue_length(tapeq) %d\n", queue_length(tapeq));
-    driver_debug(2, "allow_size_or_number %d\n", allow_size_or_number);
+    driver_debug(2, "taperflush_criteria %d\n", taperflush_criteria);
+    driver_debug(2, "flush_criteria %d\n", flush_criteria);
 
     // Changing conditionals can produce a driver hang, take care.
-    // 
+    //
     // when to start writting to a new tape
     if (taper->state & TAPER_STATE_TAPE_REQUESTED) {
        driver_debug(2, "tape_action: TAPER_STATE_TAPE_REQUESTED\n");
@@ -3852,16 +3921,19 @@ tape_action(
            nb_taper_active == 0) {
            *why_no_new_tape = g_strdup_printf(_("%d tapes filled; runtapes=%d "
                "does not allow additional tapes"), current_tape, conf_runtapes);
+           driver_debug(2, "tape_action: TAPER_STATE_TAPE_REQUESTED return TAPE_ACTION_NO_NEW_TAPE\n");
            result |= TAPE_ACTION_NO_NEW_TAPE;
        } else if (current_tape < conf_runtapes &&
                   taper_nb_scan_volume == 0 &&
-                  (allow_size_or_number ||
+                  (flush_criteria ||
                    (data_lost > data_lost_next_tape) ||
                    nb_taper_active == 0) &&
                   (last_started_taper == NULL ||
                    last_started_taper == taper)) {
+           driver_debug(2, "tape_action: TAPER_STATE_TAPE_REQUESTED return TAPE_ACTION_SCAN\n");
            result |= TAPE_ACTION_SCAN;
        } else {
+           driver_debug(2, "tape_action: TAPER_STATE_TAPE_REQUESTED return TAPE_ACTION_MOVE\n");
            result |= TAPE_ACTION_MOVE;
        }
     } else if ((taper->state & TAPER_STATE_WAIT_FOR_TAPE) &&
@@ -3869,11 +3941,8 @@ tape_action(
         !empty(directq) ||                             // if a dle is waiting for a dump to tape
          !empty(roomq) ||                              // holding disk constraint
          idle_reason == IDLE_NO_DISKSPACE ||           // holding disk constraint
-        allow_size_or_number ||
-        (data_lost > data_lost_next_tape) ||
-        (taperflush < tapeq_size &&                    // taperflush
-         (force_flush == 1 ||                          //  if force_flush
-          dump_to_disk_terminated))                    //  or all dump to disk terminated
+        flush_criteria ||                              // flush criteria
+        data_lost > data_lost_next_tape
        )) {
        driver_debug(2, "tape_action: TAPER_STATE_WAIT_FOR_TAPE return TAPE_ACTION_NEW_TAPE\n");
        result |= TAPE_ACTION_NEW_TAPE;
@@ -3905,14 +3974,17 @@ tape_action(
            (taper->state & TAPER_STATE_TAPE_STARTED ||         // tape already started 
              !empty(roomq) ||                                  // holding disk constraint
              idle_reason == IDLE_NO_DISKSPACE ||               // holding disk constraint
-            allow_size_or_number ||
-             (force_flush == 1 && taperflush < tapeq_size))) { // taperflush if force_flush
+            flush_criteria)) {                                 // flush
 
            if (nb_taper_flushing == 0) {
+               driver_debug(2, "tape_action: TAPER_STATE_IDLE return TAPE_ACTION_START_A_FLUSH\n");
                result |= TAPE_ACTION_START_A_FLUSH;
            } else {
+               driver_debug(2, "tape_action: TAPER_STATE_IDLE return TAPE_ACTION_START_A_FLUSH_FIT\n");
                result |= TAPE_ACTION_START_A_FLUSH_FIT;
            }
+       } else {
+           driver_debug(2, "tape_action: TAPER_STATE_IDLE return TAPE_ACTION_NO_ACTION\n");
        }
     }
     return result;
@@ -3931,6 +4003,14 @@ no_taper_flushing(void)
     return 1;
 }
 
+static int
+active_dumper(void)
+{
+    int i, nidle=0;
+
+    for(i = 0; i < inparallel; i++) if(!dmptable[i].busy) nidle++;
+    return inparallel - nidle;
+}
 #if 0
 static void
 dump_state(