From: scjones@thor.sdrc.com (Larry Jones)
Newsgroups: comp.unix.sysv386
Subject: ISC 2.0.2 Fix for inode bug
Date: 11 Apr 92 22:23:35 GMT
Organization: SDRC, Cincinnati

Here's the inode bug workaround for ISC 2.0.2 that Bill Wells posted
long ago.  Note that this is really a workaround rather than a fix --
in particular, should the free inode count in the superblock become
corrupted such that it is larger than the actual number of free inodes,
it is possible for the kernel to go into an infinite loop.  The chances
of this actually happening in real life is essentially zero.  The
message headers got lost somewhere along the way, but here's Bill's
message: 

Since I've got so many requests for the fix for the inode bug for ISC
2.0.2, I decided to post it. Sorry I've been so slothful on this;
various things popped up and I wasn't able to get it all together.

In case you want to know what this is: there is a kernel bug that, on
occasion, and particularly if you are running a newsfeed, will cause
the file system to think that there are no more free inodes, thus
preventing creation of new files. Running fsck fixes the inode count
but doesn't prevent the problem from occuring again.

The problem is this: there is a "free inode cache" stored in the
superblock for each file system; this block is kept in memory when
the file system is mounted, thus the inode cache permits rapid
allocation of inodes. When the cache is emptied, the kernel tries to
read more inodes from the disk to fill the cache and then retries the
allocate. If the kernel is unable to read more inodes from the disk,
it assumes that there are no more free inodes.

There is an optimization in the allocation code, which depends on the
condition that the lowest free inode is always in the inode cache.
What it does is to start the disk read from that lowest inode,
instead of the first inode. This means that the inode table doesn't
have to always be fully read, for what could be a significant savings
in allocating inodes. (Consider what might happen when almost all
inodes are in use.)

However, the kernel does not maintain that condition properly. It is
possible for the kernel to forget the lowest inode, with the effect
that the kernel tries to read from some place too far in the inode
table, and maybe discovers that there are no free inodes. When that
happens, the kernel clears the available inode count, and the file
system is essentially kaput.

The right fix for this would be to always maintain that condition.
However, a binary patch for that would be tricky at best, and maybe
impossible. A patch that is possible is to have the allocation routine
try from the beginning of the inode table whenever it fails to read
inodes from the disk, relying on the free inode count to tell when
the table is empty. This changes the condition that must be
maintained to: the free inode count must always be accurate. (Having
the free inode count never be larger than the actual number of free
inodes is sufficient for the patch to not cause problems.)

I made a similar patch for Microport SysV/386 3.0e and have been
running it for most of this year without problems. I was asked to
solve this for Interactive 2.0.2 and did so. However, I did the work
on my Microport system, and the enclosed shell script works on that;
it ought to work on an Interactive system but I've not tried this.

With that caveat, here is what you do to patch your kernel. First,
run the shell script. Make sure that it behaved correctly. Then save
a copy of your good kernel and /etc/conf/pack.d/s5/Driver.o. If you
are really paranoid, back up your whole system, though this shouldn't
be necessary. Replace the Driver.o file with the one on /tmp.
Finally, rebuild your kernel. That particular bug should never bite
you again. Further kernel builds will have the bug fixed as well. If
you have any problems, send me e-mail. I'll try to get back to you
quicker than I did with this! If the patch causes some nasty kind of
crash, please post immediately in hopes that others will read your
message before having tried the patch.

in=/etc/conf/pack.d/s5/Driver.o
out=/tmp/Driver.o

# check that we have the right Driver.o file

if [ x"`sum $in`" != x"50880 81 $in" ]; then
	echo "sum failed"
	exit 1
fi
if [ x"`sum -r $in`" != x"33908    81 $in" ]; then
	echo "sum -r failed"
	exit 1
fi

# copy the file and make an appropriate fix

{
	dd ibs=1 obs=1k count=1977
	dd bs=19 count=1 of=/dev/null
	echo '\074\0144\017\0204\0327\0376\0377\0377\0146\c'
	echo '\0307\0207\0324\00\00\00\0144\00\0353\0151\c'
	dd bs=16k
} <$in >$out

# compare the list of differences against the expected differences

cat <<\+ >/tmp/fix$$
  1978  75  74
  1980   0  17
  1981   0 204
  1982   0 327
  1983 164 376
  1984  14 377
  1985 146 377
  1986 307 146
  1987 207 307
  1988 324 207
  1989   0 324
  1992 144   0
  1993   0 144
  1994 353   0
  1995 152 353
  1996 220 151
+
if cmp -l $in $out | cmp -s - /tmp/fix$$; then
	rm /tmp/fix$$
	exit 0
else
	rm /tmp/fix$$
	echo "patch failed"
	exit 1
fi

---
Bill                    { uunet | novavax | ankh | sunvice } !twwells!bill
bill@twwells.com
----
Larry Jones, SDRC, 2000 Eastman Dr., Milford, OH  45150-2789  513-576-2070
Domain: scjones@sdrc.com  Path: uunet!sdrc!scjones  IBM: USSDR7DR at IBMMAIL
There's a connection here, I just know it. -- Calvin