FS support for grant-based i/o.

For character device i/o, FS does a so-called 'magic' grant to let the
driver copy from or to user space. As this is done in FS address space,
the driver is told to do this in FS address space. The redirection to
the right user process then happens at copy-time in the kernel, using the
FS grant table. This also happens for DEV_READ and DEV_WRITE on block
devices.

For other block device i/o, which happens from/to FS buffers, FS does
a 'direct' grant to its own address space for the driver.

After the i/o returns, this access has to be K-I-L-L-E-D, revoked.
Sometimes this is after a SUSPEND and DEV_REVIVE, in which case the
revoking happens in pipe.c.

This conversion happens in safe_io_conversion() in device.c, called
by dev_io and dev_bio.

FS has to pre-allocate its own space for these grant tables. This happens
in main.c.
This commit is contained in:
Ben Gras 2006-06-20 10:12:09 +00:00
parent 54f1e6d3d4
commit 2384a85296
8 changed files with 307 additions and 61 deletions

View file

@ -22,7 +22,7 @@ OBJ = main.o open.o read.o write.o pipe.o dmap.o \
install all build: $(SERVER)
$(SERVER): $(OBJ)
$(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS)
install -S 512w $@
install -S 1024w $@
# clean up local files
clean:

View file

@ -70,6 +70,7 @@ int only_search; /* if NO_READ, don't read, else act normal */
/* This block is not the one sought. */
bp = bp->b_hash; /* move to next block on hash chain */
}
}
}
@ -272,7 +273,7 @@ int rw_flag; /* READING or WRITING */
if ( (dev = bp->b_dev) != NO_DEV) {
pos = (off_t) bp->b_blocknr * block_size;
op = (rw_flag == READING ? DEV_READ : DEV_WRITE);
r = dev_bio(op, dev, FS_PROC_NR, bp->b_data, pos, block_size, 0);
r = dev_bio(op, dev, FS_PROC_NR, bp->b_data, pos, block_size);
if (r != block_size) {
if (r >= 0) r = END_OF_FILE;
if (r != END_OF_FILE)
@ -376,7 +377,7 @@ int rw_flag; /* READING or WRITING */
}
r = dev_bio(rw_flag == WRITING ? DEV_SCATTER : DEV_GATHER,
dev, FS_PROC_NR, iovec,
(off_t) bufq[0]->b_blocknr * block_size, j, 0);
(off_t) bufq[0]->b_blocknr * block_size, j);
/* Harvest the results. Dev_io reports the first error it may have
* encountered, but we only care if it's the first block that failed.

View file

@ -19,9 +19,11 @@
#include "fs.h"
#include <fcntl.h>
#include <assert.h>
#include <minix/callnr.h>
#include <minix/com.h>
#include <minix/endpoint.h>
#include <minix/ioctl.h>
#include "file.h"
#include "fproc.h"
#include "inode.h"
@ -30,6 +32,11 @@
#define ELEMENTS(a) (sizeof(a)/sizeof((a)[0]))
FORWARD _PROTOTYPE( int safe_io_conversion, (endpoint_t, int,
cp_grant_id_t *, int *, cp_grant_id_t *, int, endpoint_t *,
void *, int *, vir_bytes, off_t *));
FORWARD _PROTOTYPE( void safe_io_cleanup, (cp_grant_id_t, cp_grant_id_t *, int));
extern int dmap_size;
PRIVATE int dummyproc;
@ -72,12 +79,34 @@ dev_t dev; /* device to close */
}
/*===========================================================================*
* dev_status *
* suspended_ep *
*===========================================================================*/
endpoint_t suspended_ep(endpoint_t driver, cp_grant_id_t g)
{
/* A process is suspended on a driver for which FS issued
* a grant. Find out which process it was.
*/
struct fproc *rfp;
for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) {
if(rfp->fp_pid == PID_FREE)
continue;
if(rfp->fp_suspended == SUSPENDED &&
rfp->fp_task == -driver && rfp->fp_grant == g) {
return rfp->fp_endpoint;
}
}
return NONE;
}
/*===========================================================================*
* dev_status *
*===========================================================================*/
PUBLIC void dev_status(message *m)
{
message st;
int d, get_more = 1;
endpoint_t endpt;
for(d = 0; d < NR_DEVICES; d++)
if (dmap[d].dmap_driver != NONE &&
@ -100,7 +129,18 @@ PUBLIC void dev_status(message *m)
switch(st.m_type) {
case DEV_REVIVE:
revive(st.REP_ENDPT, st.REP_STATUS);
endpt = st.REP_ENDPT;
if(endpt == FS_PROC_NR) {
endpt = suspended_ep(m->m_source,
st.REP_IO_GRANT);
if(endpt == NONE) {
printf("FS: proc with "
"grant %d not found (revive)\n",
st.REP_IO_GRANT);
continue;
}
}
revive(endpt, st.REP_STATUS);
break;
case DEV_IO_READY:
select_notified(d, st.DEV_MINOR, st.DEV_SEL_OPS);
@ -117,21 +157,143 @@ PUBLIC void dev_status(message *m)
return;
}
/*===========================================================================*
* safe_io_conversion *
*===========================================================================*/
PRIVATE int safe_io_conversion(driver, dev, gid, op, gids, gids_size,
io_ept, buf, vec_grants, bytes, pos)
endpoint_t driver;
int dev;
cp_grant_id_t *gid;
int *op;
cp_grant_id_t *gids;
int gids_size;
endpoint_t *io_ept;
void *buf;
int *vec_grants;
vir_bytes bytes;
off_t *pos;
{
int access = 0, size;
int m, j;
iovec_t *v;
/* Is this device driver (identified by major number)
* ready to accept *_S commands?
*/
if(major(dev) == 7) /* major number of inet. */
return 0; /* inet is not safe-capable. */
/* Number of grants allocated in vector I/O. */
*vec_grants = 0;
/* Driver can handle it - change request to a safe one. */
*gid = GRANT_INVALID;
switch(*op) {
case DEV_READ:
case DEV_WRITE:
/* Change to safe op. */
*op = *op == DEV_READ ? DEV_READ_S : DEV_WRITE_S;
if((*gid=cpf_grant_magic(driver, *io_ept,
(vir_bytes) buf, bytes,
*op == DEV_READ_S ? CPF_WRITE : CPF_READ)) < 0) {
panic(__FILE__,
"cpf_grant_magic of buffer failed\n", NO_NUM);
}
break;
case DEV_GATHER:
case DEV_SCATTER:
/* Change to safe op. */
*op = *op == DEV_GATHER ? DEV_GATHER_S : DEV_SCATTER_S;
/* Grant access to i/o vector. */
if((*gid = cpf_grant_direct(driver, (vir_bytes)
buf, bytes * sizeof(iovec_t),
CPF_READ | CPF_WRITE)) < 0) {
panic(__FILE__,
"cpf_grant_direct of vector failed", NO_NUM);
}
v = (iovec_t *) buf;
/* Grant access to i/o buffers. */
for(j = 0; j < bytes; j++) {
gids[j] = v[j].iov_addr =
cpf_grant_direct(driver, (vir_bytes)
v[j].iov_addr, v[j].iov_size,
*op == DEV_GATHER_S ? CPF_WRITE : CPF_READ);
if(!GRANT_VALID(gids[j])) {
panic(__FILE__, "grant to iovec buf failed",
NO_NUM);
}
(*vec_grants)++;
}
break;
case DEV_IOCTL:
*pos = *io_ept; /* Old endpoint in POSITION field. */
*op = DEV_IOCTL_S;
if(_MINIX_IOCTL_IOR(m_in.REQUEST)) access |= CPF_WRITE;
if(_MINIX_IOCTL_IOW(m_in.REQUEST)) access |= CPF_READ;
size = _MINIX_IOCTL_SIZE(m_in.REQUEST);
if(access && size > 0) {
if((*gid=cpf_grant_magic(driver, *io_ept,
(vir_bytes) buf, size, access)) < 0) {
panic(__FILE__,
"cpf_grant_magic failed (ioctl)\n",
NO_NUM);
}
}
}
/* If we have converted to a safe operation, I/O
* endpoint becomes FS if it wasn't already.
*/
if(GRANT_VALID(*gid)) {
*io_ept = FS_PROC_NR;
return 1;
}
/* Not converted to a safe operation (because there is no
* copying involved in this operation).
*/
return 0;
}
/*===========================================================================*
* safe_io_cleanup *
*===========================================================================*/
PRIVATE void safe_io_cleanup(gid, gids, gids_size)
cp_grant_id_t gid;
cp_grant_id_t *gids;
int gids_size;
{
/* Free resources (specifically, grants) allocated by safe_io_conversion(). */
int j;
cpf_revoke(gid);
for(j = 0; j < gids_size; j++)
cpf_revoke(gids[j]);
return;
}
/*===========================================================================*
* dev_bio *
*===========================================================================*/
PUBLIC int dev_bio(op, dev, proc_e, buf, pos, bytes, flags)
PUBLIC int dev_bio(op, dev, proc_e, buf, pos, bytes)
int op; /* DEV_READ, DEV_WRITE, DEV_IOCTL, etc. */
dev_t dev; /* major-minor device number */
int proc_e; /* in whose address space is buf? */
void *buf; /* virtual address of the buffer */
off_t pos; /* byte position */
int bytes; /* how many bytes to transfer */
int flags; /* special flags, like O_NONBLOCK */
{
/* Read or write from a device. The parameter 'dev' tells which one. */
struct dmap *dp;
int r;
int r, safe;
message m;
/* Determine task dmap. */
@ -139,24 +301,40 @@ int flags; /* special flags, like O_NONBLOCK */
for (;;)
{
static cp_grant_id_t gids[NR_IOREQS];
cp_grant_id_t gid = GRANT_INVALID;
int vec_grants;
/* See if driver is roughly valid. */
if (dp->dmap_driver == NONE) {
printf("FS: dev_io: no driver for dev %x\n", dev);
return ENXIO;
}
/* Set up the message passed to task. */
/* By default, these are right. */
m.IO_ENDPT = proc_e;
m.ADDRESS = buf;
/* Convert parameters to 'safe mode'. */
safe = safe_io_conversion(dp->dmap_driver, dev, &gid,
&op, gids, NR_IOREQS, &m.IO_ENDPT, buf, &vec_grants, bytes, &pos);
/* Set up rest of the message. */
if(safe) m.IO_GRANT = (char *) gid;
m.m_type = op;
m.DEVICE = (dev >> MINOR) & BYTE;
m.POSITION = pos;
m.IO_ENDPT = proc_e;
m.ADDRESS = buf;
m.COUNT = bytes;
m.TTY_FLAGS = flags;
m.HIGHPOS = 0;
/* Call the task. */
(*dp->dmap_io)(dp->dmap_driver, &m);
/* As block I/O never SUSPENDs, safe cleanup must be done whether
* the I/O succeeded or not.
*/
if(safe) safe_io_cleanup(gid, gids, vec_grants);
if(dp->dmap_driver == NONE) {
/* Driver has vanished. Wait for a new one. */
for (;;)
@ -217,9 +395,13 @@ int flags; /* special flags, like O_NONBLOCK */
/* Read or write from a device. The parameter 'dev' tells which one. */
struct dmap *dp;
message dev_mess;
cp_grant_id_t gid = GRANT_INVALID;
static cp_grant_id_t gids[NR_IOREQS];
int vec_grants = 0, orig_op, safe;
/* Determine task dmap. */
dp = &dmap[(dev >> MAJOR) & BYTE];
orig_op = op;
/* See if driver is roughly valid. */
if (dp->dmap_driver == NONE) {
@ -233,38 +415,71 @@ int flags; /* special flags, like O_NONBLOCK */
return ENXIO;
}
/* Set up the message passed to task. */
/* By default, these are right. */
dev_mess.IO_ENDPT = proc_e;
dev_mess.ADDRESS = buf;
/* Convert DEV_* to DEV_*_S variants. */
safe = safe_io_conversion(dp->dmap_driver, dev, &gid,
&op, gids, NR_IOREQS, &dev_mess.IO_ENDPT, buf, &vec_grants, bytes, &pos);
/* If the safe conversion was done, set the ADDRESS to
* the grant id.
*/
if(safe) dev_mess.IO_GRANT = (char *) gid;
/* Set up the rest of the message passed to task. */
dev_mess.m_type = op;
dev_mess.DEVICE = (dev >> MINOR) & BYTE;
dev_mess.POSITION = pos;
dev_mess.IO_ENDPT = proc_e;
dev_mess.ADDRESS = buf;
dev_mess.COUNT = bytes;
dev_mess.TTY_FLAGS = flags;
dev_mess.HIGHPOS = 0;
/* This field will be used if the i/o is suspended. */
fp->fp_ioproc = dev_mess.IO_ENDPT;
/* Call the task. */
(*dp->dmap_io)(dp->dmap_driver, &dev_mess);
if(dp->dmap_driver == NONE) {
/* Driver has vanished. */
printf("Driver gone?\n");
if(safe) safe_io_cleanup(gid, gids, vec_grants);
return EIO;
}
/* Task has completed. See if call completed. */
if (dev_mess.REP_STATUS == SUSPEND) {
if(vec_grants > 0) {
panic(__FILE__,"SUSPEND on vectored i/o", NO_NUM);
}
if (flags & O_NONBLOCK) {
/* Not supposed to block. */
dev_mess.m_type = CANCEL;
dev_mess.IO_ENDPT = proc_e;
dev_mess.IO_ENDPT = fp->fp_ioproc;
dev_mess.IO_GRANT = (char *) gid;
/* This R_BIT/W_BIT check taken from suspend()/unpause()
* logic. Mode is expected in the COUNT field.
*/
dev_mess.COUNT = 0;
if(call_nr == READ) dev_mess.COUNT = R_BIT;
else if(call_nr == WRITE) dev_mess.COUNT = W_BIT;
dev_mess.DEVICE = (dev >> MINOR) & BYTE;
(*dp->dmap_io)(dp->dmap_driver, &dev_mess);
if (dev_mess.REP_STATUS == EINTR) dev_mess.REP_STATUS = EAGAIN;
} else {
/* Suspend user. */
suspend(dp->dmap_driver);
assert(!GRANT_VALID(fp->fp_grant));
fp->fp_grant = gid; /* revoke this when unsuspended. */
return(SUSPEND);
}
}
/* No suspend, or cancelled suspend, so I/O is over and can be cleaned up. */
if(safe) safe_io_cleanup(gid, gids, vec_grants);
return(dev_mess.REP_STATUS);
}
@ -389,40 +604,6 @@ PUBLIC int do_ioctl()
&& (rip->i_mode & I_TYPE) != I_BLOCK_SPECIAL) return(ENOTTY);
dev = (dev_t) rip->i_zone[0];
#if ENABLE_BINCOMPAT
if ((m_in.TTY_REQUEST >> 8) == 't') {
/* Obsolete sgtty ioctl, message contains more than is sane. */
struct dmap *dp;
message dev_mess;
dp = &dmap[(dev >> MAJOR) & BYTE];
dev_mess = m; /* Copy full message with all the weird bits. */
dev_mess.m_type = DEV_IOCTL;
dev_mess.PROC_NR = who_e;
dev_mess.TTY_LINE = (dev >> MINOR) & BYTE;
/* Call the task. */
if (dp->dmap_driver == NONE) {
printf("FS: do_ioctl: no driver for dev %x\n", dev);
return ENXIO;
}
if(isokendpt(dp->dmap_driver, &dummyproc) != OK) {
printf("FS: do_ioctl: old driver for dev %x (%d)\n",
dev, dp->dmap_driver);
return ENXIO;
}
(*dp->dmap_io)(dp->dmap_driver, &dev_mess);
m_out.TTY_SPEK = dev_mess.TTY_SPEK; /* erase and kill */
m_out.TTY_FLAGS = dev_mess.TTY_FLAGS; /* flags */
return(dev_mess.REP_STATUS);
}
#endif
return(dev_io(DEV_IOCTL, dev, who_e, m_in.ADDRESS, 0L,
m_in.REQUEST, f->filp_flags));
}
@ -494,10 +675,14 @@ message *mess_ptr; /* pointer to message for task */
/* Did the process we did the sendrec() for get a result? */
if (mess_ptr->REP_ENDPT == proc_e) {
break;
} else if (mess_ptr->m_type == REVIVE) {
}
#if 0
else if (mess_ptr->m_type == REVIVE) {
/* Otherwise it should be a REVIVE. */
revive(mess_ptr->REP_ENDPT, mess_ptr->REP_STATUS);
} else {
}
#endif
else {
printf(
"fs: strange device reply from %d, type = %d, proc = %d (2) ignored\n",
mess_ptr->m_source,

View file

@ -1,4 +1,5 @@
#include <sys/select.h>
#include <minix/safecopies.h>
/* This is the per-process information. A slot is reserved for each potential
* process. Thus NR_PROCS must be the same as in the kernel. It is not
@ -22,11 +23,13 @@ EXTERN struct fproc {
char fp_suspended; /* set to indicate process hanging */
char fp_revived; /* set to indicate process being revived */
int fp_task; /* which task is proc suspended on */
endpoint_t fp_ioproc; /* proc no. in suspended-on i/o message */
cp_grant_id_t fp_grant; /* revoke this grant on unsuspend if > -1 */
char fp_sesldr; /* true if proc is a session leader */
char fp_execced; /* true if proc has exec()ced after fork */
pid_t fp_pid; /* process id */
long fp_cloexec; /* bit map for POSIX Table 6-2 FD_CLOEXEC */
int fp_endpoint; /* kernel endpoint number of this process */
endpoint_t fp_endpoint; /* kernel endpoint number of this process */
} fproc[NR_PROCS];
/* Field values. */

View file

@ -15,6 +15,7 @@ struct super_block; /* proto.h needs to know this */
#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <assert.h>
#include <stdlib.h>
#include <sys/ioc_memory.h>
#include <sys/svrctl.h>
@ -24,6 +25,7 @@ struct super_block; /* proto.h needs to know this */
#include <minix/keymap.h>
#include <minix/const.h>
#include <minix/endpoint.h>
#include <minix/safecopies.h>
#include "buf.h"
#include "file.h"
#include "fproc.h"
@ -136,6 +138,10 @@ PRIVATE void get_work()
rp->fp_suspended = NOT_SUSPENDED; /*no longer hanging*/
rp->fp_revived = NOT_REVIVING;
reviving--;
/* This should be a pipe I/O, not a device I/O.
* If it is, it'll 'leak' grants.
*/
assert(!GRANT_VALID(rp->fp_grant));
return;
}
panic(__FILE__,"get_work couldn't revive anyone", NO_NUM);
@ -219,6 +225,22 @@ PRIVATE void fs_init()
message mess;
int s;
/* Maximum number of outstanding grants is one full i/o
* vector, and all processes hanging on SUSPENDed i/o, and
* grants for printf() to tty and log.
*
* Space is declared for it here, and cpf_preallocate()
* uses it internally and internally tells the kernel
* about it. FS then never touches the data again directly,
* only through the cpf_* library routines.
*/
#define NGRANTS (NR_PROCS + NR_IOREQS + 10)
static cp_grant_t grants[NGRANTS];
/* Set data copy grant table, as FS can't allocate it dynamically. */
if(cpf_preallocate(grants, NGRANTS) != OK)
panic(__FILE__,"cpf_preallocate failed", NO_NUM);
/* Initialize the process table with help of the process manager messages.
* Expect one message for each system process with its slot number and pid.
* When no more processes follow, the magic process number NONE is sent.
@ -237,6 +259,7 @@ PRIVATE void fs_init()
rfp->fp_realgid = (gid_t) SYS_GID;
rfp->fp_effgid = (gid_t) SYS_GID;
rfp->fp_umask = ~0;
rfp->fp_grant = GRANT_INVALID;
} while (TRUE); /* continue until process NONE */
mess.m_type = OK; /* tell PM that we succeeded */
@ -334,8 +357,9 @@ PRIVATE void service_pm()
{
panic("fs", "service_pm: sendrec failed", r);
}
if (m.m_type == PM_IDLE)
if (m.m_type == PM_IDLE) {
break;
}
call= m.m_type;
switch(call)
{

View file

@ -20,8 +20,10 @@
#include "fs.h"
#include <fcntl.h>
#include <assert.h>
#include <unistd.h> /* cc runs out of memory with unistd.h :-( */
#include <minix/callnr.h>
#include <minix/safecopies.h>
#include <minix/endpoint.h>
#include <minix/com.h>
#include <sys/ptrace.h>
@ -351,6 +353,12 @@ int cpid; /* Child process id */
cp->fp_pid = cpid;
cp->fp_endpoint = cproc;
/* A forking process never has an outstanding grant,
* as it isn't blocking on i/o.
*/
assert(!GRANT_VALID(fp->fp_grant));
assert(!GRANT_VALID(cp->fp_grant));
/* A child is not a process leader. */
cp->fp_sesldr = 0;
@ -661,8 +669,9 @@ struct mem_map *seg_ptr;
r= sys_trace(T_GETUSER, proc_e, trace_off, &trace_data);
if (r != OK)
{
printf("dumpcore: sys_trace failed at offset %d: %d\n",
trace_off, r);
printf("dumpcore pid %d: sys_trace failed "
"at offset %d: %d\n",
rfp->fp_pid, trace_off, r);
break;
}
r= write_bytes(rip, off, (char *)&trace_data,

View file

@ -18,6 +18,7 @@
#include "fs.h"
#include <fcntl.h>
#include <signal.h>
#include <assert.h>
#include <minix/callnr.h>
#include <minix/endpoint.h>
#include <minix/com.h>
@ -192,6 +193,7 @@ int task; /* who is proc waiting for? (PIPE = pipe) */
if (task == XPIPE || task == XPOPEN) susp_count++;/* #procs susp'ed on pipe*/
fp->fp_suspended = SUSPENDED;
assert(!GRANT_VALID(fp->fp_grant));
fp->fp_fd = m_in.fd << 8 | call_nr;
if(task == NONE)
panic(__FILE__,"suspend on NONE",NO_NUM);
@ -316,8 +318,20 @@ int returned; /* if hanging on task, how many bytes read */
else if (task == XSELECT) {
reply(proc_nr_e, returned);
} else {
/* Revive a process suspended on TTY or other device. */
rfp->fp_nbytes = returned; /*pretend it wants only what there is*/
/* Revive a process suspended on TTY or other device.
* Pretend it wants only what there is.
*/
rfp->fp_nbytes = returned;
/* If a grant has been issued by FS for this I/O, revoke
* it again now that I/O is done.
*/
if(GRANT_VALID(rfp->fp_grant)) {
if(cpf_revoke(rfp->fp_grant)) {
panic(__FILE__,"FS: revoke failed for grant",
rfp->fp_grant);
}
rfp->fp_grant = GRANT_INVALID;
}
reply(proc_nr_e, returned); /* unblock the process */
}
}
@ -386,13 +400,21 @@ int proc_nr_e;
f = rfp->fp_filp[fild];
dev = (dev_t) f->filp_ino->i_zone[0]; /* device hung on */
mess.TTY_LINE = (dev >> MINOR) & BYTE;
mess.IO_ENDPT = proc_nr_e;
mess.IO_ENDPT = rfp->fp_ioproc;
mess.IO_GRANT = (char *) rfp->fp_grant;
/* Tell kernel R or W. Mode is from current call, not open. */
mess.COUNT = (rfp->fp_fd & BYTE) == READ ? R_BIT : W_BIT;
mess.m_type = CANCEL;
fp = rfp; /* hack - ctty_io uses fp */
(*dmap[(dev >> MAJOR) & BYTE].dmap_io)(task, &mess);
if(GRANT_VALID(rfp->fp_grant)) {
if(cpf_revoke(rfp->fp_grant)) {
panic(__FILE__,"FS: revoke failed for grant (cancel)",
rfp->fp_grant);
}
rfp->fp_grant = GRANT_INVALID;
}
}
rfp->fp_suspended = NOT_SUSPENDED;

View file

@ -2,6 +2,8 @@
#include "timers.h"
#include <minix/safecopies.h>
/* Structs used in prototypes must be declared as such first. */
struct buf;
struct filp;
@ -31,7 +33,7 @@ _PROTOTYPE( void invalidate2, (Dev_t device) );
_PROTOTYPE( int dev_open, (Dev_t dev, int proc, int flags) );
_PROTOTYPE( void dev_close, (Dev_t dev) );
_PROTOTYPE( int dev_bio, (int op, Dev_t dev, int proc, void *buf,
off_t pos, int bytes, int flags) );
off_t pos, int bytes) );
_PROTOTYPE( int dev_io, (int op, Dev_t dev, int proc, void *buf,
off_t pos, int bytes, int flags) );
_PROTOTYPE( int gen_opcl, (int op, Dev_t dev, int proc, int flags) );