Browse Source

s-s-d: Support setting the IO scheduler class and priority

Add new option -I, --iosched <class[:priority]>.

Closes: #443535

Signed-off-by: Guillem Jover <guillem@debian.org>
Chris Coulson 17 years ago
parent
commit
b0569c7d4d
5 changed files with 132 additions and 2 deletions
  1. 23 0
      ChangeLog
  2. 2 1
      configure.ac
  3. 3 0
      debian/changelog
  4. 7 0
      man/start-stop-daemon.8
  5. 97 1
      utils/start-stop-daemon.c

+ 23 - 0
ChangeLog

@@ -1,3 +1,26 @@
+2009-02-26  Chris Coulson  <chrisccoulson@googlemail.com>,
+            Guillem Jover  <guillem@debian.org>
+
+	* configure.ac (AC_CHECK_HEADERS): Add 'sys/syscall.h'.
+	* man/start-stop-daemon.8: Document new option --iosched.
+	* utils/start-stop-daemon.c [HAVE_SYS_SYSCALL_H]: Include
+	<sys/syscall.h>.
+	(HAVE_IOPRIO_SET) [SYS_ioprio_set, linux]: New macro.
+	(IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT): New anonymous enum.
+	(IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE): Likewise.
+	(IOPRIO_WHO_PROCESS, IOPRIO_WHO_PGRP, IOPRIO_WHO_USER): Likewise.
+	(IOPRIO_CLASS_SHIFT, IOPRIO_PRIO_VALUE, IO_SCHED_PRIO_MIN)
+	(IO_SCHED_PRIO_MAX): New macros.
+	(io_sched): New variable.
+	(do_help): Document --iosched.
+	(ioprio_set) [HAVE_IOPRIO_SET]: New function.
+	(parse_io_schedule, set_io_schedule): Likewise.
+	(parse_options): Add 'iosched' to longopts. Add 'I:' to getopt_long
+	call. Handle 'I' as getopt_long return value. Call parse_io_schedule
+	if io_schedule_str is not NULL.
+	(main): Print io_sched values if --test is used. Call set_io_schedule
+	if io_sched is not NULL.
+
 2009-02-26  Romain Francoise <rfrancoise@debian.org>,
             Guillem Jover  <guillem@debian.org>
 

+ 2 - 1
configure.ac

@@ -73,7 +73,8 @@ fi
 
 # Checks for header files.
 AC_HEADER_STDC
-AC_CHECK_HEADERS([stddef.h error.h locale.h libintl.h sys/cdefs.h kvm.h])
+AC_CHECK_HEADERS([stddef.h error.h locale.h libintl.h kvm.h \
+                  sys/cdefs.h sys/syscall.h])
 DPKG_CHECK_DEFINE(TIOCNOTTY, [sys/ioctl.h])
 
 # Checks for typedefs, structures, and compiler characteristics.

+ 3 - 0
debian/changelog

@@ -62,6 +62,9 @@ dpkg (1.15.0) UNRELEASED; urgency=low
   * Print correct feature name on «dpkg --assert-*» failures.
   * Add progress reporting to dpkg while reading the file list database.
     Based on a patch by Romain Francoise.
+  * Add new option --iosched to start-stop-daemon to be able to set the
+    IO scheduling class and priority. Closes: #443535
+    Thanks to Chris Coulson <chrisccoulson@googlemail.com>.
 
   [ Raphael Hertzog ]
   * Enhance dpkg-shlibdeps's error message when a library can't be found to

+ 7 - 0
man/start-stop-daemon.8

@@ -225,6 +225,13 @@ starting it. The priority can be optionally specified by appending a \fB:\fP
 followed by the value. The default \fIpriority\fP is 0. The currently
 supported policy values are \fBother\fP, \fBfifo\fP and \fBrr\fP.
 .TP
+.BR \-I ", " \-\-iosched " \fIclass\fP\fB:\fP\fIpriority\fP"
+This alters the IO scheduler class and priority of the process before starting
+it. The priority can be optionally specified by appending a \fB:\fP followed
+by the value. The default \fIpriority\fP is 4, unless \fIclass\fP is \fBidle\fP,
+then \fIpriority\fP will always be 7. The currently supported values for
+\fIclass\fP are \fBidle\fP, \fBbest-effort\fP and \fBreal-time\fP.
+.TP
 .BR \-k ", " \-\-umask " \fImask\fP"
 This sets the umask of the process before starting it.
 .TP

+ 97 - 1
utils/start-stop-daemon.c

@@ -111,6 +111,27 @@
 #include <error.h>
 #endif
 
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
+
+#if defined(SYS_ioprio_set) && defined(linux)
+#define HAVE_IOPRIO_SET
+#endif
+
+enum {
+	IOPRIO_WHO_PROCESS = 1,
+	IOPRIO_WHO_PGRP,
+	IOPRIO_WHO_USER,
+};
+
+enum {
+	IOPRIO_CLASS_NONE,
+	IOPRIO_CLASS_RT,
+	IOPRIO_CLASS_BE,
+	IOPRIO_CLASS_IDLE,
+};
+
 static int testmode = 0;
 static int quietmode = 0;
 static int exitnodo = 1;
@@ -136,6 +157,11 @@ static const char *progname = "";
 static int nicelevel = 0;
 static int umask_value = -1;
 
+#define IOPRIO_CLASS_SHIFT 13
+#define IOPRIO_PRIO_VALUE(class, prio) (((class) << IOPRIO_CLASS_SHIFT) | (prio))
+#define IO_SCHED_PRIO_MIN 0
+#define IO_SCHED_PRIO_MAX 7
+
 static struct stat exec_stat;
 #if defined(OSHURD)
 static struct proc_stat_list *procset = NULL;
