Path: utzoo!utgpu!news-server.csri.toronto.edu!rutgers!cs.utexas.edu!usc!apple!portal!atari!imagen!sun!snafu!lm From: lm@snafu.Sun.COM (Larry McVoy) Newsgroups: comp.unix.wizards Subject: Re: Streams message allocation Keywords: streams deadlock Message-ID: <140410@sun.Eng.Sun.COM> Date: 9 Aug 90 17:21:06 GMT References: <10332@celit.fps.com> <1990Aug2.043059.578@cbnewsl.att.com> Sender: news@sun.Eng.Sun.COM Reply-To: lm@sun.UUCP (Larry McVoy) Distribution: usa Organization: Sun Microsystems, Mountain View Lines: 887 In article <1990Aug2.043059.578@cbnewsl.att.com> sar0@cbnewsl.att.com (stephen.a.rago) writes: >The number of messages needed per buffer class (size) is something that >can only be determined statistically or empirically. In the absence of >information like inter-arrival rate of allocation requests and message >hold times, the best way to proceed is to start with an educated guess >of how many messages you may need. Here's a package I wrote a few years ago when tuning STREAMS for SCO XENIX (I was porting the LAI TCP/IP to XENIX and tuning was really critical). Use this while your system is under a "normal" load and it will give you a pretty good idea of where to set things. No promises that it works - it worked on SCO XENIX a while ago, beyond that you're on your own. If you have SCO XENIX & TCP/IP you should have this program already. # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # Makefile mode.c sw.1 sw.c term.h termcap.c echo x - Makefile cat > "Makefile" << '//E*O*F Makefile//' O = sw.o termcap.o mode.o S = Makefile sw.1 sw.c termcap.c mode.c term.h #CFLAGS=-DUNIX=\"/xenix\" -DKMEM=\"/dev/mem\" -DSAVECNT=0 BIN=/usr/local/bin all sw: $O cc $O -ltermlib -o sw install: sw cp sw $(BIN) chown root $(BIN)/sw chmod 4755 $(BIN)/sw clean: rm -f $O a.out core shar clobber: clean rm -f sw shar: shar $S > shar //E*O*F Makefile// echo x - mode.c cat > "mode.c" << '//E*O*F mode.c//' /* * copyright (C) 1986 by Larry McVoy * MUST be distributed in source form only. */ # include # include # include static struct sgttyb buf; static done = 0; delay(on) { if (on) { int flags; fcntl(0, F_GETFL, &flags); flags &= ~O_NDELAY; return fcntl(0, F_SETFL, flags); } else { return fcntl(0, F_SETFL, O_NDELAY); } } cbreak(on) { if (!done) { ioctl(fileno(stdin), TIOCGETP, &buf); done++; } if (on) { buf.sg_flags |= CBREAK; ioctl(fileno(stdin), TIOCSETP, &buf); } else { buf.sg_flags &= ~CBREAK; ioctl(fileno(stdin), TIOCSETP, &buf); } } echo(on) { if (!done) { ioctl(fileno(stdin), TIOCGETP, &buf); done++; } if (on) { buf.sg_flags |= ECHO; ioctl(fileno(stdin), TIOCSETP, &buf); } else { buf.sg_flags &= ~ECHO; ioctl(fileno(stdin), TIOCSETP, &buf); } } //E*O*F mode.c// echo x - sw.1 cat > "sw.1" << '//E*O*F sw.1//' .TH SW 1 .UC 4 .SH NAME sw - (stream watch) watch streams resources on System V .SH SYNOPSIS .B sw .SH DESCRIPTION .I Sw digs into kmem to find out how many streams, queues, message blocks, and data blocks are in use. It find this in the \fIstruct strstat strst\fR variable. For each category mentioned above the following fields are printed: .IP Use (strst.use) How many of the resource in question are in use. .IP "Ave10, Ave30, Ave60, Ave120" As above, only the value is averaged over the last N iterations (an iteration is about one second). .IP Total (strst.total) The total number of the resource used since boot time or the last time it was cleared. .IP Max (strst.max) The maximum number of the resource allocated at the same time. .IP Fail (strst.fail) The number of times a request was made, for the resource, that could not be granted. .PP The data block section is further broken down into sub classes. This section has a slightly different format; in the title section the field is \fISiz<#> Count <#>\fR, where the first number is the data block size and the second number is the number of data blocks statically allocated. .PP The screen is managed by curses. It will respond to: .IP c clear the total max & fail fields (you have to have write permission on /dev/mem). .IP ^L redraw the screen. .IP N Where N is 0-9. Set the number of seconds between interations. .IP q (quit) Quit the program. .IP ^L (Control-L) Refresh the screen. .SH FILES .DT /dev/kmem kernel memory .br /unix for getting variable addresses .SH BUGS The definitions of the various fields is my best guess, they do not reflect any AT&T documentation that I've read. .SH COPYRIGHT \fBSw\fR is copyright 1988 by Larry McVoy. Permission is hereby granted to publish strings in source or object form as long as all copyright notices are retained. Object-only distributions are permitted only if the source is also freely available from the distributer. Any fee charged for such publication may consist only of a reasonable charge for any media used. .SH AUTHOR Larry McVoy (lm@eng.sun.com) //E*O*F sw.1// echo x - sw.c cat > "sw.c" << '//E*O*F sw.c//' /* * copyright 1988 by Larry McVoy. All rights reserved. * If you redistribute this you must distribute in source form. */ #include "term.h" #include #ifdef M_XENIX #include #else #include #endif #include #include #include #include #include #include #include #if defined(M_XENIX) || defined(sys5) #include #endif #ifndef UNIX #define UNIX "/vmunix" #endif #ifndef KMEM #define KMEM "/dev/kmem" #endif #ifdef M_XENIX #define v_nblk4096 v_nblk8192 #endif #ifdef sun #define NCLASS 9 #endif #ifndef SAVECNT #define SAVECNT 121 #endif #define kbytes(x) ( ((x)+1023) >> 10) #ifdef M_XENIX #define nlist xlist #define n_value xl_value #define n_name xl_name struct nlist nl[] = { #define NL_STRST 0 {0,0,0,"_strst"}, #define NL_RBSIZE 1 {0,0,0,"_rbsize"}, #define NL_V 2 {0,0,0,"_v"}, #define NL_DBALLOC 3 {0,0,0,"_dballoc"}, #define NL_NMBLOCK 4 {0,0,0,"_nmblock"}, {0,0,0,(char *) 0}, }; #else #ifdef sys5 struct nlist nl[] = { #define NL_STRST 0 {"_strst"}, #define NL_RBSIZE 1 {"_rbsize"}, #define NL_V 2 {"_v"}, #define NL_DBALLOC 3 {"_dballoc"}, #define NL_NMBLOCK 4 {"_nmblock"}, { 0 }, }; #else #ifdef sun char* nl_names[] = { #define NL_STRST 0 "_strst", #define NL_RBSIZE 1 "_rbsize", #define NL_NDBLKS 2 "_ndblks", #define NL_DBALLOC 3 "_dballoc", #define NL_NMBLOCK 4 "_nmblock", "", }; struct nlist nl[sizeof(nl_names)/sizeof(char*)]; #endif /* sun */ #endif /* sys5 */ #endif /* M_XENIX */ ushort rbsize[NCLASS]; short cnt[NCLASS]; struct dbalcst dballoc[NCLASS]; int total, ndblock, nmblock, fd, sleeptime = 1; #ifndef sun struct var v; #endif typedef struct { alcdat stream; alcdat queue; alcdat mblock; alcdat dblock; alcdat dblk[NCLASS]; } Strstat; Strstat strst; /* * It's the main thing... */ main(ac, av) char** av; { int* p; int i; int done(); for (i=1; i 0 sprintf(buf, "%11s%5s%6s%6s%6s%6s%7s%8s%8s%8s", "Resource", "Cnt", "Use", "Ave10", "Ave30", "Ave60", "Ave120", "Total", "Max", "Fail"); # else sprintf(buf, "%11s%5s%10s%8s%8s%8s", "Resource", "Cnt", "Use", "Total", "Max", "Fail"); # endif tprint(buf, 0, 2); # ifdef sun sprintf(buf, "%11s:%4s", "stream", "?"); tprint(buf, 0, 3); sprintf(buf, "%11s:%4s", "queue", "?"); tprint(buf, 0, 4); # else sprintf(buf, "%11s:%4d", "stream", v.v_nstream); tprint(buf, 0, 3); sprintf(buf, "%11s:%4d", "queue", v.v_nqueue); tprint(buf, 0, 4); # endif sprintf(buf, "%11s:%4d", "mblock", nmblock); tprint(buf, 0, 5); sprintf(buf, "%11s:%4d", "dblk totals", ndblock); tprint(buf, 0, 6); # if SAVECNT > 0 sprintf(buf, "%4s%4s%4s%4s%6s%6s%6s%6s%7s%8s%8s%8s", "Size", "Cnt", "Med", "Low", "Use", "Ave10", "Ave30", "Ave60", "Ave120", "Total", "Max", "Fail"); # else sprintf(buf, "%-4s%4s%4s%4s%4s%6s%8s%8s%8s", "Mem", "Size", "Cnt", "Med", "Low", "Use", "Total", "Max", "Fail"); # endif tprint(buf, 0, 8); for (total=i=0; i 0 sprintf(buf, "%4d %3d %3d %3d", rbsize[i], cnt[i], med, lo); # else sprintf(buf, "%3d %4d %3d %3d %3d", kbytes(rbsize[i] * cnt[i]), rbsize[i], cnt[i], med, lo); # endif tprint(buf, 0, 9 + i); } sprintf(buf, "Buffers (used/total) = "); tprint(buf, 0, 23); Pause(); } Pause() { char buf[40]; sprintf(buf, "Pause=%d", sleeptime); tprint(buf, 0, 0); } # if SAVECNT == 0 /* * display the information, called once per second (about) * * No averaging version */ dump(s) register struct strstat* s; { char buf[80]; register i, mem = 0; static calls = 0; sprintf(buf, "%6d%8d%8d%8d", s->stream.use, s->stream.total, s->stream.max, s->stream.fail); tprint(buf, 20, 3); sprintf(buf, "%6d%8d%8d%8d", s->queue.use, s->queue.total, s->queue.max, s->queue.fail); tprint(buf, 20, 4); sprintf(buf, "%6d%8d%8d%8d", s->mblock.use, s->mblock.total, s->mblock.max, s->mblock.fail); tprint(buf, 20, 5); sprintf(buf, "%6d%8d%8d%8d", s->dblock.use, s->dblock.total, s->dblock.max, s->dblock.fail); tprint(buf, 20, 6); for (i=0; idblk[i].use * rbsize[i]; sprintf(buf, "%6d%8d%8d%8d", s->dblk[i].use, s->dblk[i].total, s->dblk[i].max, s->dblk[i].fail); tprint(buf, 20, 9 + i); } sprintf(buf, "%d/%d Kbytes", kbytes(mem), kbytes(total)); tprint(buf, 23, 23); calls++; sprintf(buf, "Calls=%d", calls); tprint(buf, 10, 0); } # else /* * display the information, called once per second (about) * * Averaging version */ dump(s) register struct strstat* s; { char buf[80]; register i, j, b10, b30, b60, b120, mem = 0; static struct strstat pst[SAVECNT]; static struct strstat sum10, sum30, sum60, sum120; static calls = 0; j = calls % SAVECNT; b10 = (j + SAVECNT - 10) % SAVECNT; b30 = (j + SAVECNT - 30) % SAVECNT; b60 = (j + SAVECNT - 60) % SAVECNT; b120 = (j + SAVECNT - 120) % SAVECNT; memcpy(&pst[j], s, sizeof(struct strstat)); addstrst(s, &sum10); addstrst(s, &sum30); addstrst(s, &sum60); addstrst(s, &sum120); if (!calls) { mulstrst(&sum10, 10); mulstrst(&sum30, 30); mulstrst(&sum60, 60); mulstrst(&sum120, 120); for (i=1; istream.use, sum10.stream.use / 10, sum30.stream.use / 30, sum60.stream.use / 60, sum120.stream.use / 120, s->stream.total, s->stream.max, s->stream.fail); tprint(buf, 16, 3); sprintf(buf, "%6d%6d%6d%6d%7d%8d%8d%8d", s->queue.use, sum10.queue.use / 10, sum30.queue.use / 30, sum60.queue.use / 60, sum120.queue.use / 120, s->queue.total, s->queue.max, s->queue.fail); tprint(buf, 16, 4); sprintf(buf, "%6d%6d%6d%6d%7d%8d%8d%8d", s->mblock.use, sum10.mblock.use / 10, sum30.mblock.use / 30, sum60.mblock.use / 60, sum120.mblock.use / 120, s->mblock.total, s->mblock.max, s->mblock.fail); tprint(buf, 16, 5); sprintf(buf, "%6d%6d%6d%6d%7d%8d%8d%8d", s->dblock.use, sum10.dblock.use / 10, sum30.dblock.use / 30, sum60.dblock.use / 60, sum120.dblock.use / 120, s->dblock.total, s->dblock.max, s->dblock.fail); tprint(buf, 16, 6); for (i=0; idblk[i].use * rbsize[i]; sprintf(buf, "%6d%6d%6d%6d%7d%8d%8d%8d", s->dblk[i].use, sum10.dblk[i].use / 10, sum30.dblk[i].use / 30, sum60.dblk[i].use / 60, sum120.dblk[i].use / 120, s->dblk[i].total, s->dblk[i].max, s->dblk[i].fail); tprint(buf, 16, 9 + i); } sprintf(buf, "%d/%d Kbytes", kbytes(mem), kbytes(total)); tprint(buf, 23, 23); calls++; sprintf(buf, "Calls=%d", calls); tprint(buf, 10, 0); } /* * add a to be, leave result in b * * N.B. Assumes that elements are ints */ addstrst(a, b) register alcdat* a; register alcdat* b; { register i; for (i=sizeof(struct strstat)/sizeof(*a); i--; (b++)->use += (a++)->use) ; } /* * subtract a from be, result in b */ substrst(a, b) register alcdat* a; register alcdat* b; { register i; for (i=sizeof(struct strstat)/sizeof(*a); i--; (b++)->use -= (a++)->use) ; } /* * multiply a by b, result in a */ mulstrst(a, b) register alcdat* a; register int b; { register i; for (i=sizeof(struct strstat)/sizeof(*a); i--; (a++)->use *= b) ; } # endif /* SAVECNT */ /* * nasty cleanup */ error(s) char* s; { perror(s); done(); } /* * nice cleanup */ done() { echo(1); cbreak(0); delay(1); tprint("", cols -1, lines - 1); printf("\n"); exit(0); } /* * read all once only stuff from kmem */ readstuff() { register i; if (!nl[NL_RBSIZE].n_value) { printf("%s: no value\n", nl[NL_RBSIZE].n_name); exit(1); } if (!nl[NL_STRST].n_value) { printf("%s: no value\n", nl[NL_STRST].n_name); exit(1); } # ifdef sun if (!nl[NL_NDBLKS].n_value) { printf("%s: no value\n", nl[NL_NDBLKS].n_name); # else if (!nl[NL_V].n_value) { printf("%s: no value\n", nl[NL_V].n_name); # endif exit(1); } if (!nl[NL_DBALLOC].n_value) { printf("%s: no value\n", nl[NL_DBALLOC].n_name); exit(1); } if (!nl[NL_NMBLOCK].n_value) { printf("%s: no value\n", nl[NL_NMBLOCK].n_name); exit(1); } if (lseek(fd, nl[NL_RBSIZE].n_value, 0) == -1) error("lseek"); if (read(fd, rbsize, sizeof(rbsize)) != sizeof(rbsize)) error("read"); # ifdef sun if (lseek(fd, nl[NL_NDBLKS].n_value, 0) == -1) error("lseek"); if (read(fd, cnt, sizeof(cnt)) != sizeof(cnt)) error("read"); # else if (lseek(fd, nl[NL_V].n_value, 0) == -1) error("lseek"); if (read(fd, &v, sizeof(v)) != sizeof(v)) error("read"); # endif # ifdef sun if (lseek(fd, nl[NL_DBALLOC].n_value, 0) == -1) error("lseek"); if (read(fd, &nl[NL_DBALLOC].n_value, sizeof(int)) != sizeof(int)) error("read"); # endif if (lseek(fd, nl[NL_DBALLOC].n_value, 0) == -1) error("lseek"); if (read(fd, dballoc, sizeof(dballoc)) != sizeof(dballoc)) error("read"); if (lseek(fd, nl[NL_NMBLOCK].n_value, 0) == -1) error("lseek"); if (read(fd, &nmblock, sizeof(nmblock)) != sizeof(nmblock)) error("read"); # ifdef sun if (lseek(fd, nl[NL_STRST].n_value, 0) == -1) error("lseek"); if (read(fd, &nl[NL_STRST].n_value, sizeof(int)) != sizeof(int)) error("read"); # endif } //E*O*F sw.c// echo x - term.h cat > "term.h" << '//E*O*F term.h//' # ifndef _TERM_H_ #include #include char *getenv(); char *tgetstr(); char PC; short ospeed; short lines; short cols; char ceolbuf[20]; char clbuf[20]; char pcbuf[20]; char cmbuf[20]; #undef putchar int putchar(); char term_buf[1024]; # endif _TERM_H_ //E*O*F term.h// echo x - termcap.c cat > "termcap.c" << '//E*O*F termcap.c//' /* * copyright (C) 1986 by Larry McVoy * MUST be distributed in source form only. */ # include "term.h" char* tgoto(); /*------------------------------------------------------------------15/Dec/86-* * init_term - read in the termcap stuff *----------------------------------------------------------------larry mcvoy-*/ termcap() { char *cp = getenv("TERM"); char *foo; char garbage[10]; struct sgttyb tty; gtty(1, &tty); ospeed = tty.sg_ospeed; if (cp == (char *) 0) return -1; if (tgetent(term_buf, cp) != 1) exit(1); foo = garbage; foo = tgetstr("pc", &foo); if (foo) PC = *foo; foo = clbuf; tgetstr("cl", &foo); foo = ceolbuf; tgetstr("ce", &foo); foo = cmbuf; tgetstr("cm", &foo); lines = tgetnum("li"); cols = tgetnum("co"); return 0; } /* clear to end of line */ ceol(col, row) { char *foo = tgoto(cmbuf, col, row); write(1, foo, strlen(foo)); tputs(ceolbuf, 1, putchar); } /* clear screen */ clear() { tputs(clbuf, lines, putchar); } /*------------------------------------------------------------------15/Dec/86-* * tputchar(c, col, row) - put a single char on the screen * * Inputs ----> (char), (int), (int) * * Bugs ------> Assumes that the coords make sense * * Revisions: *----------------------------------------------------------------larry mcvoy-*/ tputchar(c, col, row) char c; { register char* foo; foo = tgoto(cmbuf, col, row); tputs(foo, lines, putchar); write(1, &c, 1); } /*------------------------------------------------------------------15/Dec/86-* * tprint(s, col, row) - put a string on the screen * * Inputs ----> (char), (int), (int) * * Results ---> Puts the string out iff it will fit. * * Revisions: *----------------------------------------------------------------larry mcvoy-*/ tprint(s, col, row) register char* s; { register char* foo; if (row > lines || col > cols) return -1; if (strlen(s) > cols - col) return -2; foo = tgoto(cmbuf, col, row); tputs(foo, lines, putchar); return write(1, s, strlen(s)); } /* fake putchar for tputs */ putchar(c) char c; { write(1, &c, 1); } //E*O*F termcap.c// echo Possible errors detected by \'wc\' [hopefully none]: temp=/tmp/shar$$ trap "rm -f $temp; exit" 0 1 2 3 15 cat > $temp <<\!!! 17 53 336 Makefile 57 120 878 mode.c 63 359 2087 sw.1 563 1521 12403 sw.c 19 36 278 term.h 104 281 2203 termcap.c 823 2370 18185 total !!! wc Makefile mode.c sw.1 sw.c term.h termcap.c | sed 's=[^ ]*/==' | diff -b $temp - exit 0 --- Larry McVoy, Sun Microsystems (415) 336-7627 ...!sun!lm or lm@sun.com