patch for rsync: provides "nice = N" option

Steve Friedl steve at unixwiz.net
Tue Oct 9 14:05:08 GMT 2007


Hi folks,

This is a patch for rsync 2.6.9 - I'm not sure if this is the proper vehicle
for submitting them. It's been working great for us for months, and I hope
it's seen as offering useful utility in general.

Steve (who is not a subscriber to this list)

--- 
Stephen J Friedl | Security Consultant |  UNIX Wizard  |   +1 714 544-6561
www.unixwiz.net  | Tustin, Calif. USA  | Microsoft MVP | steve at unixwiz.net

----------------------------------------------------------------------------

Date:	2007/10/09
By:	Stephen J. Friedl
	steve at unixwiz.net

Purpose:
	This patch introduces a "nice = <N>" variable in the rsyncd.conf
	file so that the daemon will raise its nice value (lower CPU
	priority) by the given integer delta for a given rsync module.

Background:

	When doing full-system backups with rsync across the data center,
	we use several techniques to avoid swamping the backed up machine
	when the rsync kicks in. We set a --bwlimit to cut down on the I/O,
	but we also wanted to lower the CPU priority as well.

	We've had real problems with the rsync backups adversely affecting
	production workloads, and though it's probably possible to build
	some kind of wrapper that nices the process priority up before
	execing the real daemon, that would apply the nice to all modules
	served by that daemon. That's not what we wanted.

Using this:

	In an rsyncd.conf:

		[fullbackup]
			path = /
			...
			nice = 10

	This *adds* 10 to the current nice, which *lowers* the CPU priority.
	Though the nice(2) system call can take negative numbers to lower
	the nice/raise the priority, that's disallowed here. We only allow
	lowering of the CPU priority, never raising it.

The patch:

	This was a pretty easy patch to make: I found where the variables
	were defined and simply added a new integral one in the pattern
	of the timeout variable.

	Not being terribly familiar with the internals of rsync, I was
	not absolutely sure that rsync forked a child for each client
	request, and since nice(2) is additive, doing this in the wrong
	place means that each client gets lower and lower priority.

	So I put the call right after the chroot(2) call - I was sure that
	*this* was not in a loop, and I wouldn't have to worry about it.

	The nice(2) system call has different return semantics across
	different operating systems (Linux returns 0 on success, BSD
	returns the new nice value), and there's no real point in checking
	for success or failure: nice() with positive values is supposed
	to work for everybody anyway.

	It might be possible to support *raising* CPU priority (via a
	negative nice), but this seems like an area rife with trouble
	for no real benefit. We'd have to coordinate lowering the nice
	before giving up root permissions, check for errors, probably
	get it wrong sometimes, and it seems so much easier to allow only
	the behavior which the patch intended to add anyway: lower the
	priority.

	The only thing I'm not completely sure about is how to best do
	logging. I have two calls to rprint(FLOG, ...): one is for an
	error condition (providing a negative nice), and the other is
	just an info notice.

	rprintf(FLOG, "WARNING: nice(%d) not allowed (must be positive) on module %s\n",
		niceval, name);

	rprintf(FLOG, "rsync set nice(%d) on module %s\n", niceval, name);

	The error should not be fatal, and neither of these should be routed
	to the client.

	This very patch has been in use on a dozen machines (Linux and
	FreeBSD) for more than two months, and we've verified that it really
	does increase the nice as requested.

--- clientserver.c.orig	2007-08-19 07:36:59.000000000 +0000
+++ clientserver.c	2007-08-20 00:47:10.000000000 +0000
@@ -283,6 +283,7 @@
 	int ret, pre_exec_fd = -1;
 	pid_t pre_exec_pid = 0;
 	char *request = NULL;