@@ -168,6 +194,7 @@ struct schedule_item {
 };
 
 static struct res_schedule *proc_sched = NULL;
+static struct res_schedule *io_sched = NULL;
 
 static int schedule_length;
 static struct schedule_item *schedule = NULL;
@@ -339,6 +366,8 @@ do_help(void)
 "  -P|--procsched <policy[:prio]>\n"
 "                                use <policy> with <prio> for the kernel\n"
 "                                  process scheduler (default prio is 0)\n"
+"  -I|--iosched <class[:prio]>   use <class> with <prio> to set the IO\n"
+"                                  scheduler (default prio is 4)\n"
 "  -k|--umask <mask>             change the umask to <mask> before starting\n"
 "  -b|--background               force the process to detach\n"
 "  -m|--make-pidfile             create the pidfile before starting\n"
@@ -492,6 +521,40 @@ parse_proc_schedule(const char *string)
 #endif
 }
 
+static void
+parse_io_schedule(const char *string)
+{
+	char *class_str, *prio_str;
+	int prio = 4;
+
+	class_str = xstrdup(string);
+	class_str = strtok(class_str, ":");
+	prio_str = strtok(NULL, ":");
+
+	if (prio_str && parse_integer(prio_str, &prio) != 0)
+		fatal("invalid IO scheduler priority");
+
+	io_sched = xmalloc(sizeof(*io_sched));
+	io_sched->policy_name = class_str;
+
+	if (strcmp(class_str, "real-time") == 0) {
+		io_sched->policy = IOPRIO_CLASS_RT;
+		io_sched->priority = prio;
+	} else if (strcmp(class_str, "best-effort") == 0) {
+		io_sched->policy = IOPRIO_CLASS_BE;
+		io_sched->priority = prio;
+	} else if (strcmp(class_str, "idle") == 0) {
+		io_sched->policy = IOPRIO_CLASS_IDLE;
+		io_sched->priority = 7;
+	} else
+		badusage("invalid IO scheduler policy");
+
+	if (io_sched->priority < IO_SCHED_PRIO_MIN)
+		badusage("IO scheduler priority less than min");
+	if (io_sched->priority > IO_SCHED_PRIO_MAX)
+		badusage("IO scheduler priority greater than max");
+}
+
 static void
 set_proc_schedule(struct res_schedule *sched)
 {
@@ -505,6 +568,26 @@ set_proc_schedule(struct res_schedule *sched)
 #endif
 }
 
+#ifdef HAVE_IOPRIO_SET
+static inline int
+ioprio_set(int which, int who, int ioprio)
+{
+	return syscall(SYS_ioprio_set, which, who, ioprio);
+}
+#endif
+
+static void
+set_io_schedule(struct res_schedule *sched)
+{
+#ifdef HAVE_IOPRIO_SET
+	int io_sched_mask;
+
+	io_sched_mask = IOPRIO_PRIO_VALUE(sched->policy, sched->priority);
+	if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), io_sched_mask) == -1)
+		fatal("Unable to alter IO priority to mask %i", io_sched_mask);
+#endif
+}
+
 static void
 parse_schedule_item(const char *string, struct schedule_item *item)
 {
@@ -607,6 +690,7 @@ parse_options(int argc, char * const *argv)
 		{ "chuid",	  1, NULL, 'c'},
 		{ "nicelevel",	  1, NULL, 'N'},
 		{ "procsched",	  1, NULL, 'P'},
+		{ "iosched",	  1, NULL, 'I'},
 		{ "umask",	  1, NULL, 'k'},
 		{ "background",	  0, NULL, 'b'},
 		{ "make-pidfile", 0, NULL, 'm'},
@@ -618,11 +702,12 @@ parse_options(int argc, char * const *argv)
 	const char *signal_str = NULL;
 	const char *schedule_str = NULL;
 	const char *proc_schedule_str = NULL;
+	const char *io_schedule_str = NULL;
 	int c;
 
 	for (;;) {
 		c = getopt_long(argc, argv,
-		                "HKSVa:n:op:qr:s:tu:vx:c:N:P:k:bmR:g:d:",
+		                "HKSVa:n:op:qr:s:tu:vx:c:N:P:I:k:bmR:g:d:",
 		                longopts, NULL);
 		if (c == -1)
 			break;
@@ -688,6 +773,9 @@ parse_options(int argc, char * const *argv)
 		case 'P':  /* --procsched */
 			proc_schedule_str = optarg;
 			break;
+		case 'I':  /* --iosched */
+			io_schedule_str = optarg;
+			break;
 		case 'k':  /* --umask <mask> */
 			umask_str = optarg;
 			break;
@@ -721,6 +809,9 @@ parse_options(int argc, char * const *argv)
 	if (proc_schedule_str != NULL)
 		parse_proc_schedule(proc_schedule_str);
 
+	if (io_schedule_str != NULL)
+		parse_io_schedule(io_schedule_str);
+
 	if (umask_str != NULL) {
 		if (parse_umask(umask_str, &umask_value) != 0)
 			badusage("umask value must be a positive number");
@@ -1410,6 +1501,9 @@ main(int argc, char **argv)
 		if (proc_sched)
 			printf(", with scheduling policy %s with priority %i",
 			       proc_sched->policy_name, proc_sched->priority);
+		if (io_sched)
+			printf(", with IO scheduling class %s with priority %i",
+			       io_sched->policy_name, io_sched->priority);
 		printf(".\n");
 	}
 	if (testmode)
@@ -1433,6 +1527,8 @@ main(int argc, char **argv)
 	}
 	if (proc_sched)
 		set_proc_schedule(proc_sched);
+	if (io_sched)
+		set_io_schedule(io_sched);
 	if (umask_value >= 0)
 		umask(umask_value);
 	if (mpidfile && pidfile != NULL) {