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