1219 lines
32 KiB
C
1219 lines
32 KiB
C
static char sccsid[] = "@(#)14 1.50.1.2 src/bos/kernel/ios/select.c, sysios, bos41J, 9516B_all 4/19/95 10:36:30";
|
|
/*
|
|
* COMPONENT_NAME: (SYSIOS) Select system call
|
|
*
|
|
* FUNCTIONS: chk_timeout select
|
|
*
|
|
* ORIGINS: 26, 27, 83
|
|
*
|
|
* This module contains IBM CONFIDENTIAL code. -- (IBM
|
|
* Confidential Restricted when combined with the aggregated
|
|
* modules for this product)
|
|
* SOURCE MATERIALS
|
|
* (C) COPYRIGHT International Business Machines Corp. 1989, 1993
|
|
* All Rights Reserved
|
|
*
|
|
* US Government Users Restricted Rights - Use, duplication or
|
|
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
|
|
*
|
|
* Copyright (c) 1982, 1986 Regents of the University of California.
|
|
* All rights reserved. The Berkeley software License Agreement
|
|
* specifies the terms and conditions for redistribution.
|
|
*/
|
|
/*
|
|
* LEVEL 1,5 Years Bull Confidential Information
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/select.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/device.h>
|
|
#include <sys/user.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/sleep.h>
|
|
#include <sys/intr.h>
|
|
#include <sys/syspest.h>
|
|
#include "selpoll.h"
|
|
#include <sys/thread.h>
|
|
#include <sys/uthread.h>
|
|
#include <sys/atomic_op.h>
|
|
|
|
/*
|
|
* Turn ON the specified bit in the elem-th element in 'array'.
|
|
*/
|
|
#define SETBIT(array_elem, bit) \
|
|
(array_elem |= (1 << bit))
|
|
|
|
/*
|
|
* Turn OFF the specified bit in the elem-th element in 'array'.
|
|
*/
|
|
#define CLRBIT(array_elem, bit) \
|
|
(array_elem &= ~(1 << bit))
|
|
|
|
/*
|
|
* See if the specified bit in the elem-th element of 'array' is ON.
|
|
*/
|
|
#define ISSET(array_elem, bit) \
|
|
(array_elem & (1 << bit))
|
|
|
|
/*
|
|
* See if the specified bit in the elem-th element of 'array' is OFF.
|
|
*/
|
|
#define ISCLR(array_elem, bit) \
|
|
((array_elem & (1 << bit)) == 0)
|
|
|
|
/*
|
|
* NAME: chk_timeout
|
|
*
|
|
* FUNCTION: Check the validity of the values in a timeval structure.
|
|
*
|
|
* EXECUTION ENVIRONMENT:
|
|
* This routine is called by select.
|
|
* It is pageable.
|
|
*
|
|
* NOTES:
|
|
*
|
|
* DATA STRUCTURES: timeval
|
|
*
|
|
* RETURN VALUES DESCRIPTION: 0 upon successful completion;
|
|
* -1 if timeval struct contained negative seconds
|
|
* or microseconds values.
|
|
*
|
|
* EXTERNAL PROCEDURES CALLED: none.
|
|
*/
|
|
int
|
|
chk_timeout(timeout, reqev_mskp)
|
|
|
|
struct timeval *timeout; /* length of time to wait for events */
|
|
int *reqev_mskp; /* requested event mask */
|
|
{
|
|
if (timeout == NULL)
|
|
{
|
|
return(0);
|
|
}
|
|
if ((timeout->tv_sec < 0) || (timeout->tv_usec < 0) ||
|
|
(timeout->tv_usec >= uS_PER_SECOND))
|
|
{
|
|
/*
|
|
* Negative time values are invalid.
|
|
*/
|
|
return(-1);
|
|
}
|
|
else if ((timeout->tv_sec == NO_TIMEOUT) && (timeout->tv_usec == NO_TIMEOUT))
|
|
{
|
|
/*
|
|
* Zero timeout value specified, so we will not wait.
|
|
*/
|
|
*reqev_mskp = POLLSYNC;
|
|
}
|
|
|
|
return(0);
|
|
|
|
} /* end chk_timeout */
|
|
|
|
/*
|
|
* NAME: select
|
|
*
|
|
* FUNCTION: The SELECT system call is used to check the I/O status of
|
|
* multiple file descriptors and message queues to see if they are
|
|
* ready for reading (receiving) or writing (sending), or if they
|
|
* have an exceptional condition pending.
|
|
*
|
|
* EXECUTION ENVIRONMENT:
|
|
* This routine is called by user programs.
|
|
* It is pageable.
|
|
*
|
|
* NOTES: nfdsmsgs is an integer specifying the number of file descriptors
|
|
* and message queues to check. The low order 16 bits give the length of a
|
|
* bit mask which indicates which file descriptors to check; the high order
|
|
* 16 bits give the size of an array that contains message queue
|
|
* identifiers.
|
|
*
|
|
* DATA STRUCTURES: sellist_2, thread, uthread,
|
|
* timeval,
|
|
* sel_ptrs,
|
|
* sel_cb
|
|
*
|
|
* RETURN VALUES DESCRIPTION: non-negative value upon successful completion:
|
|
* >0 -- the number of file descriptors and
|
|
* message queues that satisfy the
|
|
* selection criteria. The file descriptor
|
|
* bit masks are modified so that bits set
|
|
* to one indicate file descriptors that
|
|
* met the criteria. The msgid arrays are
|
|
* altered so that message queue arrays
|
|
* that did not meet the criteria are
|
|
* replaced with a value of -1.
|
|
* 0 -- the call timed out and no file
|
|
* descriptors satisfied the selection
|
|
* criteria or no events specified in
|
|
* selection criteria.
|
|
* upon unsuccessful completion:
|
|
* -1 and errno set to EINVAL, EINTR, EBADF,
|
|
* EAGAIN, or EFAULT, - OR -
|
|
* the non-zero return code from poll_wait.
|
|
*
|
|
* note: if selpoll returns an error and it is not
|
|
* EINTR, then all requested events are set true for
|
|
* the object that had the error.
|
|
*
|
|
*
|
|
* EXTERNAL PROCEDURES CALLED:
|
|
* assert
|
|
* chk_timeout check values in timeval struct
|
|
* xmalloc allocate storage for file descriptor/msgid
|
|
* arrays
|
|
* xmfree free previously allocated storage
|
|
* copyin copy data from user to kernel space
|
|
* copyout copy data from kernel to user space
|
|
* selpoll poll a device
|
|
* et_wait clear EVENT_SYNC flag possibly set by selnotify
|
|
* msgselect poll a msg queue
|
|
* poll_wait wait for events to occur
|
|
* selpoll_cleanup unchain control block from the device and
|
|
* thread chains; free the control block's storage
|
|
* fetch_and_or to modify TSELECT
|
|
* get_curthread get current thread pointer
|
|
*/
|
|
int
|
|
select(ulong nfdsmsgs, void *readlist, void *writelist, void *exceptlist, struct timeval *utimeoutp)
|
|
|
|
/* ulong nfdsmsgs; #file descriptors/message queues */
|
|
/* void *readlist; check file descriptors for reads */
|
|
/* void *writelist; check file descriptors for writes */
|
|
/* void *exceptlist; check file descriptors for exceptions*/
|
|
/* struct timeval *utimeoutp; length of time to wait for events */
|
|
{
|
|
register int rc; /* return code from various routines */
|
|
register int rc_save;/* return code from various routines */
|
|
register int fdes; /* number of file descriptors */
|
|
register int msgs; /* number of message queues */
|
|
register int nfilewords;/* #words needed to hold fdes */
|
|
register int array_size;/* #bytes needed to hold fdes & msgids*/
|
|
register int num_valid;/* number of valid input pointers */
|
|
register int num_req_events;/* number of requested events */
|
|
register int num_found;/* offset in allocated area */
|
|
register int fd; /* counter: #bits processed */
|
|
register int num_fdes;/* #file descriptors with occurred events*/
|
|
register int num_msgs = 0;/* #msg queue ids with occurred events*/
|
|
caddr_t k_list; /* malloc'd area: kernel's copy of user data*/
|
|
struct sel_ptrs sel_ptrs[3];/* array containing user & kernel ptrs*/
|
|
struct sel_cb *cb; /* ctl blk at head of thread chain */
|
|
ushort reqevents;/* requested events; used in call to selpoll*/
|
|
ushort rtnevents;/* returned events; used in call to selpoll*/
|
|
int reqev_msk;/* requested event mask */
|
|
register int elem; /* counter used in for loops */
|
|
register int bit; /* counter used in for loops */
|
|
register int list_num;/* counter used in for loops */
|
|
struct timeval stimeout, *ktimeoutp;
|
|
extern int chk_timeout();
|
|
extern int copyin();
|
|
extern int copyout();
|
|
extern int selpoll();
|
|
extern int poll_wait();
|
|
extern void selpoll_cleanup();
|
|
int bitmask[3]; /* used to copy the user file-descriptor masks for the
|
|
most common cases */
|
|
ushort malloc_req; /* flag for malloc'ing */
|
|
register struct thread *temp_threadp; /* temp pointer to thread */
|
|
register struct uthread *temp_uthreadp; /* temp pointer to uthread */
|
|
|
|
temp_threadp = CURTHREAD;
|
|
temp_uthreadp = temp_threadp->t_uthreadp;
|
|
|
|
|
|
ktimeoutp = &stimeout;
|
|
if (utimeoutp != NULL) {
|
|
if (copyin((caddr_t) utimeoutp, (caddr_t) ktimeoutp, sizeof(stimeout)))
|
|
{
|
|
temp_uthreadp->ut_error = EFAULT;
|
|
return -1;
|
|
}
|
|
} else
|
|
ktimeoutp = (struct timeval *) 0;
|
|
|
|
/*
|
|
* Validate nfdsmsgs and then extract from it
|
|
* the number of file descriptors and number of
|
|
* message queues.
|
|
*/
|
|
fdes = NFDS(nfdsmsgs);
|
|
msgs = NMSGS(nfdsmsgs);
|
|
|
|
/*
|
|
* Initialize array with user data pointers.
|
|
*/
|
|
sel_ptrs[0].user_ptr = readlist;
|
|
sel_ptrs[1].user_ptr = writelist;
|
|
sel_ptrs[2].user_ptr = exceptlist;
|
|
|
|
/*
|
|
* Determine the number of valid (i.e. non-NULL) input pointers.
|
|
*/
|
|
num_valid = 0;
|
|
for (elem = 0; elem < 3; elem++)
|
|
{
|
|
if (sel_ptrs[elem].user_ptr != NULL)
|
|
{
|
|
num_valid++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If there are no valid input pointers, select acts as a timer.
|
|
* Returns when a signal occurs or when the timeout is reached.
|
|
* If the ktimeout is null, we could wait forever.
|
|
*/
|
|
if (num_valid == 0)
|
|
{
|
|
rc = chk_timeout(ktimeoutp, &reqev_msk);
|
|
if (rc != 0)
|
|
{
|
|
temp_uthreadp->ut_error = EINVAL;
|
|
return(rc);
|
|
}
|
|
if ((rc = poll_wait(ktimeoutp, temp_threadp)) != 0)
|
|
{
|
|
temp_uthreadp->ut_error = rc; /* EINTR if signal */
|
|
/* poll_wait() was interrupted by a signal,
|
|
* clear a possible timer et_post()
|
|
*/
|
|
(void)et_wait(EVENT_NDELAY, EVENT_SYNC, EVENT_SHORT);
|
|
return(-1);
|
|
}
|
|
else
|
|
return(rc);
|
|
}
|
|
|
|
/*
|
|
* Compute number of file descriptor mask words in parameter arrays.
|
|
*
|
|
* We need to allocate an array, each element of which
|
|
* will be 32 (the size of an integer) bits (1 word) long.
|
|
* If fdes is not an even number of words, we will need
|
|
* to allocate one more array element for the 'leftover'
|
|
* bits...
|
|
* ...However, we only want to use the number of 'leftover' bits
|
|
* of that extra word. For example, if fdes is 68, then the
|
|
* array would be 3 elements long, but we would only look at the
|
|
* 0th element (bits 0-31), the 1st element (bits 32-63), and the
|
|
* last 4 bits of the 2nd element (bits 64-67).
|
|
*/
|
|
|
|
/*
|
|
* Compute size of each array.
|
|
*/
|
|
if (fdes & (NBITS - 1))
|
|
{
|
|
/*
|
|
* Not an even number of words.
|
|
*/
|
|
nfilewords = 1 + (fdes >> UWSHIFT);
|
|
}
|
|
else
|
|
{
|
|
nfilewords = fdes >> UWSHIFT;
|
|
}
|
|
array_size = (nfilewords + msgs) * NBPW;
|
|
|
|
/*
|
|
* Allocate storage for each array, only if nfilewords > 1 or if there is at least
|
|
* one msgid. This avoids malloc'ing for the most common case of calling
|
|
* select with less than 32 file-descriptors and no msgids.
|
|
*/
|
|
|
|
if((nfilewords > 1) || (msgs != 0))
|
|
malloc_req = 1;
|
|
else
|
|
malloc_req = 0;
|
|
|
|
if(malloc_req)
|
|
{
|
|
k_list = xmalloc(num_valid * array_size,
|
|
0,
|
|
kernel_heap);
|
|
if (k_list == NULL)
|
|
{
|
|
temp_uthreadp->ut_error = EAGAIN;
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
num_found = 0; /* offset in allocated area */
|
|
|
|
|
|
/*
|
|
* In sel_ptrs array:
|
|
* readlist (user/kernel) is 0th elem;
|
|
* writelist (user/kernel) is 1st elem;
|
|
* exceptlist (user/kernel) is 2nd elem.
|
|
*
|
|
* Process the 3 lists (readlist, writelist, exceptlist).
|
|
*
|
|
* k_list________________________________
|
|
* |
|
|
* | (Malloc'd area that user
|
|
* V info. is copied into)
|
|
* _________________
|
|
* sel_ptrs[0].kernel_ptr.fdsmask-----> |<--nfilewords-->|
|
|
* | (#words) |
|
|
* |________________|
|
|
* sel_ptrs[0].kernel_ptr.msgids------> | |
|
|
* |<-msgs #words-->|
|
|
* |________________|
|
|
* sel_ptrs[1].kernel_ptr.fdsmask-----> |<--nfilewords-->|
|
|
* | (#words) |
|
|
* |________________|
|
|
* sel_ptrs[1].kernel_ptr.msgids------> | |
|
|
* |<-msgs #words-->|
|
|
* |________________|
|
|
* sel_ptrs[2].kernel_ptr.fdsmask-----> |<--nfilewords-->|
|
|
* | (#words) |
|
|
* |________________|
|
|
* sel_ptrs[2].kernel_ptr.msgids------> | |
|
|
* |<-msgs #words-->|
|
|
* | |
|
|
* ------------------
|
|
*/
|
|
for (elem = 0; elem < 3; elem++)
|
|
{
|
|
if (sel_ptrs[elem].user_ptr != NULL)
|
|
{
|
|
/*
|
|
* Initialize kernel file descriptor ptr to correct
|
|
* offset in allocated area or to the correct element
|
|
* in the local array bitmask.
|
|
*/
|
|
if(malloc_req)
|
|
sel_ptrs[elem].kernel_ptr.fdsmask =
|
|
(int *)(k_list + (num_found * array_size));
|
|
else
|
|
sel_ptrs[elem].kernel_ptr.fdsmask = &bitmask[elem];
|
|
|
|
num_found++; /* bump offset in allocated area*/
|
|
/*
|
|
* Copy data from user to kernel space.
|
|
*/
|
|
rc = copyin(sel_ptrs[elem].user_ptr,
|
|
sel_ptrs[elem].kernel_ptr.fdsmask,
|
|
array_size);
|
|
if (rc != 0)
|
|
{
|
|
if(malloc_req)
|
|
{
|
|
(void)xmfree(k_list, kernel_heap);
|
|
}
|
|
temp_uthreadp->ut_error = EFAULT;
|
|
return(-1);
|
|
}
|
|
|
|
/*
|
|
* Initialize kernel message queue ptr to correct
|
|
* offset in allocated area.
|
|
*/
|
|
if (msgs > 0)
|
|
{
|
|
sel_ptrs[elem].kernel_ptr.msgids =
|
|
sel_ptrs[elem].kernel_ptr.fdsmask + nfilewords;
|
|
}
|
|
else
|
|
sel_ptrs[elem].kernel_ptr.msgids = (int *)NULL;
|
|
}
|
|
} /* end for loop */
|
|
|
|
reqev_msk = 0;
|
|
/*
|
|
* Validate values in timeval struct.
|
|
*/
|
|
rc = chk_timeout(ktimeoutp, &reqev_msk);
|
|
if (rc != 0)
|
|
{
|
|
if(malloc_req)
|
|
{
|
|
(void)xmfree(k_list, kernel_heap);
|
|
}
|
|
temp_uthreadp->ut_error = EINVAL;
|
|
return(rc);
|
|
}
|
|
|
|
/*
|
|
* This flag is used to determine if notification has been
|
|
* received of events that have occurred.
|
|
*/
|
|
fetch_and_or(&temp_threadp->t_atomic, TSELECT);
|
|
|
|
/*
|
|
* Check no. of file descriptors against the max no. of open files.
|
|
*/
|
|
if (fdes > OPEN_MAX)
|
|
{
|
|
fdes = OPEN_MAX;
|
|
}
|
|
|
|
/*
|
|
* fd (file descriptor) = column number (bit position)
|
|
*
|
|
* Look down each column (fd), picking up a bit from each of the 3
|
|
* arrays. If the readlist bit is set, then set reqevents to POLLIN.
|
|
* If the writelist bit is set, then set the POLLOUT flag in reqevents.
|
|
* If the exceptlist bit is set, then set the POLLPRI flag in reqevents.
|
|
* This mask of flags is the reqevents for that fd.
|
|
*
|
|
* bit 31 bit 0 bit 63 bit 32
|
|
* | | | |
|
|
* | <--------- elem 0 ---------> | | <--------- elem 1 ---------> |
|
|
* V V V V
|
|
* readlist: [00010001000100010001000100010001][01010101000100011001000100010010]
|
|
* writelist: [01000100010001000100010001000100][00010101000100011001000100010001]
|
|
* exceptlist: [10010010001000100001000100010001][00010101000100010001000100010001]
|
|
* | | | |
|
|
* | | | |
|
|
* V V V V
|
|
* bit 21 bit 5 bit 58 bit 47
|
|
*
|
|
* Example: column/bit 21: reqevents = 001,
|
|
* column/bit 5: reqevents = 000,
|
|
* column/bit 58: reqevents = 111,
|
|
* column/bit 47: reqevents = 110, etc.
|
|
*/
|
|
num_fdes = 0; /* total number of events that have occurred */
|
|
num_req_events = 0;/* total number of events that were requested */
|
|
rc_save = 0; /* return code from selpoll */
|
|
fd = 0; /* total number of bits that have been processed*/
|
|
for (elem = 0; ((elem < nfilewords) && (fd < fdes) && (rc_save == 0)); elem++)
|
|
{
|
|
/*
|
|
* Look at each word (elem) in the array. Skip the entire word
|
|
* if it is null.
|
|
*/
|
|
if (((sel_ptrs[0].user_ptr == NULL) ||
|
|
(!sel_ptrs[0].kernel_ptr.fdsmask[elem]))&&
|
|
|
|
((sel_ptrs[1].user_ptr == NULL)||
|
|
(!sel_ptrs[1].kernel_ptr.fdsmask[elem]))&&
|
|
|
|
((sel_ptrs[2].user_ptr == NULL)||
|
|
(!sel_ptrs[2].kernel_ptr.fdsmask[elem])))
|
|
{
|
|
fd += NBITS;
|
|
continue;
|
|
}
|
|
|
|
for (bit = 0; ((bit < NBITS) && (fd < fdes)); bit++, fd++)
|
|
{
|
|
/*
|
|
* Look at each bit in the word, scanning from
|
|
* right (bit 0) to left (bit 31).
|
|
*/
|
|
|
|
/*
|
|
* If the following is true for all 3 ptrs, then
|
|
* don't process this bit (file descriptor):
|
|
* a) the ptr IS NOT null and the bit is 0, OR
|
|
* b) the ptr IS null
|
|
*/
|
|
if (((sel_ptrs[0].user_ptr == NULL) ||
|
|
(ISCLR(sel_ptrs[0].kernel_ptr.fdsmask[elem], bit))) &&
|
|
|
|
((sel_ptrs[1].user_ptr == NULL) ||
|
|
(ISCLR(sel_ptrs[1].kernel_ptr.fdsmask[elem], bit))) &&
|
|
|
|
((sel_ptrs[2].user_ptr == NULL) ||
|
|
(ISCLR(sel_ptrs[2].kernel_ptr.fdsmask[elem], bit))))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* At this point, there's at least one requested event.
|
|
*/
|
|
|
|
|
|
reqevents = reqev_msk; /* either 0 or POLLSYNC */
|
|
|
|
/*
|
|
* See if readlist ptr's bit is set.
|
|
*/
|
|
if ((sel_ptrs[0].user_ptr != NULL) &&
|
|
(ISSET(sel_ptrs[0].kernel_ptr.fdsmask[elem], bit)))
|
|
{
|
|
/*
|
|
* Set bit on for call to selpoll.
|
|
*/
|
|
reqevents |= POLLIN;
|
|
num_fdes++; /* #occurred events */
|
|
num_req_events++; /* #requested events */
|
|
}
|
|
|
|
/*
|
|
* See if writelist ptr's bit is set.
|
|
*/
|
|
if ((sel_ptrs[1].user_ptr != NULL) &&
|
|
(ISSET(sel_ptrs[1].kernel_ptr.fdsmask[elem], bit)))
|
|
{
|
|
/*
|
|
* Set bit on for call to selpoll.
|
|
*/
|
|
reqevents |= POLLOUT;
|
|
num_fdes++; /* #occurred events */
|
|
num_req_events++; /* #requested events */
|
|
}
|
|
|
|
/*
|
|
* See if exceptlist ptr's bit is set.
|
|
*/
|
|
if ((sel_ptrs[2].user_ptr != NULL) &&
|
|
(ISSET(sel_ptrs[2].kernel_ptr.fdsmask[elem], bit)))
|
|
{
|
|
/*
|
|
* Set bit on for call to selpoll.
|
|
*/
|
|
reqevents |= POLLPRI;
|
|
num_fdes++; /* #occurred events */
|
|
num_req_events++; /* #requested events */
|
|
}
|
|
|
|
/*
|
|
* Call selpoll to check the status of the
|
|
* specified file descriptor.
|
|
*/
|
|
rtnevents = 0; /* initialize returned events */
|
|
rc_save = selpoll(fd, fd, reqevents, &rtnevents, POLL_FDMSG, NULL);
|
|
/*
|
|
* If rc=0 but rtnevents has POLLHUP or POLLERR set,
|
|
* set all the bits and continue (treat the same as
|
|
* a non-EINTR and non-EBADF error).
|
|
*/
|
|
if ((rc_save == 0) && (!(rtnevents & (POLLHUP | POLLERR))))
|
|
{
|
|
/*
|
|
* If there were no reads, turn off the bit
|
|
* in the malloc'd array.
|
|
*/
|
|
if ((reqevents & POLLIN) &&
|
|
(!(rtnevents & POLLIN)) &&
|
|
(sel_ptrs[0].user_ptr != NULL))
|
|
{
|
|
CLRBIT(sel_ptrs[0].kernel_ptr.fdsmask[elem], bit);
|
|
num_fdes--; /* #occurred events*/
|
|
}
|
|
|
|
/*
|
|
* If there were no writes, turn off the bit
|
|
* in the malloc'd array.
|
|
*/
|
|
if ((reqevents & POLLOUT) &&
|
|
(!(rtnevents & POLLOUT)) &&
|
|
(sel_ptrs[1].user_ptr != NULL))
|
|
{
|
|
CLRBIT(sel_ptrs[1].kernel_ptr.fdsmask[elem], bit);
|
|
num_fdes--; /* #occurred events*/
|
|
}
|
|
|
|
/*
|
|
* If there were no exceptions, turn off the bit
|
|
* in the malloc'd array.
|
|
*/
|
|
if ((reqevents & POLLPRI) &&
|
|
(!(rtnevents & POLLPRI)) &&
|
|
(sel_ptrs[2].user_ptr != NULL))
|
|
{
|
|
CLRBIT(sel_ptrs[2].kernel_ptr.fdsmask[elem], bit);
|
|
num_fdes--; /* #occurred events*/
|
|
}
|
|
}
|
|
else /* selpoll's rc != 0 */
|
|
{
|
|
/* exit for loop if signal caught */
|
|
if ((rc_save == EINTR) || (rc_save == EBADF))
|
|
break;
|
|
else
|
|
/* if error was not signal, then indicate
|
|
error by setting all request events to
|
|
true for the object that had the error
|
|
*/
|
|
{
|
|
rtnevents = reqevents & (~POLLSYNC);
|
|
rc_save = 0;
|
|
}
|
|
}
|
|
|
|
if (rtnevents != 0)
|
|
{
|
|
/*
|
|
* At least 1 of the reqevents has occurred,
|
|
* so process the remaining data synchronously.
|
|
*/
|
|
reqev_msk = POLLSYNC;
|
|
}
|
|
/*
|
|
* If TSELECT is off, notification has been received
|
|
* that at least one event has occurred; therefore,
|
|
* all other selpoll() calls will be synchronous.
|
|
*/
|
|
if (!(temp_threadp->t_atomic & TSELECT))
|
|
{
|
|
reqev_msk = POLLSYNC;
|
|
}
|
|
|
|
} /* end for bit loop */
|
|
} /* end for elem loop */
|
|
|
|
|
|
/*
|
|
* See if any errors or signals caught.
|
|
*/
|
|
if (rc_save != 0)
|
|
{
|
|
/* remove any select control blocks from chain */
|
|
/* and decrement fd hold counts */
|
|
while (temp_uthreadp->ut_selchn != NULL)
|
|
selpoll_cleanup(((struct sel_cb *)
|
|
temp_uthreadp->ut_selchn)->corl);
|
|
|
|
if (!(temp_threadp->t_atomic & TSELECT))
|
|
{
|
|
(void)et_wait(EVENT_NDELAY, EVENT_SYNC, EVENT_SHORT);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* clear TSELECT flag
|
|
*/
|
|
fetch_and_and(&(temp_threadp->t_atomic), ~TSELECT);
|
|
}
|
|
|
|
if(malloc_req)
|
|
{
|
|
(void)xmfree(k_list, kernel_heap);
|
|
}
|
|
temp_uthreadp->ut_error = rc_save;
|
|
return(-1);
|
|
}
|
|
|
|
|
|
if (msgs > 0)
|
|
{
|
|
/*
|
|
* Process Message Queue ids.
|
|
*
|
|
* One msg (msg queue id) per array element (i.e. msgid = int).
|
|
*
|
|
* In the readlist, look at an array element. If it does not
|
|
* contain -1, then set reqevents to POLLIN, and then look at
|
|
* the corresponding array element in the writelist and
|
|
* exceptlist. If either/both contain the same msgid as the
|
|
* one in readlist, then set the corresponding bits (POLLOUT/
|
|
* POLLPRI) in the reqevents, and set did_wr/did_ex as appropriate.
|
|
* The purpose of this is to minimize the number of calls to msgselect,
|
|
* since in most cases where the same msgid is being selected for
|
|
* more than one event, the indices into the read/write/except
|
|
* arrays will be the same for each instance of the msgid.
|
|
*
|
|
* In the writelist, look at an array element. If it does not
|
|
* contain -1, then set reqevents to POLLOUT, and then look at
|
|
* the corresponding array element in the exceptlist. If it
|
|
* contains the same msgid as the one in writelist, then set
|
|
* the corresponding bit (POLLPRI) in the reqevents, and set
|
|
* did_ex to 1.
|
|
*
|
|
* In the exceptlist, look at an array element. If it does not
|
|
* contain -1, then set reqevents to POLLPRI.
|
|
*
|
|
* The loop through read/write/except lists is un-rolled to reduce
|
|
* the number of conditionals that would be required to implement this.
|
|
*
|
|
* elem 0 elem 4
|
|
* | |
|
|
* | |
|
|
* V V
|
|
* readlist: [01][02][04][06][07]
|
|
* writelist: [01][02][03][05][08]
|
|
* exceptlist: [01][03][04][05][09]
|
|
*
|
|
* Example:
|
|
* elem 0: reqevents = POLLIN | POLLOUT | POLLPRI
|
|
* set did_wr/did_ex = 1
|
|
* 1st call to msgselect
|
|
* skip write and except check
|
|
* elem 1: reqevents = POLLIN | POLLOUT
|
|
* set did_wr
|
|
* 2nd call to msgselect
|
|
* skip write check
|
|
* reqevents = POLLPRI
|
|
* 3rd call to msgselect
|
|
* elem 2: reqevents = POLLIN | POLLPRI
|
|
* set did_ex
|
|
* 4th call to msgselect
|
|
* reqevents = POLLOUT
|
|
* 5th call to msgselect
|
|
* skip except check
|
|
* elem 3: reqevents = POLLIN
|
|
* 6th call to msgselect
|
|
* reqevents = POLLOUT | POLLPRI
|
|
* set did_ex
|
|
* 7th call to msgselect
|
|
* skip except check
|
|
* elem 4: reqevents = POLLIN
|
|
* 8th call to msgselect
|
|
* reqevents = POLLOUT
|
|
* 9th call to msgselect
|
|
* reqevents = POLLPRI
|
|
* 10th call to msgselect
|
|
*
|
|
* Thus, msgselect is called 10 times instead of 15...
|
|
* Arguably, since only nine msgid's were specified, this could be
|
|
* reduced to 9, but the overhead required for that optimization would
|
|
* probably outweigh its benefit since like msgid's will probably
|
|
* have the same index.
|
|
*/
|
|
num_msgs = 0;
|
|
|
|
for (elem = 0; elem < msgs; elem++)
|
|
{
|
|
int did_wr, did_ex;
|
|
ushort tmpevents;
|
|
|
|
did_wr = 0; /* default is all lists un-done */
|
|
did_ex = 0;
|
|
rtnevents = 0; /* no returned events yet */
|
|
|
|
/* check for read request */
|
|
if ((sel_ptrs[0].user_ptr != NULL) &&
|
|
(sel_ptrs[0].kernel_ptr.msgids[elem] != -1))
|
|
{
|
|
reqevents = reqev_msk | POLLIN; /* checking for read */
|
|
num_req_events++;
|
|
|
|
/* check to see if write/except entries same */
|
|
if ((sel_ptrs[1].user_ptr != NULL) &&
|
|
(sel_ptrs[0].kernel_ptr.msgids[elem] ==
|
|
sel_ptrs[1].kernel_ptr.msgids[elem]))
|
|
{
|
|
reqevents |= POLLOUT; /* checking for write */
|
|
num_req_events++;
|
|
did_wr = 1;
|
|
}
|
|
if ((sel_ptrs[2].user_ptr != NULL) &&
|
|
(sel_ptrs[0].kernel_ptr.msgids[elem] ==
|
|
sel_ptrs[2].kernel_ptr.msgids[elem]))
|
|
{
|
|
reqevents |= POLLPRI; /* checking for except */
|
|
num_req_events++;
|
|
did_ex = 1;
|
|
}
|
|
|
|
/* check for events now */
|
|
tmpevents = 0;
|
|
rc_save = msgselect(sel_ptrs[0].kernel_ptr.msgids[elem],
|
|
elem, reqevents, &tmpevents);
|
|
|
|
/* break out if error and no events */
|
|
if (rc_save != 0)
|
|
{
|
|
if (rc_save == EINVAL)
|
|
{
|
|
/*
|
|
* select is documented to
|
|
* return EBADF if the msgid
|
|
* is bad, but msgselect
|
|
* returns EINVAL instead ...
|
|
*/
|
|
rc_save = EBADF;
|
|
break;
|
|
}
|
|
if (tmpevents == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
rtnevents |= tmpevents;
|
|
}
|
|
|
|
/* check for write request */
|
|
if ((!did_wr) && (sel_ptrs[1].user_ptr != NULL) &&
|
|
(sel_ptrs[1].kernel_ptr.msgids[elem] != -1))
|
|
{
|
|
reqevents = reqev_msk | POLLOUT; /* checking for write */
|
|
num_req_events++;
|
|
|
|
/* check to see if except msgid is same */
|
|
if ((!did_ex) && (sel_ptrs[2].user_ptr != NULL) &&
|
|
(sel_ptrs[1].kernel_ptr.msgids[elem] ==
|
|
sel_ptrs[2].kernel_ptr.msgids[elem]))
|
|
{
|
|
reqevents |= POLLPRI; /* checking for exception */
|
|
num_req_events++;
|
|
did_ex = 1;
|
|
}
|
|
|
|
/* check for events now */
|
|
tmpevents = 0;
|
|
rc_save = msgselect(sel_ptrs[1].kernel_ptr.msgids[elem],
|
|
elem, reqevents, &tmpevents);
|
|
|
|
/* break out if error and no events */
|
|
if (rc_save != 0)
|
|
{
|
|
if (rc_save == EINVAL)
|
|
{
|
|
/*
|
|
* select is documented to
|
|
* return EBADF if the msgid
|
|
* is bad, but msgselect
|
|
* returns EINVAL instead ...
|
|
*/
|
|
rc_save = EBADF;
|
|
break;
|
|
}
|
|
if (tmpevents == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
rtnevents |= tmpevents;
|
|
}
|
|
|
|
/* check for exception request */
|
|
if ((!did_ex) && (sel_ptrs[2].user_ptr != NULL) &&
|
|
(sel_ptrs[2].kernel_ptr.msgids[elem] != -1))
|
|
{
|
|
reqevents = reqev_msk | POLLPRI; /* checking for exception */
|
|
num_req_events++;
|
|
|
|
/* check for events now */
|
|
tmpevents = 0;
|
|
rc_save = msgselect(sel_ptrs[2].kernel_ptr.msgids[elem],
|
|
elem, reqevents, &tmpevents);
|
|
|
|
/* break out if error and no events */
|
|
if (rc_save != 0)
|
|
{
|
|
if (rc_save == EINVAL)
|
|
{
|
|
/*
|
|
* select is documented to
|
|
* return EBADF if the msgid
|
|
* is bad, but msgselect
|
|
* returns EINVAL instead ...
|
|
*/
|
|
rc_save = EBADF;
|
|
break;
|
|
}
|
|
if (tmpevents == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
rtnevents |= tmpevents;
|
|
}
|
|
|
|
/*
|
|
* All events for this element have been collected, now
|
|
* change event-less entries to -1, leaving and counting
|
|
* those msgid's which do have events.
|
|
*/
|
|
if (rtnevents != 0)
|
|
{
|
|
/*
|
|
* At this point, any errors left from above are interpreted
|
|
* as events and returned through normal channels.
|
|
*/
|
|
rc_save = 0;
|
|
|
|
if (reqevents & POLLIN)
|
|
{
|
|
if (rtnevents & POLLIN)
|
|
{
|
|
num_msgs++;
|
|
}
|
|
else
|
|
{
|
|
sel_ptrs[0].kernel_ptr.msgids[elem] = -1;
|
|
}
|
|
}
|
|
if (reqevents & POLLOUT)
|
|
{
|
|
if (rtnevents & POLLOUT)
|
|
{
|
|
num_msgs++;
|
|
}
|
|
else
|
|
{
|
|
sel_ptrs[1].kernel_ptr.msgids[elem] = -1;
|
|
}
|
|
}
|
|
if (reqevents & POLLPRI)
|
|
{
|
|
if (rtnevents & POLLPRI)
|
|
{
|
|
num_msgs++;
|
|
}
|
|
else
|
|
{
|
|
sel_ptrs[2].kernel_ptr.msgids[elem] = -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* At least 1 of the reqevents has occurred,
|
|
* so process the remaining data synchronously.
|
|
*/
|
|
reqev_msk = POLLSYNC;
|
|
}
|
|
else /* rtnevents == 0 */
|
|
{
|
|
if (sel_ptrs[0].user_ptr != NULL)
|
|
{
|
|
sel_ptrs[0].kernel_ptr.msgids[elem] = -1;
|
|
}
|
|
if (sel_ptrs[1].user_ptr != NULL)
|
|
{
|
|
sel_ptrs[1].kernel_ptr.msgids[elem] = -1;
|
|
}
|
|
if (sel_ptrs[2].user_ptr != NULL)
|
|
{
|
|
sel_ptrs[2].kernel_ptr.msgids[elem] = -1;
|
|
}
|
|
|
|
/*
|
|
* If TSELECT is off, notification been received
|
|
* that at least one event has occurred; therefore,
|
|
* all other selpoll() calls will be synchronous.
|
|
*/
|
|
if (!(temp_threadp->t_atomic & TSELECT))
|
|
{
|
|
reqev_msk = POLLSYNC;
|
|
}
|
|
}
|
|
} /* for loop */
|
|
}
|
|
|
|
/*
|
|
* See if any errors or signals caught.
|
|
*/
|
|
if (rc_save != 0)
|
|
{
|
|
/* remove any select control blocks from chain */
|
|
/* and decrement fd hold counts */
|
|
while (temp_uthreadp->ut_selchn != NULL)
|
|
selpoll_cleanup(((struct sel_cb *)
|
|
temp_uthreadp->ut_selchn)->corl);
|
|
|
|
if (!(temp_threadp->t_atomic & TSELECT))
|
|
{
|
|
(void)et_wait(EVENT_NDELAY, EVENT_SYNC, EVENT_SHORT);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* clear TSELECT flag
|
|
*/
|
|
fetch_and_and(&(temp_threadp->t_atomic), ~TSELECT);
|
|
}
|
|
|
|
if(malloc_req)
|
|
{
|
|
(void)xmfree(k_list, kernel_heap);
|
|
}
|
|
temp_uthreadp->ut_error = rc_save;
|
|
return(-1);
|
|
}
|
|
|
|
/*
|
|
* selpoll did not report an error condition.
|
|
*/
|
|
if ((!reqev_msk) && (temp_threadp->t_atomic & TSELECT))
|
|
{
|
|
/*
|
|
* None of the reqevents have occurred yet, so
|
|
* wait the specified length of time for a
|
|
* selnotify.
|
|
*/
|
|
rc_save = poll_wait(ktimeoutp, temp_threadp);
|
|
if (rc_save != 0)
|
|
{
|
|
/* remove any select control blocks from chain */
|
|
/* and decrement fd hold counts */
|
|
while (temp_uthreadp->ut_selchn != NULL)
|
|
selpoll_cleanup(((struct sel_cb *)
|
|
temp_uthreadp->ut_selchn)->corl);
|
|
if(malloc_req)
|
|
{
|
|
(void)xmfree(k_list, kernel_heap);
|
|
}
|
|
temp_uthreadp->ut_error = rc_save;
|
|
|
|
if ((temp_threadp->t_atomic & TSELECT))
|
|
{
|
|
/*
|
|
* clear TSELECT flag
|
|
*/
|
|
fetch_and_and(&(temp_threadp->t_atomic), ~TSELECT);
|
|
}
|
|
|
|
/* poll_wait() was interrupted by a signal,
|
|
* clear a possible timer et_post()
|
|
*/
|
|
(void)et_wait(EVENT_NDELAY, EVENT_SYNC, EVENT_SHORT);
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Run the thread chain (temp_uthreadp->ut_selchn).
|
|
*/
|
|
while ((cb = temp_uthreadp->ut_selchn) != NULL)
|
|
{
|
|
if (cb->dev_id != POLL_MSG)
|
|
{
|
|
/*
|
|
* Validate cb's correlator (i.e. file descriptor).
|
|
*/
|
|
assert(cb->corl < fdes);
|
|
|
|
/*
|
|
* Determine elem/bit offset into malloc'd array.
|
|
*/
|
|
elem = cb->corl >> UWSHIFT;
|
|
bit = cb->corl % NBITS;
|
|
|
|
/*
|
|
* If there was a read, turn on the bit
|
|
* in the malloc'd array.
|
|
*/
|
|
if ((cb->rtnevents & cb->reqevents & POLLIN) &&
|
|
(sel_ptrs[0].user_ptr != NULL) &&
|
|
(!ISSET(sel_ptrs[0].kernel_ptr.fdsmask[elem], bit)))
|
|
{
|
|
SETBIT(sel_ptrs[0].kernel_ptr.fdsmask[elem], bit);
|
|
num_fdes++; /* #occurred events */
|
|
}
|
|
|
|
/*
|
|
* If there was a write, turn on the bit
|
|
* in the malloc'd array.
|
|
*/
|
|
if ((cb->rtnevents & cb->reqevents & POLLOUT) &&
|
|
(sel_ptrs[1].user_ptr != NULL) &&
|
|
(!ISSET(sel_ptrs[1].kernel_ptr.fdsmask[elem], bit)))
|
|
{
|
|
SETBIT(sel_ptrs[1].kernel_ptr.fdsmask[elem], bit);
|
|
num_fdes++; /* #occurred events */
|
|
}
|
|
|
|
/*
|
|
* If there was an exception, turn on the bit
|
|
* in the malloc'd array.
|
|
*/
|
|
if ((cb->rtnevents & cb->reqevents & POLLPRI) &&
|
|
(sel_ptrs[2].user_ptr != NULL) &&
|
|
(!ISSET(sel_ptrs[2].kernel_ptr.fdsmask[elem], bit)))
|
|
{
|
|
SETBIT(sel_ptrs[2].kernel_ptr.fdsmask[elem], bit);
|
|
num_fdes++; /* #occurred events */
|
|
}
|
|
|
|
} /* dev_id is NOT a message id */
|
|
else /* dev_id is a message id */
|
|
{
|
|
if ((sel_ptrs[0].user_ptr != NULL) &&
|
|
(cb->reqevents & cb->rtnevents & POLLIN))
|
|
{
|
|
sel_ptrs[0].kernel_ptr.msgids[cb->corl] = cb->unique_id;
|
|
num_msgs++;
|
|
}
|
|
|
|
if ((sel_ptrs[1].user_ptr != NULL) &&
|
|
(cb->reqevents & cb->rtnevents & POLLOUT))
|
|
{
|
|
sel_ptrs[1].kernel_ptr.msgids[cb->corl] = cb->unique_id;
|
|
num_msgs++;
|
|
}
|
|
|
|
if ((sel_ptrs[2].user_ptr != NULL) &&
|
|
(cb->reqevents & cb->rtnevents & POLLPRI))
|
|
{
|
|
sel_ptrs[2].kernel_ptr.msgids[cb->corl] = cb->unique_id;
|
|
num_msgs++;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Remove control block from device and
|
|
* thread chains and free its storage.
|
|
* And decrement fd hold counts.
|
|
*/
|
|
selpoll_cleanup(cb->corl);
|
|
|
|
} /* end while */
|
|
|
|
for (elem = 0; elem < 3; elem++)
|
|
{
|
|
if (sel_ptrs[elem].user_ptr != NULL)
|
|
{
|
|
/*
|
|
* Copy data from kernel to user space.
|
|
*/
|
|
rc = copyout(sel_ptrs[elem].kernel_ptr.fdsmask,
|
|
sel_ptrs[elem].user_ptr,
|
|
array_size);
|
|
if (rc != 0)
|
|
{
|
|
if (!(temp_threadp->t_atomic & TSELECT))
|
|
{
|
|
(void)et_wait(EVENT_NDELAY, EVENT_SYNC, EVENT_SHORT);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* clear TSELECT flag
|
|
*/
|
|
fetch_and_and(&(temp_threadp->t_atomic), ~TSELECT);
|
|
}
|
|
|
|
if(malloc_req)
|
|
{
|
|
(void)xmfree(k_list, kernel_heap);
|
|
}
|
|
temp_uthreadp->ut_error = EFAULT;
|
|
return(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Free the previously-allocated memory.
|
|
*/
|
|
if(malloc_req)
|
|
{
|
|
(void)xmfree(k_list, kernel_heap);
|
|
}
|
|
|
|
/*
|
|
* If a selnotify() has occurred, but we didn't have to
|
|
* poll_wait() for an event to occur, then we must clear
|
|
* the event (EVENT_SYNC) that selnotify() e_post()ed.
|
|
*/
|
|
if (!(temp_threadp->t_atomic & TSELECT))
|
|
{
|
|
(void)et_wait(EVENT_NDELAY, EVENT_SYNC, EVENT_SHORT);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* clear TSELECT flag
|
|
*/
|
|
fetch_and_and(&(temp_threadp->t_atomic), ~TSELECT);
|
|
}
|
|
|
|
/*
|
|
* Total #events that occurred.
|
|
*/
|
|
return((num_fdes) | (num_msgs << 16));
|
|
|
|
|
|
} /* end select */
|