sleepy_penguin RubyGem user+dev discussion/patches/pulls/bugs/help
 help / color / mirror / code / Atom feed
* [sleepy.penguin] [PATCH] epoll: avoid EPOLL_CTL_MOD race condition
@ 2013-01-03  4:19 Eric Wong
  2013-01-17 21:35 ` Eric Wong
  0 siblings, 1 reply; 2+ messages in thread
From: Eric Wong @ 2013-01-03  4:19 UTC (permalink / raw)
  To: sleepy.penguin

Until everybody updates to a version of the Linux kernel with
this fix, we need to enable a workaround for older kernels using
EPOLL_CTL_DEL + EPOLL_CTL_ADD to emulate EPOLL_CTL_MOD behavior.

This race condition is fixed in Linux upstream
commit 128dd1759d96ad36c379240f8b9463e8acfd37a1 and included in
Linux v3.8-rc2 and later.

This fix will likely be backported to stable and vendor kernels,
so we'll whitelist them as we learn of them.
---
 Pushed to git://bogomips.org/sleepy_penguin.git
 commit 02e5a91b24983d96b342c007661966495ccdb619

 ext/sleepy_penguin/epoll.c       | 90 ++++++++++++++++++++++++++++++++++++++--
 test/test_epoll.rb               |  5 ++-
 test/test_epoll_optimizations.rb | 37 ++++++++++++-----
 3 files changed, 118 insertions(+), 14 deletions(-)

diff --git a/ext/sleepy_penguin/epoll.c b/ext/sleepy_penguin/epoll.c
index a507b0c..60a34be 100644
--- a/ext/sleepy_penguin/epoll.c
+++ b/ext/sleepy_penguin/epoll.c
@@ -2,6 +2,9 @@
 #include <sys/epoll.h>
 #include <pthread.h>
 #include <time.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/utsname.h>
 #include "missing_epoll.h"
 #ifdef HAVE_RUBY_ST_H
 #  include <ruby/st.h>
@@ -195,6 +198,75 @@ static VALUE init(int argc, VALUE *argv, VALUE self)
 	return self;
 }
 
+static int epoll_ctl_mod_racy;
+static void epoll_ctl_mod_racy_detect(void)
+{
+	struct utsname buf;
+	int rc;
+	unsigned version, patchlevel, sublevel;
+
+	/*
+	 * Online/current processors for this process is not enough,
+	 * we need all processors since events may be triggered
+	 * by interrupt handlers on any CPU in the system
+	 */
+	long nproc = sysconf(_SC_NPROCESSORS_CONF);
+
+	/* Eric Wong's personal machines are ancient and weak: */
+	if (nproc == 1)
+		return;
+
+	if (uname(&buf) != 0)
+		rb_sys_fail("uname");
+
+	/* who knows, maybe there'll be an epoll on other OSes one day */
+	if (strcmp(buf.sysname, "Linux"))
+		return;
+
+	rc = sscanf(buf.release, "%u.%u.%u", &version, &patchlevel, &sublevel);
+	if (rc != 3) {
+		rb_warn("sscanf failed to parse kernel version: %s (rc=%d), "
+			"assuming EPOLL_CTL_MOD is racy on SMP",
+			buf.release, rc);
+		epoll_ctl_mod_racy = 1;
+		return;
+	}
+
+	/*
+	 * TODO: whitelist vendor kernels as fixes are backported
+	 * TODO: It is likely 3.7.2 (or 3.7.3) will have this
+	 *       fix backported.
+	 */
+	if (version <= 2) {
+		epoll_ctl_mod_racy = 1;
+	} else if (version == 3) {
+		if (patchlevel <= 7)
+			epoll_ctl_mod_racy = 1;
+	}
+}
+
+/*
+ * EPOLL_CTL_MOD is racy under Linux <= 3.7.1 on SMP systems,
+ * so we emulate it with EPOLL_CTL_ADD + EPOLL_CTL_DELETE
+ * This is fixed in Linux commit 128dd1759d96ad36c379240f8b9463e8acfd37a1
+ */
+static int
+fake_epoll_ctl_mod(int epfd, int fd, struct epoll_event *event)
+{
+	int rc = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, event);
+	if (rc == 0)
+		rc = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, event);
+	return rc;
+}
+
+static int my_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
+{
+	if (epoll_ctl_mod_racy && op == EPOLL_CTL_MOD)
+		return fake_epoll_ctl_mod(epfd, fd, event);
+	else
+		return epoll_ctl(epfd, op, fd, event);
+}
+
 static VALUE ctl(VALUE self, VALUE io, VALUE flags, int op)
 {
 	struct epoll_event event;
@@ -206,11 +278,11 @@ static VALUE ctl(VALUE self, VALUE io, VALUE flags, int op)
 	event.events = rb_sp_get_uflags(self, flags);
 	pack_event_data(&event, io);
 
-	rv = epoll_ctl(ep->fd, op, fd, &event);
+	rv = my_epoll_ctl(ep->fd, op, fd, &event);
 	if (rv == -1) {
 		if (errno == ENOMEM) {
 			rb_gc();
-			rv = epoll_ctl(ep->fd, op, fd, &event);
+			rv = my_epoll_ctl(ep->fd, op, fd, &event);
 		}
 		if (rv == -1)
 			rb_sys_fail("epoll_ctl");
@@ -269,7 +341,7 @@ static VALUE set(VALUE self, VALUE io, VALUE flags)
 			return Qnil;
 
 fallback_mod:
-		rv = epoll_ctl(ep->fd, EPOLL_CTL_MOD, fd, &event);
+		rv = my_epoll_ctl(ep->fd, EPOLL_CTL_MOD, fd, &event);
 		if (rv == -1) {
 			if (errno != ENOENT)
 				rb_sys_fail("epoll_ctl - mod");
@@ -786,4 +858,16 @@ void sleepy_penguin_init_epoll(void)
 	rb_define_const(cEpoll, "ONESHOT", UINT2NUM(EPOLLONESHOT));
 
 	id_for_fd = rb_intern("for_fd");
+
+	epoll_ctl_mod_racy_detect();
+
+
+	/*
+	 * true if EPOLL_CTL_MOD is racy on your version of Linux
+	 * (anything before 3.8) under SMP.
+	 * Please report patched vendor kernels to
+	 * mailto:sleepy.penguin@librelist.org
+	 */
+	rb_define_const(cEpoll, "EPOLL_CTL_MOD_RACY",
+                        epoll_ctl_mod_racy ? Qtrue : Qfalse);
 }
diff --git a/test/test_epoll.rb b/test/test_epoll.rb
index fd22654..1eb987e 100644
--- a/test/test_epoll.rb
+++ b/test/test_epoll.rb
@@ -18,7 +18,10 @@ def setup
 
   def test_constants
     Epoll.constants.each do |const|
-      next if const.to_sym == :IO
+      case const.to_sym
+      when :IO, :EPOLL_CTL_MOD_RACY
+        next
+      end
       nr = Epoll.const_get(const)
       assert nr <= 0xffffffff, "#{const}=#{nr}"
     end
diff --git a/test/test_epoll_optimizations.rb b/test/test_epoll_optimizations.rb
index bd77397..cdb94c8 100644
--- a/test/test_epoll_optimizations.rb
+++ b/test/test_epoll_optimizations.rb
@@ -31,17 +31,34 @@ def test_set
     assert_equal 1, lines.grep(/^epoll_ctl/).size
     assert_match(/EPOLL_CTL_ADD/, lines.grep(/^epoll_ctl/)[0])
 
-    io, err = Strace.me { @ep.set(@wr, Epoll::OUT | Epoll::ONESHOT) }
-    assert_nil err
-    lines = io.readlines; io.close
-    assert_equal 1, lines.grep(/^epoll_ctl/).size
-    assert_match(/EPOLL_CTL_MOD/, lines.grep(/^epoll_ctl/)[0])
+    if Epoll::EPOLL_CTL_MOD_RACY
+      io, err = Strace.me { @ep.set(@wr, Epoll::OUT | Epoll::ONESHOT) }
+      assert_nil err
+      lines = io.readlines; io.close
+      assert_equal 2, lines.grep(/^epoll_ctl/).size
+      assert_match(/EPOLL_CTL_DEL/, lines.grep(/^epoll_ctl/)[0])
+      assert_match(/EPOLL_CTL_ADD/, lines.grep(/^epoll_ctl/)[1])
+
+      io, err = Strace.me { @ep.set(@wr, Epoll::OUT) }
+      assert_nil err
+      lines = io.readlines; io.close
+      assert_equal 2, lines.grep(/^epoll_ctl/).size
+      assert_match(/EPOLL_CTL_DEL/, lines.grep(/^epoll_ctl/)[0])
+      assert_match(/EPOLL_CTL_ADD/, lines.grep(/^epoll_ctl/)[1])
+    else
+      io, err = Strace.me { @ep.set(@wr, Epoll::OUT | Epoll::ONESHOT) }
+      assert_nil err
+      lines = io.readlines; io.close
+      assert_equal 1, lines.grep(/^epoll_ctl/).size
+      assert_match(/EPOLL_CTL_MOD/, lines.grep(/^epoll_ctl/)[0])
+
+      io, err = Strace.me { @ep.set(@wr, Epoll::OUT) }
+      assert_nil err
+      lines = io.readlines; io.close
+      assert_equal 1, lines.grep(/^epoll_ctl/).size
+      assert_match(/EPOLL_CTL_MOD/, lines.grep(/^epoll_ctl/)[0])
+    end
 
-    io, err = Strace.me { @ep.set(@wr, Epoll::OUT) }
-    assert_nil err
-    lines = io.readlines; io.close
-    assert_equal 1, lines.grep(/^epoll_ctl/).size
-    assert_match(/EPOLL_CTL_MOD/, lines.grep(/^epoll_ctl/)[0])
     @wr.close
     @rd.close
 
-- 
1.8.1.46.gcb3a6ab



^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [sleepy.penguin] [PATCH] epoll: avoid EPOLL_CTL_MOD race condition
  2013-01-03  4:19 [sleepy.penguin] [PATCH] epoll: avoid EPOLL_CTL_MOD race condition Eric Wong
@ 2013-01-17 21:35 ` Eric Wong
  0 siblings, 0 replies; 2+ messages in thread
