Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 26 May 2000 12:19:10 -0600
From:      Chuck Paterson <cp@bsdi.com>
To:        Matthew Dillon <dillon@apollo.backplane.com>
Cc:        arch@freebsd.org
Subject:   Re: Short summary 
Message-ID:  <200005261819.MAA14981@berserker.bsdi.com>

next in thread | raw e-mail | index | archive | help
Some confusion may have been introduced by when I said that we
allowed no interrupts in the kernel at the transitional stage. This
isn't true, we allowed all interrupts, its just that all they did
is schedule a thread, which got put on a run queue which then go
run on the way out.

I believe that we want to get to the point where all top half code
is running under one mutex with interrupts enabled before adding
any mutexs. One advantage of having a single well known mutex for
unconverted code is that sleep and tsleep can know to release it.

Running cli'd will prevent IPIs from being delivered. Any of these
that need to run synchronously will fail, such as tlb shoot downs.

The code which implements the mutex operations knows that the
processor should not be cli'd when acquiring or releasing a non
spin mutex. Aside from making the non debug code simpler it has
proved to be a valuable debugging tool.

Once you have the kernel running with interrupts enabled and under
a single lock work can begin in parallel. This without actually
having to start another processor.

    a)	Putting in the mechanism so you can have safe
	    and unsafe drivers. You need this unless you
	    want to try and convert all the drivers at once.
    b)	The actual code to handle light weight interrupt
	    threads.
    c)	Malloc, which has to be addressed to a first
	    degree before much or top half work can be done.
    d)	Implementation/porting/debugging of mutex order 
	    checking code.
    e)	Converting drivers and driver sub systems.


There are multiple paths to getting to running under a single mutex.
What Matt suggested below can be modified slight to achieve this.
I'll try and lay out what was done with BSD/OS though it was a long
time ago. I should also point out that it didn't take very long.
One person for a couple maybe 3 weeks. The actual code took very little
time, but debugging when the most basic stuff is suspect was painful.
The trace stuff in the SMPng code can now trace to memory on the
PCI bus. This would have made things much easier. The spontaneous
reboots where a killer.

This is what we did for BSD/OS, not the modification of Matt's
suggestion to achieve the same thing. Its been a while so I am
undoubtedly forgetting details. It was all straight forward and
because apparent in the normal course of events.

	1)	Defined basic types for macros.
	2)	Added mutex argument to sleep and tsleep, modified
		all callers to pass in NULL.
	3)	Added code to create the process for an interrupt
		thread. Really just called fork I think. Got
		thread created and safely stopped, not running
		not on run queue, and other process running.
	4)	Did basic testing of mutexs, both in user code
		and then in kernel. Not used for anything but
		test to see that they operated as expected. In
		our case this didn't really get all the bugs
		out.
	5)	Put code in spls so they could be turned into NOP
		by setting a new_mode variable
	6)	Added code to top level interrupt handlers to
		put a thread on the run queue and return leaving
		interrupts for that level blocked rather than
		calling the actual handlers for that level if new
		mode was set.
	6)	Added the code to the interrupt threads to
		a) release sched lock (spin mutex with a bad name)
		b) acquire giant (non spin)
		c) call the handles associated with the
		    thread associated with a given level
		d) release giant
		e) acquire sched
		f) re-enable interrupts
		g) call cpu_switch
	8)	Added code to the mutex routines so they would not do anything
		unless new mode was set.
	9)	Added code to cpu switch to fix up recursion count and
		owner on sched_lock
	10)	Added code around all calls to cpu_switch to acquire
		and release the sched lock.
	10)	Add acquiring and releasing of giant in trap an syscall.
	11)	Made sleep/tsleep give up giant if held on
		entry and acquire on exit.
	12)	Flipped the switch and debugged for a week or so.
		A couple of items above where actually done here, but
		I don't remember which ones.

I only hacked up the non sio mode interrupt code so we never
made the kernel in this configuration run with more than one
processor.

At this point someone else was working on the real interrupt
handler and could have some confidence that when he started
trying to run them the rest of the system would behave.

The SMPng kernel went directly from hacked up code above to the
light weight interrupt code. This took a long time, months. This
code is very optimized, assembler code which is then patched together
on the fly to match the level, type of interrupt source and the
number of handlers which need to be called. The goal, which was
achieved, was to make getting in an out of an interrupt thread as
cheap or cheaper than what was present in the previous kernel. This
was all part of the effort to make sure we would not require two
kernels in the long run. I would have really liked to have a MP
capable kernel in less time. This could have been achieved with a
fairly simple code to always use heavy weight interrupt threads.
The person who really knew how to deal with the APIC was the same
person doing the light weight threads, so this didn't happen in
BSD/OS. We, meaning FreeBSD this time, should certainly consider
this. This is assuming an approach similar to that which I have
outlined is adopted.

While waiting for real interrupt code:
    Really made sleep and tsleep do the right thing. Actually
    this took several go arounds, but it got close here.
    I Made malloc use its own lock.
    Added the run priorities really needed by interrupt threads.
    Added the priority propagation code.
    Added the code for safe and unsafe drivers.
    And mainly worked on the non SMPng kernel. Not because I
	didn't have stuff to do on the SMPng kernel, but other
	stuff got pushed first.

Chuck

----- Begin Included Message -----

    I think this will work to get the ball rolling.  We can simply
    'cli' in the MP lock code and 'sti' in the MP last-unlock code
    ( i386/i386/mplock.s ).  Then we can turn the spl*() calls into
    NOPs and do the (relatively trivial) fixup to the scheduler.
    Actually, I don't think we would have to touch the scheduler at all
    for this step, it already releases the MP lock and it already supports
    scheduling supervisor contexts to multiple cpu's.  (In fact, we 
    already support lockless system calls even though only a few trivial
    calls do it at the moment!).

    The next step would be to implement interrupt threads and simply
    allow them to be scheduled by the scheduler holding the MP lock.

    After the interrupts are all threaded, we can start removing the MP
    lock and switching subsystems over to use mutexes.

    What do you think?

					-Matt
					Matthew Dillon 
					<dillon@backplane.com>


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-arch" in the body of the message
----- End Included Message -----



To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-arch" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200005261819.MAA14981>