+	int niceval;
 
 	if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) {
 		rprintf(FLOG, "rsync denied on module %s from %s (%s)\n",
@@ -559,6 +560,29 @@
 		am_root = (MY_UID() == 0);
 	}
 
+	/* If the user provided a nice increment to lower the priority,
+	 * do it here. Negative nice not allowed, and we rely on the OS
+	 * to quietly limit the maximum nice (no point in us vetting it).
+	 *
+	 * The return value from nice(2) varies across operating systems
+	 * (linux has 0=success, BSD return new nice value), so there is
+	 * no point in checking for errors.
+	 */
+	if ( (niceval = lp_nice(i)) > 0 )
+	{
+		rprintf(FLOG, "rsync set nice(%d) on module %s\n", niceval, name);
+
+		nice(niceval);
+	}
+	else if ( niceval < 0 )
+	{
+		rprintf(FLOG, "WARNING: nice(%d) not allowed (must be positive) on module %s\n",
+			niceval, name);
+
+		/* but we keep going */
+	}
+
+
 	if (lp_temp_dir(i) && *lp_temp_dir(i)) {
 		tmpdir = lp_temp_dir(i);
 		if (strlen(tmpdir) >= MAXPATHLEN - 10) {
--- loadparm.c.orig	2007-08-19 07:28:25.000000000 +0000
+++ loadparm.c	2007-08-19 07:39:00.000000000 +0000
@@ -149,6 +149,7 @@
 	int max_verbosity;
 	int syslog_facility;
 	int timeout;
+	int niceval;
 
 	BOOL ignore_errors;
 	BOOL ignore_nonreadable;
@@ -196,6 +197,7 @@
  /* max_verbosity; */		1,
  /* syslog_facility; */		LOG_DAEMON,
  /* timeout; */			0,
+ /* nice; */ 			0,
 
  /* ignore_errors; */		False,
  /* ignore_nonreadable; */	False,
@@ -327,6 +329,7 @@
  {"syslog facility",   P_ENUM,   P_LOCAL, &sDefault.syslog_facility,enum_facilities,0},
  {"temp dir",          P_PATH,   P_LOCAL, &sDefault.temp_dir,          NULL,0},
  {"timeout",           P_INTEGER,P_LOCAL, &sDefault.timeout,           NULL,0},
+ {"nice",              P_INTEGER,P_LOCAL, &sDefault.niceval,           NULL,0},
  {"transfer logging",  P_BOOL,   P_LOCAL, &sDefault.transfer_logging,  NULL,0},
  {"uid",               P_STRING, P_LOCAL, &sDefault.uid,               NULL,0},
  {"use chroot",        P_BOOL,   P_LOCAL, &sDefault.use_chroot,        NULL,0},
@@ -411,6 +414,7 @@
 FN_LOCAL_INTEGER(lp_max_connections, max_connections)
 FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
 FN_LOCAL_INTEGER(lp_timeout, timeout)
+FN_LOCAL_INTEGER(lp_nice, niceval)
 
 FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
 FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
--- proto.h.orig	2007-08-19 07:41:19.000000000 +0000
+++ proto.h	2007-08-19 07:42:12.000000000 +0000
@@ -173,6 +173,7 @@
 int lp_max_connections(int );
 int lp_max_verbosity(int );
 int lp_timeout(int );
+int lp_nice(int );
 BOOL lp_ignore_errors(int );
 BOOL lp_ignore_nonreadable(int );
 BOOL lp_list(int );
--- rsyncd.conf.5.orig	2007-08-19 08:02:16.000000000 +0000
+++ rsyncd.conf.5	2007-08-20 00:31:23.000000000 +0000
@@ -573,6 +573,13 @@
 of the patterns will not be compressed during transfer\&.
 .IP 
 The default setting is \f(CW*\&.gz *\&.tgz *\&.zip *\&.z *\&.rpm *\&.deb *\&.iso *\&.bz2 *\&.tbz\fP
+.IP
+.IP "\fBnice\fP
+This provides a positive nice(2) value that lowers the CPU priority of the
+rsyncd process when servicing this module. This is useful for backups across
+the network that are not time critical, and the lowered priority can help
+avoid swamping a busy machine. Negative nice (to increase priority) are 
+specifically disallowed here.
 .IP 
 .IP "\fBpre-xfer exec\fP, \fBpost-xfer exec\fP"
 You may specify a command to be run
@@ -686,6 +693,12 @@
         auth users = tridge, susan
         secrets file = /etc/rsyncd\&.secrets
 
+[fullbackup]
+        path = /
+        read only = yes
+        nice = 5
+        comment = Full system backup
+
 .fi 
 
 .PP 


More information about the rsync mailing list