> The main issue which all proposed solutions share is when
> there's a large array, say, md0, and a small array, say,
> md1, both shares the same set of underlying disks, so md
> subystem will not check/repair them in parallel.  In this
> situation, we will never check md1 if checking md0 takes
> more time than we allow in a month (28 days).

What do you think about suggested above solution
(set sync_force_parallel to 1 during cronjobs)?  This workaround
is implemented in the updated (attached) patch.

See also: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=556610#74

BTW, how bad is in general to set
sync_force_parallel to 1 per default?  (Cc'd to Neil Brown.)

I think, it would be nice to end (not pause) check if it's reached
sync_max.  Perhaps, there is deep reasons why md's interface
doesn't work in this way.  Neil, could you explan this a bit?

> I'll think about it all more.

Any news?
--- /etc/cron.d/mdadm.orig	2013-12-25 19:00:14.000000000 +0400
+++ /etc/cron.d/mdadm	2013-12-25 19:01:50.000000000 +0400
@@ -5,8 +5,7 @@
 # distributed under the terms of the Artistic Licence 2.0
 #
 
-# By default, run at 00:57 on every Sunday, but do nothing unless the day of
-# the month is less than or equal to 7. Thus, only run on the first Sunday of
-# each month. crontab(5) sucks, unfortunately, in this regard; therefore this
-# hack (see #380425).
-57 0 * * 0 root if [ -x /usr/share/mdadm/checkarray ] && [ $(date +\%d) -le 7 ]; then /usr/share/mdadm/checkarray --cron --all --idle --quiet; fi
+# By default, start (or continue unfinished checks) at 00:57
+# and stop (interrupt) checks at 01:57.
+57 0 * * * root [ -x /usr/share/mdadm/checkarray ] && /usr/share/mdadm/checkarray --cron --all --idle --quiet
+57 1 * * * root [ -x /usr/share/mdadm/checkarray ] && /usr/share/mdadm/checkarray --cron --all --idle --quiet --interrupt
--- /usr/share/mdadm/checkarray.orig	2013-01-24 17:26:51.000000000 +0400
+++ /usr/share/mdadm/checkarray	2013-12-25 18:58:56.000000000 +0400
@@ -27,10 +27,12 @@
  -a|--all	check all assembled arrays (ignores arrays in command line).
  -s|--status	print redundancy check status of devices.
  -x|--cancel	queue a request to cancel a running redundancy check.
+  --interrupt   queue a request to interrupt a running redundancy check.
  -i|--idle	perform check in a lowest scheduling class (idle)
  -l|--slow	perform check in a lower-than-standard scheduling class
  -f|--fast	perform check in higher-than-standard scheduling class
  --realtime	perform check in real-time scheduling class (DANGEROUS!)
+ --split n      check next 1/n'th part (n <= 28) of every specified device (override CHECK_SPLIT)
  -c|--cron	honour AUTOCHECK setting in /etc/default/mdadm.
  -q|--quiet	suppress informational messages
 		(use twice to suppress error messages too).
@@ -50,7 +52,7 @@
 }
 
 SHORTOPTS=achVqQsxilf
-LONGOPTS=all,cron,help,version,quiet,real-quiet,status,cancel,idle,slow,fast,realtime
+LONGOPTS=all,cron,help,version,quiet,real-quiet,status,cancel,interrupt,idle,slow,fast,realtime,split:
 
 eval set -- $(getopt -o $SHORTOPTS -l $LONGOPTS -n $PROGNAME -- "$@")
 
@@ -62,20 +64,31 @@
 action=check
 ionice=
 
