Index: [thread] [date] [subject] [author]
  From: Andreas Beck <becka@rz.uni-duesseldorf.de>
  To  : ggi-develop@eskimo.com
  Date: Tue, 17 Aug 1999 00:02:00 +0200

Re: Ping-pong buffers on KGIcon are here!

> > How do you handle the locking with the framebuffer ? 

> 	Right now I just spin_lock() in the kernel |->.  But when I get
> around to implementing a real solution which does not involve blocking all
> of userspace, I will do it the same way that Marcus' fbdev code does - by
> using the accelidle handler.  As fas as KGIcon is concerned, there are a
> lot of ways I could go.  select() on /proc/kgi/kgicommand might be the
> easiest, we'll see.  

Yes. That sounds good.

> A better solution, involving no locks at all, would
> be to fault the process to sleep if it touches the framebuffer whilst
> accels are still being processed....

This is usually bad, as it has heavy penalties on mapping/unmapping the fb,
as it causes CPU TLB flushes, which are very expensive, as Morton already
pointed out.

IIF the card will not lock up on concurrent access (mess up screen is o.k.)

You should use an advisory lock. For mechanism, I propose to have two locks
within the mapped GC:

1. fb-lock.

This one gets engaged, while fb operations are in progress. That is, when a
directbuffer is locked in or a LibGGI primitive is currently accessing the
fb. It is mutually exclusive with

2. accel-lock.

Which does the same for running accel commands.

These locks have to be implemented in user/kernel crossmapped space and
should be handled as follows:

When the kernel gets an acceleration call, it first engages the accel lock.

Then it _tests_ the fb lock. As the accel lock is held, the fb lock cannot
change, as you will see later.

If the fb lock is on, the kernel will operate the accelerator
_synchronously_ and _atomically_ with respect to the application.

That is, the call that trapped into the kernel will not return until the
accelerator is ready again. For multithreading, it might be required to put
the whole thread family to sleep.

When the acceleration command has completed, the accel lock gets disengaged.

If the fb lock if off, the kernel operates the accelerator asynchronously. 
The accel lock is kept until the accelerator is idle. An interrupt should be
used to achieve that.

Now for the userspace part:

If the fb lock shall be acquired, you first have to acquire the accel lock
to avoid deadlocking or the possibility of race conditions.
When you hold that, you engage the fb lock, thus putting the accel engine to
synchronous mode.
Then you release the accel lock again, which serves as a mutex for the other
lock in that case.

Releaseing the fb lock could actually skip the accel-lock "bracketing", as 
this operation is "harmless". A false reading will fail safely.

Examples:

LibGGI is in ASYNC mode (or PP buffers make less sense anyway, we can just
do away with some latency by not waiting for the accel to complete).

The app has been feeding the accel queue with a bunch of requests that are
still executing. Thus the Accel lock is on.

Now the app calls ggiPutPixel, which will do fb access and thus asks for the
fb lock. The locking code now tries to acquire the accel lock. This blocks.

When the accel-idle interrupt comes in, the kernel releases the accel lock,
and the app can go on. It gets the fb lock, releases the accel lock, does
its stuff and the releases the fb lock.

Now it again sends a bunch of accel requests. Accel lock goes on again and
the accel runs freely in the background.

The app now tries to lock down a directbuffer. The same as above happens.
It waits on the accel lock, engages the fb lock, frees the accel lock.
It draws some stuff on the DB.
While doing so, it calls a LibGGI primitive with the DB still locked.
The LibGGI primitive issues an accel command. The kernel sees it, and gets
the accel lock. It tests the fb lock and finds it locked.

Oh-oh. Some app is holding down an fb lock, and asks me to accel something
... O.K. - so we better do that synchronously. The kernel executes the accel
request and waits for accel idle. Then it releases the accel lock and
returns to the application. This is transparent to the application, as the
accel is ready, when the app gets back control.

Hope I was clear enough on how to implement that "advisory locking" I am
talking about.

Oh - a small clarification: Before waiting for the accel lock, one should of
course do an implicit accel-flush command, or we would get reordering
problems. This could even be used to get at the blocking behaviour very
simply.

CU, ANdy

-- 
= Andreas Beck                    |  Email :  <andreas.beck@ggi-project.org> =

Index: [thread] [date] [subject] [author]