Subject: Re: default maximum number of open file descriptors too small?
To: Bakul Shah <bakul@netcom.com>
From: Luke Mewburn <lukem@telstra.com.au>
List: tech-kern
Date: 11/28/1995 10:01:30
Bakul Shah writes:
> > But even if you increase FD_SETSIZE for your own program's internals,
> > you have to worry about libraries which use a fixed FD_SETSIZE and
> > manipulate fd_sets using descriptors beyond the FD_SETSIZE range.  This
> > often makes for hard to find stack-twiddling corruption!

> I am aware that a completely clean solution is not possible
> at this point but most programs that need large set size
> usually don't rely on such library routines, preferring to
> roll their own.  As long the number of significant fds
> (nfds) is communicated and a routine touching argument fd
> sets is careful to access only the first nfds bits (rounded
> up to the next sizeof (long) boundary bytes), different
> FD_SETSIZE definitions are not a problem.

> Right now you can define FD_SETSIZE to something other than
> the default and get in trouble with library routines relying
> on the default definition so fixing select won't break
> anything new.  Basically fdset_t should've never existed.

Another idea (that I thought of in the shower this morning) is to
rename select to oselect, and to redefine fd_set as something like:

	typedef int32_t fd_mask;
	typedef struct fd_set {
		size_t	 fd_size;
		fd_mask	*fds_bits;
	} fd_set;
Have the new select use this new type.

Define the macros like (with better error checking of course):
---
#define	FD_ZERO(p) 	{ \
	(p).fd_size = FD_SETSIZE; \
	(p).fds_bits = calloc(howmany((p).fd_size, NFDBITS), (p).fd_size); \
}

#define FD_COPY(f, t)   { \
	(t).fd_size = (f).fd_size ; \
	(t).fds_bits = calloc(howmany((t).fd_size, NFDBITS), (t).fd_size); \
	memmove((t).fds_bits, (f).fds_bits, howmany((t).fd_size, NBBY)); \
}

#define FD_ISSET(n, p)	\
		(((p)->fd_size < (n)/NFDBITS) ? 0 : \
		((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))))

#define FD_SET(n, p)	{\
		if ((p).fd_size < (n)/NFDBITS) { \
			(p)->fds_bits = realloc((p)->fds_bits, \
						howmany((n), NBBY)); \
			memset((p)->fds_bits + howmany((p).fd_size/NBBY), \
			    0, howmany((n), NBBY), howmany((p).fd_size/NBBY)); \
			(p).fd_size = howmany((n), NFDBITS); \
		} \
		(p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)); \
}

#define FD_CLR(n, p)	{\
		if ((p).fd_size < (n)/NFDBITS) { \
			(p)->fds_bits = realloc((p)->fds_bits, \
						howmany((n), NBBY)); \
			memset((p)->fds_bits + howmany((p).fd_size/NBBY), \
			    0, howmany((n), NBBY), howmany((p).fd_size/NBBY)); \
			(p).fd_size = howmany((n), NFDBITS); \
		} \
		(p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)); \
}
---


> It pains me to say that winsock (on NT & Windows) does at
> least this right.  So do IRIX and Solaris.  BSDI, Linux,
> FreeBSD, older SunOS don't.


On Solaris, if you bump up file descriptors using maxfdescs in /etc/system,
then the max per process (as set by setrlimit) is increased. However,
select(2) is broken, and processes using it will unexpectedly hang at times.
poll(2) works ok though.

(On Solaris, to increase the # of FDs available to lockd, we just left
 maxfdescs at 1024, and invoked lockd as something like:
	#!/bin/csh -f
	limit -h descriptors 5120
	exec /usr/lib/nfs/lockd $*
 which works ok, because the limit still comes out of the global kernel pool
 which is huge enough on our system, which is just an NFS server.
 I digress from NetBSD tech-kern topics though...)


-- 
Luke Mewburn <luke.mewburn@itg.telstra.com.au>

Lawyers - the only group for which ignorance of the law is not a crime - anon