Date: Tue, 29 Apr 2003 16:59:16 -0400 (EDT) From: John Baldwin <jhb@FreeBSD.org> To: Andrew Gallatin <gallatin@cs.duke.edu> Cc: cvs-all@FreeBSD.org Subject: Re: cvs commit: src/sys/dev/fxp if_fxp.c if_fxpvar.h Message-ID: <XFMail.20030429165916.jhb@FreeBSD.org> In-Reply-To: <16046.55840.945040.149302@grasshopper.cs.duke.edu>
next in thread | previous in thread | raw e-mail | index | archive | help
On 29-Apr-2003 Andrew Gallatin wrote: > > Where would you do this check for more pending handlers? In detach? > > What does detach do when it's true? The problem is that detach may > > It sleeps. > > > be too late. Take this sequence: > > > > > > CPU 0 CPU 1 > > -------- -------- > > foo_detach() ... > > FOO_LOCK() interrupt for foo > > ... foo_intr() > > ... FOO_LOCK() and blocks > > You forgot: > prevent_device_from_generating_interrupts(); > FOO_UNLOCK(); > > > bus_teardown_intr() This doesn't do any good though. When you FOO_UNLOCK, CPU 1 is _STILL_ going to resume execution of foo_intr(). Depending on if CPU 1 gets interrupted some more before it finishes foo_intr(), CPU 0 can finish all of detach before CPU 1 finishes foo_intr(). How do you handle that? [ Reading on it seems you advocate the sleep in detach to ensure you don't go beyond bus_teardown_intr() in detach(). ] > In foo_detach you disable interrupts on the board, and you replace its > handler with an orphan handler for the benefit of shared irqs. At the > point there can be exactly one interrupt pending which may be blocked > anywere, possibly on the foo lock. You then drop the foo lock. > > You then grab the required locks, and see if foo's ithread is running, > or runnable. If it is, you drop the locks, sleep for hz and repeat > the check. If its not, you do the guts of ithread remove handler. Your orphan handler doesn't buy you anything. Due to IH_DEAD you don't have to worry about that step, IH_DEAD already ensures that you only have the one race case we are talking about to deal with. Basically though, you are moving the sleep from inside bus_teardown_intr() out into each driver, increasing the maintenance load on the driver author. Also, you still haven't solved the problem that it may be _very_ bad to touch foo's registers in foo_intr() after you've gotten into foo_detach() since it could re-enable interrupts, etc. You might be able to work around that on a case-by-case basis by tweaking the order of shutdown, but that makes it harder to write drivers. Also, it's only a win if you avoid a test for the special case in foo_intr(). Hmmmm. You almost want a way to turn off the interrupt handler as the very first thing (maybe just mark it as dead if it is still running, but not block) while holding the driver lock, then turn off the ability to generate interrupts, then sit and wait for the interrupt handler to remove itself. Maybe: mtx_lock(&sc->sc_mtx); /* handler no longer executes */ bus_disable_intr(inthand_cookie, &sc->sc_mtx); /* dink with device */ mtx_unlock(&sc->sc_mtx); bus_teardown_intr(inthande_cookie); With bus_disable_intr() calling a function like this: void ithread_disable_handler(void *cookie, struct mtx *m) { struct intrhand *ih; struct ithread *it; ih = (struct intrhand *)cookie; it = ih->ih_it; mtx_lock(&it->it_lock); if (ithread_is_running) { it->it_need = 1; ih->ih_flags |= IH_DISABLING; ih->ih_mutex = m; mtx_unlock(&it->it_lock); msleep(ih, m, ...); } else { ih->ih_flags |= IH_DISABLED; mtx_unlock(&it->it_lock); } } and some changes to ithread_loop() along the lines of: CTR6(... /* existing */) if (ih->ih_flags & IH_DISABLED) continue; if (ih->ih_flags & IH_DISABLING) { struct mtx *m; m = ih->ih_mutex; ih->ih_flags |= IH_DISABLED; ih->ih_flags &= ~IH_DISABLING; mtx_unlock(&it->it_lock); mtx_lock(&m); wakeup(ih); mtx_unlock(&m); goto restart; } if (ih->ih_flags & IH_DEAD) { TAILQ_REMOVE(...); wakeup(ih); continue; } I would need to hold the ithread lock around the TAILQ_FOREACH() bit and drop it around the execution of the handlers, but I probably need that anyways. Of course, all this really does, is take your gone test and move it up in the stack. You are still checking a flag on every interrupt handler invocation. > > > We really need to think about efficiency. Our 5.x performance sucks. > > > Really sucks. We're being nickled and dimed to death by extra > > > instructions here, there, and everywhere. > > > > Unfortunately 5.x attempts to run with a thread-safe kernel, and that > > involves extra overhead to work around races that 4.x didn't even have > > to dream about. In theory the increased performance should come from > > increased parallelism at the cost of increased overhead. If FreeBSD > > No, in theory increased performance should come from increased > parallelism with no increased overhead. Any increased overhead is a > bug. Linux 2.4 runs a thread safe kernel with less overhead than we > have in 4.x. Its possible. How are we going to achieve increased paralellism w/o increased overhead? Discounting redesigning algo's which would have been a win in the non-parallel kernel as well. A mutex is far more expensive than an spl. You have to protect against more things. Of course overhead is going to go up. > As we get closer to a stable branchpoing, and continue to suck, I'm > starting to think we should start over. Well, the Project is free to choose that if it wishes. -- John Baldwin <jhb@FreeBSD.org> <>< http://www.FreeBSD.org/~jhb/ "Power Users Use the Power to Serve!" - http://www.FreeBSD.org/
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?XFMail.20030429165916.jhb>