Streams function registering6098112Abstract An apparatus and method for kernel level modules or drivers identifying message processing functions and for controlling execution of the message processing functions at the stream head, so that the functions are available at the stream head for adapting stream data interpretation, stream execution behavior, and the like in accordance with various system requirements such as requirements of devices or user processes. Claims What is claimed is: Description FIELD OF THE INVENTION
______________________________________
struct sth.sub.-- func.sub.-- reg {
int (*sth.sub.-- copyin)();
/* Write-side copyin*/
int sth.sub.-- copyin.sub.-- threshold;
/* argument passed to sth.sub.-- copyin()*/
int (*sth.sub.-- 32.sub.-- to.sub.-- 64)();
/* Write-side 32-to-64 conversion
function*/
int (*sth.sub.-- write.sub.-- opt)();
/* Write-side optional function */
int (*sth.sub.-- read.sub.-- opt)();
/* Read-side optional function */
int (*sth.sub.-- ioctl.sub.-- opt)();
/* Ioctl optional function */
int (*sth.sub.-- 64.sub.-- to.sub.-- 32)();
/* Read-side 64-to-32 conversion
function */
Within stream head declaration (private structure), define the
following:
struct sth.sub.-- s {
. . .
struct sth.sub.-- func.sub.-- reg sth.sub.-- f.sub.-- reg;
. . .
}
______________________________________
FIG. 2 is a simplified flow diagram illustrating operation of the preferred embodiment of the invention. In the preferred embodiment, streams modules or drivers register functions via str.sub.-- func.sub.-- install(). str.sub.-- func.sub.-- install() may be called during module or driver installation, or at any time a module or driver decides to register functions. For example, driver A identifies 32-to-64 and 64-to-32 bit conversion functions for applications to use on the write and/or read paths. These functions will be registered when the driver is opened. This allows a single driver instance to simultaneously support thirty two and sixty four-bit applications. Similarly, if a push operation occurs, STREAMS will invoke the module's open routine and perform the stream head registration. As shown, a module identifies the sth.sub.-- 32.sub.-- to.sub.-- 64 function using a function pointer, which the module transmits to the controller of the stream head to be recorded. The module may disable the function by sending a message. For example, the module may disable the function by sending a message that results in a null evaluation of the record in the data structure, expressed as: sth-<sth.sub.-- func.sub.-- reg.sth.sub.-- 32.sub.-- to.sub.-- 64=NULL If two STREAMS components have registered message processing functions which conflict, e.g. two sth.sub.-- read.sub.-- opt() functions, the stream head structure will reflect the highest component's function within the stack; if there is no conflict, the stream head will reflect both component's functionality. A preferred calling sequence is as follows: str.sub.-- func.sub.-- install(struct sth.sub.-- func.sub.-- reg*func.sub.-- reg, int func.sub.-- mask); func.sub.-- mask is a bit mask composed of any of the following:
______________________________________
/* Flags for func.sub.-- mask */
#define FUNC.sub.-- REG.sub.-- COPYIN.sub.-- ENABLE
0X0001
#define FUNC.sub.-- REG.sub.-- COPYIN.sub.-- DISABLE
0X0002
#define FUNC.sub.-- REG.sub.-- 32.sub.-- TO.sub.-- 64.sub.-- ENABLE
0X0004
#define FUNC.sub.-- REG.sub.-- 32.sub.-- TO.sub.-- 64.sub.-- DISABLE
0X0008
#define FUNC.sub.-- REG.sub.-- WRITE.sub.-- OPT.sub.-- ENABLE
0X0010
#define FUNC.sub.-- REG.sub.-- WRITE.sub.-- OPT.sub.-- DISABLE
0X0020
#define FUNC.sub.-- REG.sub.-- READ.sub.-- OPT.sub.-- ENABLE
0X0040
#define FUNC.sub.-- REG.sub.-- READ.sub.-- OPT.sub.-- DISABLE
0X0080
#define FUNC.sub.-- REG.sub.-- 64.sub.-- TO.sub.-- 32.sub.-- ENABLE
0X0100
#define FUNC.sub.-- REG.sub.-- 64.sub.-- TO.sub.-- 32.sub.-- DISABLE
0X0200
#define FUNC.sub.-- REG.sub.-- IOCTL.sub.-- OPT.sub.-- ENABLE
0x0400
#define FUNC.sub.-- REG.sub.-- IOCTL.sub.-- OPT.sub.-- DISABLE
0x0800
______________________________________
This calling sequence specifies which functions are automatically registered at open time or push time. func.sub.-- req and func.sub.-- mask are stored in its module switch table entry:
______________________________________
struct modsw {
struct modsw * d.sub.-- next;
struct modsw * d.sub.-- prev;
char d.sub.-- namee[FMNAMESZ+1];
char d.sub.-- flags;
SQHP d.sub.-- sqh;
int d.sub.-- curstr
struct streamtab *
d.sub.-- str
struct streamtab *
d.sub.-- default.sub.-- alt
int d.sub.-- naltstr;
struct streamtab **
d.sub.-- altstr;
int d.sub.-- sq.sub.-- level;
int d.sub.-- refcnt;
int d.sub.-- major;
struct sth.sub.-- s *
d.sub.-- pmux.sub.-- top;
int d.sub.-- func.sub.-- mask
struct sth.sub.-- func.sub.-- reg *
d.sub.-- func.sub.-- reg;
};
______________________________________
A module or driver will activate or de-activate a registered message processing function by informing the stream head via a M.sub.-- SETOPTS message. This is done by extending the stroptions data structure which is defined within <stream.h>.
______________________________________
struct stroptions {
ulong so.sub.-- flags;
/* options to set*/
short so.sub.-- readopt;
/* read option*/
ushort so.sub.-- wroff
/* write offset*/
long so.sub.-- minpsz;
/* minimum read packet size*/
long so.sub.-- maxpsz;
/* maximum read packet size*/
ulong so.sub.-- hiwat
/* read queue high-water mark*/
ulong so.sub.-- Iowat;
/* read queue low-water mark*/
unsigned char so.sub.-- band;
/* band for water marks*/
};
______________________________________
By extending this data structure to the following, we maintain the original layout so that modules or drivers which do not support function registration will continue to work as before:
______________________________________
struct stroptions {
ulong so.sub.-- flags;
/* options to set*/
short so.sub.-- readopt;
/* read option*/
ushort so.sub.-- wroft;
/* write offset*/
long so.sub.-- minpsz;
/* minimum read packet size*/
long so.sub.-- maxpsz;
/* maximum read packet size*/
ulong so.sub.-- hiwat;
/* read queue high-water mark*/
ulong so.sub.-- Iowat;
/* read queue low-water mark*/
unsigned char
so.sub.-- band;
/* band for water marks*/
short so.sub.-- device.sub.-- id
/* driver or module identifier*/
short so.sub.-- func.sub.-- mask;
/* mask of enabled functions*/
______________________________________
so.sub.-- device.sub.-- id is the device identifier that is returned when the driver or module is installed within the system. For the preferred embodiment, this occurs via str.sub.-- install(). str.sub.-- install() will generate a unique so.sub.-- device.sub.-- id which the driver or module records in a global variable which can be utilized whenever it wishes to perform function registration. so.sub.-- func.sub.-- mask specifies which function(s) is to be activated or de-activated. If a bit is not defined, the corresponding registered message processing function will remain in effect. A module or driver has the capability of sending a specially adapted M.sub.-- SETOPTS message up at any time they choose in order to modify stream head characteristics. When this structure is decoded, the stream head code will examine the so.sub.-- func.sub.-- mask and record or nullify the corresponding stream head function address based on the flags set. If any functions are enabled or remain enabled, the stream head will set a flag to indicate function registration should be checked for during stream head message processing. For using the invention in conjunction with the system calls, the following discussions apply. Upon the write() system call, the following code will be added to the STREAMS implementation. The registered write function preferably includes a protocol dependent checksum off-load function which would combine the data movement from user-to-kernel space with an automatic checksum calculation. Using this technique, the invention continues to remain protocol independent while still adding value to the STREAMS framework. Preferably, such a combined copyin/checksum function should reference a threshold variable in order to improve performance. This threshold variable is stored as part of the original data structure and will be passed into the function. The function would then examine the uiop length fields and compare them with the threshold variable and determine if it is appropriate to perform the registered copyin function. The circumstances for not doing so are protocol dependent but an example case would be if the underlying protocol were only allowed to send small packets such as 512 bytes in length and the application were sending down data in 8 Kilo-Byte units. In such a case, better performance would be seen using the standard copyin and checksum processing instead of invoking 16 registered copyin/checksum function calls and the rest of the associated overhead. To determine the appropriate formula for when to invoke and such will require some experimentation with each protocol, but this is independent of the STREAMS framework and the proposed implementation. The following exemplary instruction code provides for operation of the invention in conjunction with the write() system call:
______________________________________
/* If functions have been registered, then controller executes proper
functions*/
if (sth->sth.sub.-- flags & F.sub.-- STH.sub.-- FUNC.sub.-- REG.sub.--
ENABLED) {
if (sth->sth.sub.-- f.sub.-- reg.sth.sub.-- copyin) {
error = (*sth->sth.sub.-- f.sub.-- reg.sth.sub.-- copyin)(mp,
w.sub.-- cnt, uiop,
sth->sth.sub.-- copyin.sub.-- threshold);
/*If EAGAIN, then copyin did not occur, so perform task normally*/
if (error == EAGAIN)
UIOMOVE.sub.-- WRITE(mp, cnt, uiop, error);
if (error)
goto error.sub.-- processing.sub.-- code;
}else {
UIOMOVE.sub.-- WRITE(mp, cnt, uiop, error);
/* controller executes 32 to 64 bit conversion jf defined and the caller
is a
* 32-bit application. This is performed on the mblk.
*/
if (sth->sth.sub.-- f.sub.-- reg.sth.sub.-- 32.sub.-- to.sub.-- 64 &&
uarea->32bit.sub.-- hint)
error = (*sth->sth.sub.-- f.sub.-- reg.sth.sub.-- 32.sub.-- to 64)(mp);
/* If the driver/module wants some other form of optional processing
* performed on outbound data, then execute this upon the mblk.
*/
if (sth->sth.sub.-- f.sub.-- reg.sth.sub.-- write.sub.-- opt)
error = (*sth->sth.sub.-- f.sub.-- reg.sth.sub.-- write.sub.-- opt)(mp);
}else{
/* The function is not defined so perform the original code*/
}
______________________________________
The rest of the write path code would be implemented normally. For putmsg()/putpmsg(), the STREAMS framework is extended as follows: putmsgo and putpmsg() allow the caller to specify control data which will be passed in a M.sub.-- PROTO or M.sub.-- PCPROTO message to the lower module or driver. This data may require conversion or post-processing once it is brought into the kernel. Examples of this would be a thirty two- bit application which was compiled using a thirty two-bit version of the TPI (Transport Provider Interface). This interface defines structures including data elements that are correctly interpreted by the invention as either thirty two-bit or sixty four-bit. These data elements would need to be converted into sixty four-bit data elements in order for a sixty four-bit system to provide the correct data interpretation. In addition, control data may require some other form of conversion once it has been brought into the kernel (keep in mind that normal data if brought in via a alternative copyin path may not be altered just like on the write() path). An example of suitable instruction code is as follows:
______________________________________
/* The control portion if being processed, is copied into the kernel
first.
* We simply perform the normal copyin activity and then
perform the post-
* processing as follows.
*/
if (error = COPYIN(user.sub.-- ptr, mp, cnt, ctl)) {
/* Perform error recovery*/
if (sth->sth.sub.-- flags & F.sub.-- STH.sub.-- FUNC.sub.-- REG.sub.--
ENABLED) {
if (sth->sth.sub.-- f.sub.-- reg.sth.sub.-- copyin){
error = (*sth->sth.sub.-- f.sub.-- reg sth.sub.-- copyin)(mp,
w.sub.-- cnt, uiop,
sth->sth.sub.-- copyin threshold);
/* If EAGAIN, then copyin did not occur, so perform task
normally*/
if (error == EAGAIN)
error = COPYIN(user.sub.-- ptr, mp, cnt, ctl);
if (error)
goto error.sub.-- processing.sub.-- code;
}else{
COPYIN(user.sub.-- ptr, mp, cnt, ctl);
}
/* Perform 32 to 64 bit conversion if defined and the caller is a
* 32-bit application.
*/
if (sth->sth.sub.-- f.sub.-- reg.sth.sub.-- 32.sub.-- to.sub.-- 64 &&
uarea->32bit.sub.-- hint)
error = (*sth->sth.sub.-- f.sub.-- reg.sth.sub.-- 32.sub.-- to
64)(mp);
/* If the driver/module wants some other form of optional processing
* performed on outbound data, then execute this upon the control
mblk.
*/
if (sth->sth.sub.-- f.sub.-- reg.sth.sub.-- write.sub.-- opt)
error = (*sth->sth.sub.-- f.sub.-- reg.sth.sub.-- write.sub.--
opt)(mp);
}else{
/* The function is not defined so perform the original code
______________________________________
*/
For the read() system call, the code would be similar in nature but its functionality would be replaced with the module/driver's required modified functionality. The present invention provides for a module or driver of a sixty four-bit kernel registering a function for adapting stream data interpretation, stream execution behavior, and the like in accordance with requirements of a thirty two-bit user process. In particular, the function examines the thread of the user process to determine whether the user processes requires a thirty two-bit format or a sixty four-bit format. If the user process requires a thirty two-bit format, the function converts the data format of the messages between a thirty two bit data format required by the user process and a sixty four bit data format required by the device driver. Similarly, if the application does not speak a currently employed version of a protocol that is initially accepted the connection, for example Internet Protocol (IP) version 6 instead of Internet Protocol (IP) version 4, then the data is converted to the correct protocol format at this time. The following code provides an example:
______________________________________
/* If functions have been registered, then controller executes proper
functions*/ if (sth->sth.sub.-- flags & F.sub.-- STH.sub.-- FUNC.sub.--
REG.sub.-- ENABLED) {
/* If the driver/module wants some other form of optional
processing
* performed on outbound data then execute this upon the mblk.
This
* could be where the conversion from IPv4 to/from IPv6 is
performed.
*/
if (sth->sth.sub.-- f.sub.-- reg.sth.sub.-- read.sub.-- opt)
error = (*sth->sth.sub.-- f.sub.-- reg.sth.sub.-- read.sub.--
opt)(mp);
/* If the calling application is a 32-bit application and there is
* conversion function registered then invoke it before moving the
* data to user space.
/*
if (sth->sth.sub.-- f.sub.-- reg.sth.sub.-- 64.sub.-- to.sub.-- 32
&& uarea->32 bit.sub.-- hint)
error = (*sth->sth.sub.-- f.sub.-- reg.sth.sub.-- 64.sub.--
to.sub.-- 32)(mp);
}
/* Move the data using the current implementation for moving code */
UIOMOVE.sub.-- READ(mp, cnt, uiop, error);
______________________________________
Using the invention in conjunction with getmsg() and getpmsg() system calls is similar to using the invention with read() system calls as discussed previously herein, additional code is included to handle the control data portion of the message. Example code is as follows:
______________________________________
/* If functions have been registered, then controller executes proper
functions*/ if (sth->sth.sub.-- flags & F.sub.-- STH.sub.-- FUNC.sub.--
REG.sub.-- ENABLED) {
/* If the driver/module wants some other form of optional processing
* performed on outbound data, then execute this upon the mblk.
This
* could be where the conversion from IPv4 tolfrom IPv6 is
performed.
*/
if (sth->sth.sub.-- f.sub.-- reg.sth.sub.-- read.sub.-- opt)
error = (*sth->sth.sub.-- f.sub.-- reg.sth.sub.-- read.sub.--
opt)(mp);
/*Next we apply the 32-bit conversion function to the
M.sub.-- DATA portion
* of the message if it exists and a function has been registered.
*/
if (sth->sth.sub.-- f.sub.-- reg.sth.sub.-- 64.sub.-- to.sub.--
32 && uarea->32bit.sub.-- hint)
error = (*sth->sth.sub.-- f.sub.-- reg.sth.sub.-- 64.sub.--
to.sub.-- 32)(mp);
error = COPYOUT(mp, user.sub.-- ptr, cnt, ctl);
______________________________________
Once the function registered processing has been completed, then move the data to the user-space using the normal execution path. For the ioctl path, depending upon whether data is being moved into or out of the kernel, similar steps are taken. The implementation assumes the optional ioctl function performs the data movement or could restrict it to simply a pre- or post-processing role as illustrated in the previous system calls. It should be understood that ioctis may communicate with different modules or drivers. Accordingly, if this optional structure is utilized, then the modules and drivers are constructed so as to have some understanding of how these messages should be interpreted--normally with the highest module/driver being the final arbiter. While this ioctl path is defined for completeness sake, it is recommended that ioctls use the per message facility instead, since this allows each module or driver processing an ioctl to target the particular message. Example code is as follows.
______________________________________
if (sth->sth.sub.-- flags & F.sub.-- STH.sub.-- FUNC.sub.-- REG.sub.--
ENABLED &&
sth->sth.sub.-- f.sub.-- reg.sth.sub.-- ioctl.sub.-- opt)
error = (*sth->sth.sub.-- f.sub.-- reg.sth.sub.-- ioctl.sub.-- opt)(mp,
direction.sub.-- hint);
______________________________________
Additionally, the invention provides for a module or driver identifying a function and for controlling execution at the stream head in response to dynamic changes in state of the module or driver within the kernel. For example, the invention provides for identifying a function to convert messages already enqueued on the stream head from normal priority messages into expedited messages, in response to priority state changes of the module or driver for new information received from a network connection. This elimates any need to modify the user application or an interface library to provide the desired functionality. In an alternative embodiment of the invention, the STREAMS framework is extended is to support the ability of a module or driver to perform function registration on a per message basis on the inbound path, i.e. read, getmsg, getpmsg, ioctl for M.sub.-- COPYOUT. This functionality is provided because a module or driver may not always require a set of functions to be invoked on every message sent upstream, but only one packet in a number, N. This functionality is added for flexibility purposes; the underlying concept of what such a function would provide is still the same. Such functions would be invoked to convert data based on the application involved. The most common use, and for which we will always pass in a hint as a parameter is for sixty four bit to thirty two bit conversion. First, the message structure used to describe the user data is suitably modified. This data structure is defined within
______________________________________
struct msgb{
struct msgb*
b.sub.-- next
/* next message on queue*/
struct msgb*
b.sub.-- prev;
/* previous message on queue*/
struct msgb*
b.sub.-- cont;
/* next message block of message*/
unsigned char*
b.sub.-- rptr;
/* first unread data byte in buffer*/
unsigned char*
b.sub.-- wptr
/* first unwritten data byte*/
struct datab*
b.sub.-- datap;
/* data block*/
unsigned char
b.sub.-- band;
/* message priority*/
unsigned char
b.sub.-- pad1;
unsigned short
b.sub.-- flag;
/* message flags*/
(int32*) b.sub.-- func
/* function to be invoked*/
MSG.sub.-- KERNEL.sub.-- FIELDS
};
______________________________________
Additionally, a new flag within the msgb->b.sub.-- flag field is defined. This new flag is used to indicate that the function which is stored within the msgb structure should be invoked upon the user data when it is being moved from the kernel to user space via an application system call: #define MSGFUNCREG 0.times.2000 /*Execute the embedded function on the data */ An alternative implementation would be to set the b.sub.-- flag field but with a flag which indicates which registered message processing function to invoke. This would also require the driver/module to include a device.sub.-- id value within a new msgb field b.sub.-- device.sub.-- id.
______________________________________
#define MSGREADOPT 0x6000
#define MSGIOCTLOPT 0xa000
#define MSG64TO32 0xb000
______________________________________
The previously described code is modified as follows: On the read, getmsg, getpmsg, and ioctl paths, the code would contain the following two lines which would be executed by the controller:
______________________________________
/* Check it per message processing has been enabled*/
if (mp->b.sub.-- flag & MSGFUNCREG)
error = (*mp->b.sub.-- func)(mp, uarea->32bithint);
/* If functions have been registered, then execute proper functions*/
if (sth->sth.sub.-- flags & F.sub.-- STH.sub.-- FUNC.sub.-- REG.sub.--
ENABLED){
. . .
An alternative implementation would be:
/* Check if per message processing has been enabled*/
if (mp->b.sub.-- flag & MSGFUNCREG) {
error = (*(find.sub.-- func(b.sub.-- device.sub.-- id b.sub.--
flag))(mp, uarea->32bithint);
/* If functions have been registered, then execute proper functions*/
if (sth->sth.sub.-- flags & F.sub.-- STH.sub.-- FUNC.sub.-- REG.sub.--
ENABLED) {
______________________________________
Various structures and codings for these register functions may be used with beneficial results. An example of a simple structure is as follows:
______________________________________
int32
drv.sub.-- a.sub.-- 32.sub.-- to.sub.-- 64(MBLKP mp)
/* Check the message type to determine the conversion code to
execute.
* For each message type there would be a set of conversion code.
* Optimalty, each driver or module should create an ABI
(Application
* Binary Interface) for communicating information
between the application
* and itself. By doing so, the conversion can be done on a per
message
* basis without creating cumbersome code to understand all
* message types. This conversion code would be re-used
* to reverse the conversion on the inbound 64.sub.-- to.sub.-- 32
* path if the calling application instance is a 32-bit
* compiled application.
*
* For other types of optional manipulation or copyin/checksum,
there
* might be similar code since the movement/manipulation of the
* data may be on a per message type need. For example, we usually
* do not need a copyin/checksum for an ioctl path (though we
* could use one); instead we may want a copyin/modification
* utility that performs some automatic
* conversion of the data during movement from user-to-kernel
* or kernel-to-user without having to perform the
* given optional execution paths.
*/
switch(mp->b.sub.-- datap->db.sub.-- type) {
case M.sub.-- DATA:
/* Perform conversion based on a pre-defined driver ABI*/
break;
case M.sub.-- PROTO:
/* Perform conversion based on a pre-defined driver ABI*/
break;
case M.sub.-- PCPROTO:
/* Perform conversion based on a pre-defined driver ABI*/
break;
case M.sub.-- IOCTL:
/* Perform conversion based on a pre-defined driver ABI*/
break;
case M.sub.-- COPYIN:
/* Perform conversion based on a pre-defined driver ABI*/
break;
case M.sub.-- COPYOUT:
/* Perform conversion based on a pre-defined driver ABI*/
break;
default:
/* do nothing*/
}
}
______________________________________
As discussed, the invention provides an apparatus and method for kernel level modules or drivers identifying message processing functions and for controlling execution of the message processing functions at the stream head. Although specific embodiments of the invention have been described and illustrated, the invention is not to be limited to the specific forms or arrangements of parts so described and illustrated, and various modifications and changes can be made without departing from the scope and spirit of the invention. Within the scope of the appended claims, therefore, the invention may be practiced otherwise than as specifically described and illustrated.
|
Same subclass Same class Consider this |
||||||||||