From: Eric Wong @ 2013-01-17 21:35 UTC (permalink / raw)
  To: sleepy.penguin

Eric Wong <normalperson@yhbt.net> wrote:
> Until everybody updates to a version of the Linux kernel with
> this fix, we need to enable a workaround for older kernels using
> EPOLL_CTL_DEL + EPOLL_CTL_ADD to emulate EPOLL_CTL_MOD behavior.
> 
> This race condition is fixed in Linux upstream
> commit 128dd1759d96ad36c379240f8b9463e8acfd37a1 and included in
> Linux v3.8-rc2 and later.
 
Linux 3.7.3, 3.4.26, 3.2.37, 3.0.59 all have this fix already.
Linux 3.5.7.3 will also have this.

> This fix will likely be backported to stable and vendor kernels,
> so we'll whitelist them as we learn of them.

I've reverted this workaround (and pushed to master @
git://bogomips.org/sleepy_penguin.git )

commit b3fa14976bc41c2daad0b8a6afd74a8212dcb97b
Author: Eric Wong <normalperson@yhbt.net>
Date:   Thu Jan 17 21:30:30 2013 +0000

    Revert "epoll: avoid EPOLL_CTL_MOD race condition"
    
    This reverts commit 02e5a91b24983d96b342c007661966495ccdb619.
    
    This workaround may have unintended side-effects for apps using
    EPOLL_CTL_DEL.
-- 
Eric Wong


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2013-01-17 21:35 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-01-03  4:19 [sleepy.penguin] [PATCH] epoll: avoid EPOLL_CTL_MOD race condition Eric Wong
2013-01-17 21:35 ` Eric Wong

Code repositories for project(s) associated with this public inbox

	https://yhbt.net/sleepy_penguin.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).