[PATCH 1 of 2] fix sched_setaffinity(2) call failure

Maxim Dounin mdounin at mdounin.ru
Fri Jan 13 18:16:51 UTC 2012


Hello!

On Thu, Jan 12, 2012 at 11:37:00PM +0800, Joshua Zhu wrote:

> Hi,
> 
> 'cpu_set_t' should be used instead of 'long' when calling
> sched_setaffinity(2). Otherwise it may fail on some Linux systems.
> Please see the attachment for more detail.

According to sched_setaffinity(2) at least in kernels >= 2.6.9 it 
should be ok to use any length as long as bits above kernel mask 
used aren't referenced.

I believe the real problem is that code uses 32 as cpusetsize, 
while this is the size in bytes.  Hence it references arbitrary 
memory and this causes EINVAL to be returned by glibc if there are 
bits referenced above kernel cpumask size, see 
sysdeps/unix/sysv/linux/sched_setaffinity.c in glibc's sources.

The following patch should be enough:

diff -r e1296af53cc0 src/os/unix/ngx_process_cycle.c
--- a/src/os/unix/ngx_process_cycle.c	Mon Dec 26 00:00:00 2011 +0400
+++ b/src/os/unix/ngx_process_cycle.c	Fri Jan 13 11:58:01 2012 -0500
@@ -914,7 +914,10 @@
         ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
                       "sched_setaffinity(0x%08Xl)", cpu_affinity);
 
-        if (sched_setaffinity(0, 32, (cpu_set_t *) &cpu_affinity) == -1) {
+        if (sched_setaffinity(0, sizeof(cpu_affinity),
+                              (cpu_set_t *) &cpu_affinity)
+            == -1)
+        {
             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                           "sched_setaffinity(0x%08Xl) failed", cpu_affinity);
         }

Could you please test if it works for you?

It passes my limited testing, but I wasn't really able to 
reproduce the problem without modifying the sched_setaffinity() 
call in question to use bigger cpusetsize.

[...]

> diff -uprN nginx-1.1.12/src/core/nginx.c nginx-1.1.12-bugfix/src/core/nginx.c
> --- nginx-1.1.12/src/core/nginx.c	2011-11-16 04:35:42.000000000 +0800
> +++ nginx-1.1.12-bugfix/src/core/nginx.c	2012-01-12 11:06:00.000000000 +0800
> @@ -1245,15 +1245,15 @@ ngx_set_cpu_affinity(ngx_conf_t *cf, ngx
>      ngx_core_conf_t  *ccf = conf;
>  
>      u_char            ch;
> -    u_long           *mask;
> +    cpu_set_t        *mask;
>      ngx_str_t        *value;
> -    ngx_uint_t        i, n;
> +    ngx_uint_t        i, j, n;
>  
>      if (ccf->cpu_affinity) {
>          return "is duplicate";
>      }
>  
> -    mask = ngx_palloc(cf->pool, (cf->args->nelts - 1) * sizeof(long));
> +    mask = ngx_palloc(cf->pool, (cf->args->nelts - 1) * sizeof(cpu_set_t));
>      if (mask == NULL) {
>          return NGX_CONF_ERROR;
>      }

[...]

While I generally agree that using cpu_set_t and CPU_* macros 
explicitly would be better, I don't think we want to introduce 
linux-centric code here in platform-independent code.

Instead we probably want to leave this code generic enough to be 
useable with other scheduler interfaces, and convert the data 
somewhere near platform-specific whatever_setaffinity() calls if 
needed.  (Likely unneeded for the Linux with the above patch.)

Maxim Dounin



More information about the nginx mailing list