-for opt in $@; do
-  case "$opt" in
-    -a|--all) all=1;;
-    -s|--status) action=status;;
-    -x|--cancel) action=idle;;
-    -i|--idle) ionice=idle;;
-    -l|--slow) ionice=low;;
-    -f|--fast) ionice=high;;
-    --realtime) ionice=realtime;;
-    -c|--cron) cron=1;;
-    -q|--quiet) quiet=$(($quiet+1));;
-    -Q|--real-quiet) quiet=$(($quiet+2));;	# for compatibility
+while true
+do
+  case "$1" in
+    -a|--all) all=1; shift;;
+    -s|--status) action=status; shift;;
+    -x|--cancel) action=cancel; shift;;
+    --interrupt) action=interrupt; shift;;
+    -i|--idle) ionice=idle; shift;;
+    -l|--slow) ionice=low; shift;;
+    -f|--fast) ionice=high; shift;;
+    --realtime) ionice=realtime; shift;;
+    --split) CHECK_SPLIT=$2; shift 2;;
+    -c|--cron) cron=1; shift;;
+    -q|--quiet) quiet=$(($quiet+1)); shift;;
+    -Q|--real-quiet) quiet=$(($quiet+2)); shift;; # for compatibility
     -h|--help) usage; exit 0;;
     -V|--version) about; exit 0;;
+    --) shift; break;;
+    *) echo "$PROGNAME: E: invalid option: $1.  Try --help." >&2; exit 1;;
+  esac
+done
+
+for opt in $@
+do
+  case $opt in
     /dev/md/*|md/*) arrays="${arrays:+$arrays }md${opt#*md/}";;
     /dev/md*|md*) arrays="${arrays:+$arrays }${opt#/dev/}";;
     /sys/block/md*) arrays="${arrays:+$arrays }${opt#/sys/block/}";;
@@ -99,6 +112,20 @@
   exit 0
 fi
 
+CHECK_SPLIT=${CHECK_SPLIT:-28}
+
+if [ $CHECK_SPLIT -gt 28 ]
+then
+  CHECK_SPLIT=28
+  echo "$PROGNAME: W: CHECK_SPLIT > 28, reset to 28." >&2
+fi
+
+if [ $CHECK_SPLIT -lt 1 ]
+then
+  CHECK_SPLIT=1
+  echo "$PROGNAME: W: CHECK_SPLIT < 1, reset to 1." >&2
+fi
+
 if [ ! -f /proc/mdstat ]; then
   [ $quiet -lt 2 ] && echo "$PROGNAME: E: MD subsystem not loaded, or /proc unavailable." >&2
   exit 2
@@ -159,10 +186,34 @@
     continue
   fi
 
+  chunk_size=$(cat $MDBASE/chunk_size)
+  # set one to safe value if raid level has no chunk_size (e.g., raid 1):
+  [ $chunk_size -lt 1 ] && chunk_size=1
+
+  array_size=$(cat $MDBASE/../size)
+  array_size=$(($array_size >> 1))
+
+  check_size=$(($array_size / $CHECK_SPLIT))
+  # ensure it's of multiple $chunk_size:
+  check_size=$(($check_size - $check_size % $chunk_size))
+  [ $check_size -eq 0 ] && check_size=$chunk_size
+
   case "$action" in
-    idle)
-      echo $action > $MDBASE/sync_action
+    cancel|interrupt)
+      echo 0 > $MDBASE/sync_force_parallel # XXX
+
+      completed=$(cut -d ' ' -f1 $MDBASE/sync_completed)
+      [ $completed = "none" ] && completed=0
+      echo "idle" > $MDBASE/sync_action
       [ $quiet -lt 1 ] && echo "$PROGNAME: I: cancel request queued for array $array." >&2
+
+      if [ "$action" = "cancel" ]
+      then
+        completed=0
+        echo max > $MDBASE/sync_max
+      fi
+      # save sync_min, it must be a multiple of chunk_size:
+      echo $(($completed - $completed%$chunk_size)) > $MDBASE/sync_min
       ;;
 
     check)
@@ -186,6 +237,21 @@
         fi
       fi
 
+      echo 1 > $MDBASE/sync_force_parallel # XXX
+
+      sync_min=$(cat $MDBASE/sync_min)
+      sync_max=$(cat $MDBASE/sync_max)
+
+      [ $sync_max = "max" ] && sync_max=0
+
+      if [ $sync_min -eq $sync_max ]
+      then
+        sync_max=$(($sync_max + $check_size))
+        [ $sync_max -ge $array_size ] && sync_max=$array_size
+
+        echo $sync_max > $MDBASE/sync_max
+      fi
+
       # queue request for the array. The kernel will make sure that these requests
       # are properly queued so as to not kill one of the array.
       echo $action > $MDBASE/sync_action

Reply via email to