buf(D4)
buf --
non-STREAMS I/O data transfer structure
Syntax
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/scgth.h>
#include <sys/ddi.h>
Description
The buf structure, also called a buffer header,
is the basic data structure for I/O transfers.
Usage
Each I/O transfer has
an associated buffer header structure.
This structure contains control and status information
for the transfer.
Buffer headers are passed to drivers'
biostart(D2)
routines.
They may also be allocated by the driver itself using
getrbuf(D3),
ngeteblk(D3),
or
geteblk(D3).
A data buffer that holds the data for the I/O operation
is associated with each buffer header.
The data buffer may be in kernel data space, or
it may be simply a list of physical memory pages.
It may also be
a set of unlocked user address ranges
that are represented by a
uio(D4)
structure.
Do not depend on the size of the buf structure
when writing a driver
(or any other module that needs binary compatibility).
In particular,
this means you must only allocate buf structures
using DDI functions such as
getrbuf(D3).
Static allocations are not allowed.
It is important to note that a buffer header
may be linked onto multiple lists simultaneously,
and is also passed back to the system
when the driver is done with it.
Because of this, most of the members
in the buffer header cannot be changed by the driver,
even when the buffer header is in one of the driver's work lists.
To understand the rules for using various members
of the buf structure,
it is necessary to first understand the various agents
that handle a buffer,
and how control of the buffer is passed among these agents.
creator agent-
acquires the buffer (through functions such as
geteblk(D3),
getrbuf(D3),
and
ngeteblk(D3))
and initializes it.
Some of the initialization is done
inside the allocating interface;
some is done by the caller.
The creator is often part of a filesystem module
or a kernel filter routine,
but it may also be a driver routine that calls
getrbuf( ),
geteblk( ),
or
ngeteblk( ).
I/O handler-
the part of a device driver,
starting from its
biostart( )
entry point,
that receives a buffer as an I/O request,
handles the transfer of data and/or errors,
and indicates I/O completion by calling the
biodone(D3)
function.
kernel filter routine-
receives a buffer
that either has already been passed to an I/O handler
or is about to be,
performs some transformations on
either the buffer itself or newly created buffer(s)
based on the original buffer,
then passes the transformed buffer(s) on to
(or back to) the I/O handler or the next filter.
controlling agent-
Control of a buffer is handed off
from one agent to another over the life of the buffer.
At any given time,
exactly one agent has control of the buffer.
Only the controlling agent may do anything with the buffer
besides waiting for I/O completion with
biowait(D3)
or
biowait_sig(D3).
The creator controls the buffer initially.
When the driver's
biostart( )
routine is called,
control transfers to the I/O handler.
Control remains with the I/O handler
until it calls the
biodone( )
function.
For synchronous I/O operations,
the creator calls the
biowait( )
or
biowait_sig( )
function; when the
biowait( )
function returns,
control returns to the creator.
If a filter routine is called,
control transfers to the filter routine
until it calls the next-level
biostart( )
routine.
During I/O completion processing,
if an
iodone( )
function is called
(b_iodone
was non-NULL),
control transfers to that routine,
which is considered to be part of the agent
that set the b_iodone
member in the first place.
Many buffer members are only allowed to be modified by certain agents.
These and other restrictions are listed below,
in the descriptions of the individual structure members.
References to the driver refer to the whole driver,
no matter which agent.
Where a member is described as being preserved by an agent,
this means that either the agent does not change the members,
or that before giving up control of the buffer,
the agent restores the members's value
to the value it had when the agent first acquired control.
Structure definitions
The buf structure, buf_t,
contains the following members
that may be accessed by drivers.
Note that some structure members may not be present in
all DDI versions.
uint_t b_flags;
buf_t *b_forw;
buf_t *b_back;
buf_t *av_forw;
buf_t *av_back;
long b_bufsize;
uint_t b_bcount;
daddr_t b_blkno;
ushort_t b_blkoff;
uchar_t b_addrtype;
union {
caddr_t b_addr;
uio_t *b_uio;
} b_un;
uint_t b_resid;
clock_t b_start;
void (*b_iodone)();
void *b_misc;
union {
void *un_ptr;
int un_int;
} b_priv;
union {
void *un_ptr;
int un_int;
long un_long;
daddr_t un_daddr;
} b_priv2;
Buffer members
Drivers are only allowed to access certain buffer members.
Accesses
by a driver to any other members are illegal
and may not continue to work in subsequent releases of the
UNIX System.
The following members may be accessed by the driver:
b_flags
-
This is a bitmask of flag bits which reflect buffer status and
control flags.
This members may not be directly assigned by any
agent; it is only legal to set or clear specific bits.
The driver may only access some of these flag bits. The following
flags may be accessed by the driver:
B_READ-
Indicates that data are to be transferred from
the peripheral device into main memory.
This flag may only be changed by the creator agent.
B_WRITE-
Indicates that data are to be transferred from
main memory to the peripheral device.
B_WRITE
is a pseudo-flag that occupies the same bit
location as B_READ.
B_WRITE cannot be directly
tested; it is only detected as the absence of
B_READ
(
(bp->b_flags&B_READ)
, for example);
it can only be ``set'' by clearing B_READ.
B_ASYNC-
Indiates an asynchronous data transfer.
The
b_iodone
member must also be set.
This flag may only be changed
by the creator agent.
b_forw/b_back
-
These members can be used to link the buffer
onto a doubly-linked list.
They may only be used by the creator agent (or, if the
creator is in a driver, by the whole driver), and only if the
buffer was created by getrbuf.
av_forw/av_back
-
These members can be used to link the buffer
onto a doubly-linked list.
The driver may change these any time it controls the buffer.
These members must be preserved by any filters.
b_bufsize
-
This member contains the size in bytes of the allocated buffer.
The
b_bufsize
member may not be changed
except by kernel functions that create buffers,
or by the creator agent if the buffer was created
with the
getrbuf(D3)
function.
b_bcount
-
This member specifies the number of bytes to be transferred.
It will be set to the total byte count
(which should always be a multiple of NBPSCTR)
upon initial entry to the I/O handler or a filter.
This member may be changed by the creator
or the I/O handler.
b_blkno
-
This member specifies the first logical block on the device
that is to be accessed.
One block equals NBPSCTR bytes.
The driver may have to convert this logical
block number to a physical location such as a cylinder, track,
and sector of a disk.
Any agent may change this member,
but it must restore the previous value before calling the
biodone(D3)
function.
b_blkoff
-
This member specifies the byte offset within the block given
by b_blkno of the beginning of the transfer.
This will always be less than NBPSCTR.
Only the creator may change this member.
Unless the driver indicates
that it understands b_blkoff
by setting D_BLKOFF in its
Any agent may change this member,
but it must restore the previous value before calling the
biodone(D3)
function.
drvinfo(D4)
structure (or, for DDI version 7 and earlier, its
devflag(D1)),
this member is always zero and may be ignored.
b_addrtype
-
This member specifies the type of address used to reference
the buffer data.
Valid values are:
BA_KVIRT-
contiguous kernel virtual
BA_UVIRT-
contiguous user virtual
BA_PAGELIST-
a list of physical pages
BA_UIO-
use
uio(D4)
structure
BA_SCGTH-
physical scatter/gather list
This member may not be changed except by kernel functions.
It is always set in the current DDI version.
b_un.b_addr
-
This member indicates the start of the data buffer for all
address types except BA_SCGTH.
It is either a virtual address
or an offset into the first page of a page list
(see B_PAGEIO, B_PHYS,
and
b_addrtype
for more details).
The creator or the I/O handler may change this member.
b_resid
-
This member indicates the number of bytes not transferred,
usually because of an error.
A value of zero indicates a successful complete transfer.
The I/O handler must set this member before
it calls the
biodone(D3)
function.
b_start
-
This member is used to hold the time
the I/O request was started.
This time is obtained by calling the
drv_getparm(D3)
function with the LBOLT parameter.
The I/O handler may set it
and use it upon I/O completion
to compute response time metrics.
b_iodone
-
This member identifies a specific routine to be called
when the I/O operation has completed.
If it is non-NULL, the
biodone(D3)
function calls the
*b_iodone
member
instead of doing the normal completion processing.
Any agent may change this member,
but it must restore the previous value before calling the
biodone( )
function, and thus relinquish control of the buffer.
For the creator, the previous value is always NULL.
This protocol allows for ``stacking'' of
iodone routines (particularly useful for filters).
Each agent saves the old value,
sets b_iodone
to its iodone routine,
and hands off the buffer to the next filter or the I/O handler.
On completion, the I/O handler calls biodone,
and each iodone function performs its final processing,
restores b_iodone
to the saved value,
and calls the
biodone( )
function again,
thus invoking the next iodone routine.
b_misc
-
This is a miscellaneous member for use by the controlling agent.
One common use is in conjunction with
b_iodone
,
to help in saving the previous b_iodone
value.
Typically, the previous
values of b_iodone
and b_misc
are saved in a structure,
b_misc
set to point to this structure,
and b_iodone
set to point to the new iodone routine.
This member may only be used by the controlling agent.
If the controlling agent is the creator,
it may modify b_misc
directly;
otherwise it must preserve the original value
before returning the buffer to another agent.
b_priv/b_priv2
-
These members are private members for use by the driver.
No other agents interpret or change them.
Warnings
Buffers are a shared resource within the kernel.
Drivers should only read or write the members listed in this
section in accordance with the rules given above.
Drivers that attempt to use undocumented members of the
buf structure risk corrupting data
in the kernel and on the device.
DDI-conforming drivers may only use
buffer headers that have been allocated using the
geteblk(D3),
ngeteblk(D3),
or
getrbuf(D3)
functions,
or have been passed to the driver
biostart(D2)
entry-point routine.
Applicable hardware
All
Version applicability
ddi:
1, 2, 3, 4, 5, 5mp, 6, 6mp, 7, 7mp, 7.1, 7.1mp, 8, 8mp
Some members of the buf structure
are not supported in all DDI versions:
b_edev
-
ddi: 1, 2, 3, 4, 5, 5mp, 6, 6mp, 7, 7mp, 7.1, 7.1mp
*b_proc
-
ddi: 1, 2, 3, 4, 5, 5mp, 6, 6mp, 7, 7mp, 7.1, 7.1mp
b_addrtype
-
ddi: 5, 5mp, 6, 6mp, 7, 7mp, 7.1, 7.1mp, 8, 8mp
*b_misc
-
ddi: 5, 5mp, 6, 6mp, 7, 7mp, 7.1, 7.1mp, 8, 8mp
b_priv
-
ddi: 5, 5mp, 6, 6mp, 7, 7mp, 7.1, 7.1mp, 8, 8mp
b_priv2
-
ddi: 5, 5mp, 6, 6mp, 7, 7mp, 7.1, 7.1mp, 8, 8mp
b_private
-
ddi: 3
b_error
-
ddi: 1, 2, 4
The following values for the b_flags
member
are not supported in all DDI versions:
B_PAGEIO-
ddi: 1, 2, 3, 4, 5, 5mp, 6, 6mp, 7, 7mp, 7.1, 7.1mp
B_PHYS-
ddi: 1, 2, 3, 4, 5, 5mp, 6, 6mp, 7, 7mp, 7.1, 7.1mp
B_ASYNC-
ddi: 8, 8mp
B_ERROR-
ddi: 1, 2, 4
Difference between versions
In DDI versions before version 8,
the buf structure is used only
for block I/O drivers and is passed to the
strategy(D2)
entry point routine or, for physical I/O, to the
physiock(D3)
routine.
The following header file is also required:
#include <sys/proc.h>
In DDI versions prior to version 8,
the following b_flags
flags are also supported:
B_PAGEIO-
If set, the data buffer is represented as a page list.
This means that b_un.b_addr is not a virtual
address but is instead the offset into the first
page of a list of one or more physical pages.
This list of pages is accessible through the
getnextpg(D3)
function.
The pages will be in contiguous device
block order, starting from the first block, given
by
b_blkno
.
If B_PAGEIO is not set, b_un.b_addr
is a virtual address; it is a global kernel virtual
address if B_PHYS is not set, or
a user virtual address if B_PHYS is set.
This flag may not be changed
except by kernel utility routines which create,
map, or unmap the buffer.
If a driver does not
have D_NOBRKUP set in its
devflag(D1)
information
it will never
see a buffer with B_PAGEIO set.
B_PHYS-
Indicates that the data buffer is in user virtual
space.
b_un.b_addr contains the starting user
virtual address of the data buffer.
The data buffer
and its virtual addresses are locked in memory
so that accesses are guaranteed to succeed.
The user virtual address may not be directly accessed;
it must be mapped into a kernel virtual address using
bp_mapin(D3),
converted to physical addresses using
vtop(D3)
and
b_proc
, or copied to/from kernel space using
copyin(D3)
and
copyout(D3)
(the copyin/copyout functions
may only be used initiator
context.)
B_PHYS and B_PAGEIO are never
set simultaneously.
This flag may not be changed except by kernel
utility routines which create, map, or unmap the
buffer.
If a driver does not call
physiock(D3)
or
does not have D_NOBRKUP set in its
devflag(D1),
it will never see a buffer with B_PHYS set.
In DDI versions prior to version 5,
the following b_flags
flag is also supported:
B_ERROR-
The driver sets B_ERROR to indicate an error occurred
during an I/O transfer.
On systems where the
bioerror(D3)
function is available,
drivers should not access this flag directly.
In DDI versions prior to version 8,
the following b_addrtype
value is not supported:
BA_PHYS-
contiguous physical
The following buf structure members
are supported only in DDI versions prior to version 8:
dev_t b_edev;
proc_t *b_proc;
void *b_private;
int b_error;
b_edev
-
This member contains the external device number of the device.
Only the creator may change this member.
b_proc
-
When B_PHYS is set,
the b_proc member identifies the process
that contains the data buffer pointed to
by the user virtual address in
b_un.b_addr
.
When B_PHYS is not set, b_proc
is NULL.
This member can thus be used as the second argument
to
vtop(D3)
to convert user virtual addresses
from b_un.b_addr
into physical addresses.
When B_PAGEIO is set,
b_proc
is undefined and should be ignored.
This member may not be changed except by kernel functions
that create buffers.
b_private
-
This member is a private member for use by the driver.
No other agents interpret or change it.
In DDI versions that do not support this member,
it is reserved for use by the kernel,
or may not exist at all,
so drivers should not used it.
b_error
-
If B_ERROR is set,
this member holds the
errnos(D5)
that indicates the type of error that occurred.
The functionality of this member is provided through the
bioerror(D3)
function in later DDI versions.
Note also the following differences
about accessing specific members:
b_blkno
-
In older DDI versions,
only the creator may change this member.
b_addrtype
-
In DDI versions before version 8,
this member is valid only for
strategy( )
routines that are called by the
buf_breakup(D3)
functions.
It is always set in DDI 8.
References
biodone(D3),
bioerror(D3),
bioreset(D3),
biowait(D3),
biowait_sig(D3),
brelse(D3),
clrbuf(D3),
freerbuf(D3),
geteblk(D3),
geterror(D3),
getrbuf(D3),
iovec(D4),
ngeteblk(D3),
physiock(D3),
strategy(D2),
uio(D4)
sdi_buf_store(D3sdi),
sdi_buf_restore(D3sdi)
``Large device support'' in HDK Technical Reference
19 June 2005
© 2005 The SCO Group, Inc. All rights reserved.
OpenServer 6 and UnixWare (SVR5) HDK - June 2005