Subject: cpuid, standalone mkfs, libc makedep, inode allocation bugs (#433)
Index:	pdpstand/{several},libc/pdp/csu/Makefile,sys/ufs_alloc.c 2.11BSD

Description:
	1. A PDP-11/53 is misidentified as a 11/45 by 'boot'.

	2. The standalone 'mkfs' program could not make filesystems on other
	   than the 'a' (0th) partition of a disk.

	3. "make depend" in /usr/src/lib/libc/pdp/csu would fail due to
	   references to nonexistent files.

	4. The system would go to sleep if a newly created filesystem ran
	   out of inodes.

Repeat-By:
	1. Boot a 11/53, notice that the prompt is "45boot:" instead of
	   "53boot".

	2. Load the standalone mkfs program from a "boottape" (or disk) and
	   attempt to make a filesystem on any partition other than 0.

		file system: ra(0,0,3)

	   notice the error message:

		ra(0,0,3) error reading labelsector

	3. Attempt to perform "make depend" in one area of the libc
	   hierarchy /usr/src/lib/libc/pdp/csu.   Notice the error messages
	   about 'gprof' related files not being found.

	4. Create a small filesystem with relatively few inodes, proceed to
	   copy files to the filesystem (but do not delete any files) until
	   the copy program goes to sleep.   Notice that any reference ('ls',
	   'cd', etc) to that filesystem cause the process to hang.  The system
	   must now be rebooted in order to become useful again.

Fix:
	Many thanks to Martijn van Buul (Pino@dohd.org) for finding problems
	1 thru 3 and testing the fixes to numbers 1 and 2.   He is the first
	person to bring up 2.11BSD on a PDP-11/53+ (thus encountering bug #1).
	He's also the first person to use the standalone mkfs since disklabels
	were added to create other than root filesystems (bug #2).

	I was the "lucky" fellow who encountered bug #4 during the process
	of creating a small root filesystem.   While attempting to save a bit of
	space by reducing the number of inodes I allocated too few.   The
	expectation was that a "out of inodes" error would be given, it was
	quite a surprise to have the system simply "go to sleep".

	Bug #1 is due to the fact that while other KDJ-11 processors have the
	MSCR register (to control cache parity traps, etc) the KDJ-11D (11/53)
	does not.   The cpu ID logic had a bug that caused it to think a system
	without the MSCR was a 11/45.   The fix was to rewrite the cpu id
	logic to not assume all KDJ-11 systems have a MSCR (since the 11/53
	does not have a cache it doesn't need cache parity control bits ;)).

	Bug #2 was introduced when disklabels were added to the system.  The
	End Of Volume check can not be done before the label has been read
	because the EOV check uses the disklabel contents!   The fix was to
	add a flag bit to the i/o structure and avoid the EOV checking if
	a disklabel (read or write) operation is in progress.

	Bug #3 was the result of an oversight 10 or 12 years ago when 4.3BSD
	was ported over to the PDP-11.   Graphical profiling ('gprof') was
	not implemented on the 11 and was placed in the PORT directory (in
	case anyone is tempted to give porting a try).   At that time the
	references to 'gprof' should have been removed from the Makefile
	so that "make depend" would not fail.

	Lastly #4.   This has been a bug in 2.11BSD from the very beginning.
	It only occurs under the rare circumstance of creating a filesystem
	and sequentially creating files without ever removing any files.

	 The kernel keeps a list (in the superblock) of up to 100 (NICINOD) 
	 free/available inodes so that a linear search of the disk does
	 not need to be made each time a file is created.   If the end
	 of the ondisk list is encountered a scan from the beginning is made
	 in an attempt to find the desired number of inodes.

	 Normally the two pass scan logic used in ialloc() works fine because 
	 1) files are being removed and  2) the number of inodes on the 
	 disk is much higher than 512 which makes it very likely the kernel 
	 will find 100 available inodes _somewhere_ on the disk.

	The comment added to ufs_alloc.c best summarizes the problem and
	the fix:

/*
 * If some inodes have been found but not NICINOD of them the intent was to
 * go back and scan the inode list from the beginning and add new inodes to
 * fs_inode[].   On filesystems with few free inodes this had the unfortunate 
 * side effect of finding the inodes we already knew about!   The resulting
 * duplicates caused fs_ninode to be higher than it should have been.   Part
 * of the allocation logic said there were inodes available (fs_ninode > 0)
 * while 'fs_tinode = 0' meant there were none!
 *
 * Since we're going to scan from the beginning simply toss away any inodes
 * already found.  Either NICINOD or all available inodes will be found.
*/

	And now on to the installation of the patch.   It's not required
	to build a new GENERIC kernel but I have found that keeping an 
	emergency kernel around to be a "good thing"

	Cut where indicated and save to a file (/tmp/patch.433).  Then:

		patch -p0 < /tmp/patch.433
		cd /sys/pdpstand
		make clean
		make
		cp boot /boot

		cd /sys/YOUR_KERNEL
		make
		mv /unix /ounix 
		mv /netnix /onetnix
		mv unix netnix /
		chmod 744 /unix /netnix
		(omit 'netnix' if you're not running a network'd system)

		cd /sys/GENERIC
		make
		mv unix /genunix
		chmod 744 /genunix

	Then reboot and you're all done.

	As always this and previous updates to 2.11BSD are available via
	anonymous FTP to either FTP.TO.GD-ES.COM or MOE.2BSD.COM in the
	directory /pub/2.11BSD.
----------------------------cut here---------------------
*** /usr/src/sys/pdpstand/M.s.old	Mon Jun  5 20:26:53 1995
--- /usr/src/sys/pdpstand/M.s	Tue Oct 17 20:10:05 2000
***************
*** 1,6 ****
  /
  /	SCCS id	@(#)M.s	1.7 (Berkeley)	7/11/83
! /		@(#)M.s	3.1 (2.11BSD)	1995/06/01 (sms@wlv.iipo.gtegsc.com)
  /
  / Startup code for two-stage bootstrap with support for autoboot.
  / Supports 11/45, 11/70, 11/53, 11/73, 11/83, 11/84, 11/93, 11/94
--- 1,6 ----
  /
  /	SCCS id	@(#)M.s	1.7 (Berkeley)	7/11/83
! /		@(#)M.s	3.2 (2.11BSD)	2000/10/17 (sms@moe.2bsd.com)
  /
  / Startup code for two-stage bootstrap with support for autoboot.
  / Supports 11/45, 11/70, 11/53, 11/73, 11/83, 11/84, 11/93, 11/94
***************
*** 104,110 ****
  /
  /	If 11/40 class processor, only need set the I space registers
  /
! 	movb	_sep_id, _ksep
  	jeq	1f
  
  /
--- 104,110 ----
  /
  /	If 11/40 class processor, only need set the I space registers
  /
! 	movb	_sep_id,_ksep
  	jeq	1f
  
  /
***************
*** 199,209 ****
  1:
  	mov	$nomfpt,nofault	/ catch possible fault from instruction
  	mfpt			/ 23/24, 44, and KDJ-11 have this instruction
  	cmp	r0,$1		/ 44?
  	bne	1f		/ no - br
  	mov	$1,*$MSCR	/ disable cache parity traps
! 	mov	$44.,_cputype
! 	rts	pc
  1:
  	cmp	r0,$5		/ KDJ-11?
  	bne	2f		/ no - br
--- 199,210 ----
  1:
  	mov	$nomfpt,nofault	/ catch possible fault from instruction
  	mfpt			/ 23/24, 44, and KDJ-11 have this instruction
+ 	bic	$!377,r0	/ cpu module in low byte
  	cmp	r0,$1		/ 44?
  	bne	1f		/ no - br
  	mov	$1,*$MSCR	/ disable cache parity traps
! 	mov	$44.,r0
! 	br	out
  1:
  	cmp	r0,$5		/ KDJ-11?
  	bne	2f		/ no - br
***************
*** 210,222 ****
  	mov	*$MAINT,r0	/ get system maint register
  	ash	$-4,r0		/ move down and
  	bic	$177760,r0	/  isolate the module id
- 	mov	$1,*$MSCR	/ disable cache parity traps
  	movb	j11typ(r0),r0	/ lookup cpu type
  	movb	_ubmap,r1	/ unibus?
- 	beq	1f		/ nope - br
- 	bis	$2,*$MSCR	/ disable unibus traps
- 1:
  	add	r1,r0		/ bump the cpu type (93 -> 94, 83 ->84)
  	br	out
  2:
  	cmp	r0,$3		/ 23 or 24?
--- 211,224 ----
  	mov	*$MAINT,r0	/ get system maint register
  	ash	$-4,r0		/ move down and
  	bic	$177760,r0	/  isolate the module id
  	movb	j11typ(r0),r0	/ lookup cpu type
  	movb	_ubmap,r1	/ unibus?
  	add	r1,r0		/ bump the cpu type (93 -> 94, 83 ->84)
+ 	mov	$out,nofault	/ catch fault if no MSCR register (53)
+ 	mov	$1,*$MSCR	/ disable cache parity traps
+ 	tst	r1		/ unibus machine?
+ 	beq	out		/ nope - we're done
+ 	bis	$2,*$MSCR	/ disable unibus traps
  	br	out
  2:
  	cmp	r0,$3		/ 23 or 24?
***************
*** 237,246 ****
  2:
  	mov	$40.,r0		/ assume 40
  	mov	$out,nofault
! 	tst	*$MSCR		/ 60 has MSCR, 40 doesn't
  	mov	$60.,r0
- 	mov	$1,*$MSCR
  out:
  	mov	r0,_cputype
  	rts	pc
  
--- 239,248 ----
  2:
  	mov	$40.,r0		/ assume 40
  	mov	$out,nofault
! 	mov	$1,*$MSCR	/ 60 has MSCR, 40 doesn't - disable cache traps
  	mov	$60.,r0
  out:
+ 	clr	nofault
  	mov	r0,_cputype
  	rts	pc
  
*** /usr/src/sys/pdpstand/conf.c.old	Fri Nov  7 19:49:28 1997
--- /usr/src/sys/pdpstand/conf.c	Fri Oct 20 20:06:03 2000
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)conf.c	2.7 (2.11BSD) 1997/11/7
   */
  
  #include "../h/param.h"
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)conf.c	2.8 (2.11BSD) 2000/10/20
   */
  
  #include "../h/param.h"
***************
*** 127,139 ****
  	int	(*strat)() = devsw[io->i_ino.i_dev].dv_strategy;
  	register struct disklabel *lp;
  	register struct partition *pi;
  	
  	switch	(fnc)
  		{
  		case	WRITELABEL:
! 			return(writelabel(io, strat));
  		case	READLABEL:
! 			return(readlabel(io, strat));
  		case	DEFAULTLABEL:
  /*
   * Zero out the label buffer and then assign defaults common to all drivers.
--- 127,144 ----
  	int	(*strat)() = devsw[io->i_ino.i_dev].dv_strategy;
  	register struct disklabel *lp;
  	register struct partition *pi;
+ 	int	status;
  	
+ 	io->i_flgs |= F_LABEL;		/* label operation now in progress */
+ 
  	switch	(fnc)
  		{
  		case	WRITELABEL:
! 			status = writelabel(io, strat);
! 			break;
  		case	READLABEL:
! 			status = readlabel(io, strat);
! 			break;
  		case	DEFAULTLABEL:
  /*
   * Zero out the label buffer and then assign defaults common to all drivers.
***************
*** 165,175 ****
  */
  			lp->d_bbsize = 512;
  			lp->d_sbsize = SBSIZE;
! 			return((*dvlab)(io));
  		default:
  			printf("devlabel: bad fnc %d\n");
! 			return(-1);
  		}
  	}
  
  /*
--- 170,184 ----
  */
  			lp->d_bbsize = 512;
  			lp->d_sbsize = SBSIZE;
! 			status = (*dvlab)(io);
! 			break;
  		default:
  			printf("devlabel: bad fnc %d\n");
! 			status = -1;
! 			break;
  		}
+ 	io->i_flgs &= ~F_LABEL;
+ 	return(status);
  	}
  
  /*
***************
*** 212,217 ****
--- 221,231 ----
   * Check for end of volume.  Actually this checks for end of partition.
   * Since this is almost always called when reading unlabeled disks (treating
   * a floppy as a short tape for example) it's effectively an EOV check.
+  *
+  * If a 'label' operation is in progress do not perform any checking because
+  * there is nothing to compare against.   The label hasn't been read yet!  The
+  * block number has been calculated/set to be the label sector so obviously 
+  * an end of volume condition can not exist.
  */
  
  deveovchk(io)
***************
*** 220,225 ****
--- 234,241 ----
  	register struct partition *pi;
  	daddr_t  sz, eov;
  
+ 	if	(io->i_flgs & F_LABEL)
+ 		return(1);
  	pi = &io->i_label.d_partitions[io->i_part];
  	sz = io->i_cc / 512;
  /*
*** /usr/src/sys/pdpstand/saio.h.old	Fri Mar  8 15:27:52 1996
--- /usr/src/sys/pdpstand/saio.h	Fri Oct 20 20:01:34 2000
***************
*** 3,9 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)saio.h	2.2 (2.11BSD GTE) 1996/3/8
   */
  
  /*
--- 3,9 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)saio.h	2.3 (2.11BSD) 2000/10/20
   */
  
  /*
***************
*** 42,47 ****
--- 42,48 ----
  #define	F_ALLOC	04
  #define	F_FILE	010
  #define	F_TAPE	020
+ #define	F_LABEL	040
  #define	READ	F_READ
  #define	WRITE	F_WRITE
  
*** /usr/src/lib/libc/pdp/csu/Makefile.old	Sat Apr  9 01:15:24 1994
--- /usr/src/lib/libc/pdp/csu/Makefile	Tue Oct 24 20:06:35 2000
***************
*** 3,17 ****
  # All rights reserved.  The Berkeley software License Agreement
  # specifies the terms and conditions for redistribution.
  #
! #	@(#)Makefile	5.6.1 (2.11BSD GTE) 4/9/94
  #
  #	crt0	Normal C run time startoff
  #	mcrt0	C run time start off for profiling, ``prof'' conventions
- #	gcrt0	C run time start off for profiling, ``gprof'' conventions
  #
  DESTDIR=
! SRCS=	crt0.s mon.c mcount.s gmon.c
! #OBJS=	crt0.o mcrt0.o gcrt0.o mon.o gmon.o
  OBJS=	crt0.o mcrt0.o mon.o
  CFLAGS=	-O ${DEFS}
  TAGSFILE=tags
--- 3,15 ----
  # All rights reserved.  The Berkeley software License Agreement
  # specifies the terms and conditions for redistribution.
  #
! #	@(#)Makefile	5.6.2 (2.11BSD) 2000/10/24
  #
  #	crt0	Normal C run time startoff
  #	mcrt0	C run time start off for profiling, ``prof'' conventions
  #
  DESTDIR=
! SRCS=	crt0.s mon.c mcount.s
  OBJS=	crt0.o mcrt0.o mon.o
  CFLAGS=	-O ${DEFS}
  TAGSFILE=tags
***************
*** 21,27 ****
  install: ${OBJS}
  	install -c -m 0644 crt0.o ${DESTDIR}/lib/crt0.o
  	install -c -m 0644 mcrt0.o ${DESTDIR}/lib/mcrt0.o
- #	install -c -m 0644 gcrt0.o ${DESTDIR}/usr/lib/gcrt0.o
  
  crt0.o:	crt0.s
  	/lib/cpp ${DEFS} ${DFLAGS} crt0.s | sed 's;^#;/;' | as -o x.o
--- 19,24 ----
***************
*** 33,41 ****
  	ld -x -r -o moncrt0.o x.o
  	rm -f x.o
  
- gcrt0.o: moncrt0.o gmon.o
- 	ld -x -r -o gcrt0.o moncrt0.o gmon.o
- 
  mcrt0.o: moncrt0.o mon.o
  	ld -x -r -o mcrt0.o moncrt0.o mon.o
  
--- 30,35 ----
***************
*** 48,60 ****
  	ld -x -r -o mon.o x.o
  	rm -f x.o x.s xx.s mon.s
  
- gmon.o: gmon.c gmon.h gmon.ex
- 	cc ${CFLAGS} -S ${DFLAGS} gmon.c
- 	ex - gmon.s < gmon.ex
- 	as -o x.o gmon.s
- 	ld -x -r -o gmon.o x.o
- 	rm -f x.o gmon.s
- 
  tags:	
  	cwd=`pwd`; \
  	for i in *.c; do \
--- 42,47 ----
***************
*** 87,93 ****
  
  crt0.o: crt0.s
  mon.o: mon.c
! gmon.o: gmon.c ./gmon.h
  # DEPENDENCIES MUST END AT END OF FILE
  # IF YOU PUT STUFF HERE IT WILL GO AWAY
  # see make depend above
--- 74,80 ----
  
  crt0.o: crt0.s
  mon.o: mon.c
! mcount.o: mcount.s ./../sys/SYS.h /usr/include/syscall.h
  # DEPENDENCIES MUST END AT END OF FILE
  # IF YOU PUT STUFF HERE IT WILL GO AWAY
  # see make depend above
*** /usr/src/sys/sys/ufs_alloc.c.old	Thu Sep 19 21:49:24 1996
--- /usr/src/sys/sys/ufs_alloc.c	Sat Oct 14 23:57:02 2000
***************
*** 3,14 ****
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_alloc.c	1.3 (2.11BSD GTE) 1996/9/19
   */
  
  #include "param.h"
  #include "../machine/seg.h"
- 
  #include "fs.h"
  #include "dir.h"
  #include "inode.h"
--- 3,13 ----
   * All rights reserved.  The Berkeley software License Agreement
   * specifies the terms and conditions for redistribution.
   *
!  *	@(#)ufs_alloc.c	1.4 (2.11BSD) 2000/10/14
   */
  
  #include "param.h"
  #include "../machine/seg.h"
  #include "fs.h"
  #include "dir.h"
  #include "inode.h"
***************
*** 37,44 ****
  {
  	register struct fs *fs;
  	register struct buf *bp;
! 	int	async;
  	daddr_t bno;
  
  	fs = ip->i_fs;
  	async = fs->fs_flags & MNT_ASYNC;
--- 36,44 ----
  {
  	register struct fs *fs;
  	register struct buf *bp;
! 	int	async, i;
  	daddr_t bno;
+ 	char *fullerr = "file system full";
  
  	fs = ip->i_fs;
  	async = fs->fs_flags & MNT_ASYNC;
***************
*** 104,122 ****
  nospace:
  	fs->fs_nfree = 0;
  	fs->fs_tfree = 0;
! 	fserr(fs, "file system full");
  	/*
  	 * THIS IS A KLUDGE...
  	 * SHOULD RATHER SEND A SIGNAL AND SUSPEND THE PROCESS IN A
  	 * STATE FROM WHICH THE SYSTEM CALL WILL RESTART
  	 */
! 	uprintf("\n%s: write failed, file system full\n", fs->fs_fsmnt);
! 	{
! 		register int i;
! 
! 		for (i = 0; i < 5; i++)
! 			sleep((caddr_t)&lbolt, PRIBIO);
! 	}
  	u.u_error = ENOSPC;
  	return(NULL);
  }
--- 104,118 ----
  nospace:
  	fs->fs_nfree = 0;
  	fs->fs_tfree = 0;
! 	fserr(fs, fullerr);
  	/*
  	 * THIS IS A KLUDGE...
  	 * SHOULD RATHER SEND A SIGNAL AND SUSPEND THE PROCESS IN A
  	 * STATE FROM WHICH THE SYSTEM CALL WILL RESTART
  	 */
! 	uprintf("\n%s: %s\n", fs->fs_fsmnt, fullerr);
! 	for (i = 0; i < 5; i++)
! 		sleep((caddr_t)&lbolt, PRIBIO);
  	u.u_error = ENOSPC;
  	return(NULL);
  }
***************
*** 181,190 ****
  	if (fs->fs_nbehind < 4 * NICINOD) {
  		first = 1;
  		ino = fs->fs_lasti;
- #ifdef DIAGNOSTIC
- 		if (itoo(ino))
- 			panic("ialloc");
- #endif DIAGNOSTIC
  		adr = itod(ino);
  	} else {
  fromtop:
--- 177,182 ----
***************
*** 202,218 ****
  			continue;
  		}
  		dp = (struct dinode *)mapin(bp);
! 		for (i = 0;i < INOPB;i++) {
  			if (dp->di_mode != 0)
! 				goto cont;
  			if (ifind(pip->i_dev, ino))
! 				goto cont;
  			fs->fs_inode[fs->fs_ninode++] = ino;
  			if (fs->fs_ninode >= NICINOD)
  				break;
- 		cont:
- 			ino++;
- 			dp++;
  		}
  		mapout(bp);
  		brelse(bp);
--- 194,207 ----
  			continue;
  		}
  		dp = (struct dinode *)mapin(bp);
! 		for (i = 0; i < INOPB; i++, ino++, dp++) {
  			if (dp->di_mode != 0)
! 				continue;
  			if (ifind(pip->i_dev, ino))
! 				continue;
  			fs->fs_inode[fs->fs_ninode++] = ino;
  			if (fs->fs_ninode >= NICINOD)
  				break;
  		}
  		mapout(bp);
  		brelse(bp);
***************
*** 219,226 ****
--- 208,230 ----
  		if (fs->fs_ninode >= NICINOD)
  			break;
  	}
+ /*
+  * If some inodes have been found but not NICINOD of them the intent was to
+  * go back and scan the inode list from the beginning and add new inodes to
+  * fs_inode[].   On filesystems with few free inodes this had the unfortunate 
+  * side effect of finding the inodes we already knew about!   The resulting
+  * duplicates caused fs_ninode to be higher than it should have been.   Part
+  * of the allocation logic said there were inodes available (fs_ninode > 0)
+  * while 'fs_tinode = 0' meant there were none!
+  *
+  * Since we're going to scan from the beginning simply toss away any inodes
+  * already found.  Either NICINOD or all available inodes will be found.
+ */
  	if (fs->fs_ninode < NICINOD && first)
+ 		{
+ 		fs->fs_ninode = 0;
  		goto fromtop;
+ 		}
  	fs->fs_lasti = inobas;
  	fs->fs_ilock = 0;
  	wakeup((caddr_t)&fs->fs_ilock);
*** /VERSION.old	Wed May 17 21:02:26 2000
--- /VERSION	Tue Oct 17 19:30:52 2000
***************
*** 1,5 ****
! Current Patch Level: 432
! Date: May 17, 2000
  
  2.11 BSD
  ============
--- 1,5 ----
! Current Patch Level: 433
! Date: October 17, 2000
  
  2.11 BSD
  ============
