[comp.unix.internals] How do I find my buffers once I have lost them?

bzs@world.std.com (Barry Shein) (11/08/90)

>When the kernel gets the interrupt how can it find the user buffer again
>(assuming it has been locked into memory) to do the next transfer? The
>buffer may no longer be mapped into the kernel's virtual address space
>since at the time of the interrupt some other user process may be running.
>How can the kernel even find the original user process (to, say, send a
>SIGIO to it)? Does it have to search the process table looking for it?
>It can't search physical RAM looking for the buffer since the buffer may
>be segmented into many chunks of RAM.

Well, one way to handle this is to just use a temp buffer to DMA
between and use copyin() and/or copyout() to move the data, but it
sounds like you're trying very hard to avoid that, ok.

Depending on how much info you really have to keep around you could
certainly declare a structure specific to the device with the items
you want to know latter, use rmalloc() or similar to create one for
each transfer and have the buffer struct point at it when you start.
Being as the buf struct is handed back to you at interrupt time you
could just snoop in there for the info you saved previously. So
the obvious thing would be:

	struct mydevinfo {
	  pid_t user_pid;
	  caddr_t user_buffer;
	};

and then when you set up the transfer something like:

	struct mydevinfo *myp;

	myp = (struct mydevinfo *)rmalloc(sizeof(struct mydevinfo));
	myp->user_pid = pid;
	myp->user_buffer = buffer;
	bp->b_un.b_addr = (caddr_t)myp;

and in the interrupt handler:

	struct mydevinfo *myp;

	myp = (struct mydevinfo *)bp->b_un.b_addr;

and so on.

sort of disgusting but it would do the job.

Maybe I'm missing something here...

gee, an internals question...imagine...I just had to *try* to answer,
but I'd suggest you wait until people pick apart my suggestion before
taking the advice.
-- 
        -Barry Shein

Software Tool & Die    | {xylogics,uunet}!world!bzs | bzs@world.std.com
Purveyors to the Trade | Voice: 617-739-0202        | Login: 617-739-WRLD

sramtrc@windy.dsir.govt.nz (11/08/90)

Suppose I have a UNIX version which maps a user program into the kernel
virtual space. Then accessing a user buffer by the kernel is easy since
the buffer has an address in the kernel virtual space. Suppose the kernel
wants to send the user buffer to a device in several transfers. This is
easy because the kernel tells the device the address of the buffer, starts
the transfer, and waits for an interrupt from the device.

When the kernel gets the interrupt how can it find the user buffer again
(assuming it has been locked into memory) to do the next transfer? The
buffer may no longer be mapped into the kernel's virtual address space
since at the time of the interrupt some other user process may be running.
How can the kernel even find the original user process (to, say, send a
SIGIO to it)? Does it have to search the process table looking for it?
It can't search physical RAM looking for the buffer since the buffer may
be segmented into many chunks of RAM.

Thanks,
Tony Cooper
sramtrc@albert.dsir.govt.nz

thorinn@rimfaxe.diku.dk (Lars Henrik Mathiesen) (11/08/90)

sramtrc@windy.dsir.govt.nz writes:
>Suppose I have a UNIX version which maps a user program into the kernel
>virtual space. Then accessing a user buffer by the kernel is easy since
>the buffer has an address in the kernel virtual space. Suppose the kernel
>wants to send the user buffer to a device in several transfers.

>When the kernel gets the interrupt how can it find the user buffer again
>(assuming it has been locked into memory) to do the next transfer? [Many
>problems described]

This is probably why even those UNIX versions where user programs are
mapped for the kernel (such as any UNIX on a VAX) often don't use this
direct approach. Rather, the driver asks a support routine (physio in
4.3BSD) to take as large a chunk of the user buffer as the driver can
handle and make it ``look like'' a kernel cache buffer.  Usually that
involves locking the pages incore and allocating a buffer header and
some kernel virtual address space to map the chunk. Physio then calls
the device strategy routine, frees up everything and repeats the
process for the next chunk. It is physio that keeps context between
chunks (such as user virtual address and location of the user page
map).

If your UNIX has physio or an equivalent, you're probably better off
using it.

--
Lars Mathiesen, DIKU, U of Copenhagen, Denmark      [uunet!]mcsun!diku!thorinn
Institute of Datalogy -- we're scientists, not engineers.      thorinn@diku.dk

bzs@world.std.com (Barry Shein) (11/09/90)

Yeah, physio(), don't you have to call mapin() to lock down the pages
first? Anyhow, that seems like the right advice that Lars just gave.
-- 
        -Barry Shein

Software Tool & Die    | {xylogics,uunet}!world!bzs | bzs@world.std.com
Purveyors to the Trade | Voice: 617-739-0202        | Login: 617-739-WRLD

hue@island.uu.net (Jon Hue - "Bo knows windsurfing?") (11/09/90)

In article <18747.27394289@windy.dsir.govt.nz>, sramtrc@windy.dsir.govt.nz writes:
> the buffer has an address in the kernel virtual space. Suppose the kernel
> wants to send the user buffer to a device in several transfers. This is
> easy because the kernel tells the device the address of the buffer, starts
> the transfer, and waits for an interrupt from the device.
> 
> When the kernel gets the interrupt how can it find the user buffer again
> (assuming it has been locked into memory) to do the next transfer? The

Unless I'm missing something here, you should try to write your driver so
you can use physio().  physio() will handle breaking up the transfer into
chunks the device can deal with, pin down the memory, and then call your
strategy routine to perform the I/O operation.

Are you under some sort of real-time contraint which requires you to start
the next I/O operation from the interrupt level?  It's much simpler if you
can just wakeup the sleeping process from the interrupt level, and wait for
it to start running (still inside physio()) again.

Jonathan Hue	Island Graphics Corp.	uunet!island!hue   hue@island.com

boyd@necisa.ho.necisa.oz (Boyd Roberts) (11/09/90)

In article <18747.27394289@windy.dsir.govt.nz> sramtrc@albert.dsir.govt.nz writes:
>Suppose I have a UNIX version which maps a user program into the kernel
>virtual space. Then accessing a user buffer by the kernel is easy since
>the buffer has an address in the kernel virtual space. Suppose the kernel
>wants to send the user buffer to a device in several transfers. This is
>easy because the kernel tells the device the address of the buffer, starts
>the transfer, and waits for an interrupt from the device.
>
>When the kernel gets the interrupt how can it find the user buffer again
>(assuming it has been locked into memory) to do the next transfer?

Usually the user data is copied into a kernel mode data structure* with
copyin() [iomove() in the read()/write() case].  The copy is done
precisely to avoid the problem of being able to reference the data when
the process's context is not available for reference.  This approach is
usually ok, but sometimes you want to DMA straight to the process's
address space.

To do that I've often cheated by getting physio() to do the hard work.
My character based driver has a `dummy' strategy routine et al and
it's called from physio().  The driver calls physio() and physio() locks
down the process and does all the foul machine dependent mapping and calls
the strategy routine until the I/O is complete.  The process is unlocked,
the DMA done.

Of course, your kernel may not do exactly what I described.  There may be
other hooks to validate pages, lock them down and provide a kernel mapping
to them.  Then again, there may be none that suit your application.



Boyd Roberts			boyd@necisa.ho.necisa.oz.au

``When the going gets wierd, the weird turn pro...''

* How you get that data structure is your problem and is specific to
  the driver and kernel.  Just make sure it's in context when you want
  to reference it.  The kernel stack is just a no-no.

sramtrc@windy.dsir.govt.nz (11/09/90)

 
PS - I should add that what I want to do is copy directly from user space to
a device. Physio is supposed to do this and it does. But my physio cheats. It
uses copyin/copyout to move the user data into kernel data space. That's the
copy I would like to avoid. I noticed that Sun's physio does not do a copy.
But I guess it uses a different mechanism for accessing user space.

 Tony Cooper
 sramtrc@albert.dsir.govt.nz