Subject: Re: increasing dump's speed
To: Manuel Bouyer <bouyer@antioche.lip6.fr>
From: Manuel Bouyer <bouyer@antioche.lip6.fr>
List: tech-userlevel
Date: 03/19/1999 12:13:24
--WIyZ46R2i8wDzkSu
Content-Type: text/plain; charset=us-ascii

Ok, following the advice from Ignatios Souvatzis, I added a command line
option to change the block size. I also changed the functions and
variables names to respect the coding style.
New diffs appened below, I expect to commit this Monday.

--
Manuel Bouyer, LIP6, Universite Paris VI.           Manuel.Bouyer@lip6.fr
--

--WIyZ46R2i8wDzkSu
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=diff

Index: Makefile
===================================================================
RCS file: /cvsroot/src/sbin/dump/Makefile,v
retrieving revision 1.21
diff -u -r1.21 Makefile
--- Makefile	1999/03/09 17:25:52	1.21
+++ Makefile	1999/03/19 11:12:30
@@ -16,8 +16,8 @@
 PROG=	dump
 LINKS=	${BINDIR}/dump ${BINDIR}/rdump
 CPPFLAGS+=-DRDUMP
-# CPPFLAGS+= -DDEBUG -DTDEBUG -DFDEBUG -DWRITEDEBUG
-SRCS=	itime.c main.c optr.c dumprmt.c tape.c traverse.c unctime.c \
+# CPPFLAGS+= -DDEBUG -DTDEBUG -DFDEBUG -DWRITEDEBUG -DSTATS -DDIAGNOSTICS
+SRCS=	itime.c main.c optr.c dumprmt.c rcache.c tape.c traverse.c unctime.c \
 	ffs_bswap.c
 BINGRP=	tty
 BINMODE=2555
Index: dump.8
===================================================================
RCS file: /cvsroot/src/sbin/dump/dump.8,v
retrieving revision 1.31
diff -u -r1.31 dump.8
--- dump.8	1999/03/09 17:25:52	1.31
+++ dump.8	1999/03/19 11:12:33
@@ -49,7 +49,9 @@
 .Op Fl d Ar density
 .Op Fl f Ar file
 .Op Fl h Ar level
+.Op Fl k Ar read blocksize
 .Op Fl L Ar label
+.Op Fl r Ar cachesize
 .Op Fl s Ar feet
 .Op Fl T Ar date
 .Ar files-to-dump
@@ -170,6 +172,8 @@
 The default honor level is 1,
 so that incremental backups omit such files
 but full backups retain them.
+.It Fl k Ar read blocksize
+The size in kilobyte of a the read buffers. Default is 32k.
 .It Fl L Ar label
 The user-supplied text string
 .Ar label
@@ -190,6 +194,14 @@
 .Qq operator
 by means similar to a
 .Xr wall 1 .
+.It Fl r Ar cachesize
+Use that many buffers for read cache operations. 
+A value of zero disables the read cache altogether, higher values
+improve read performance by reading larger data blocks from the
+disk and maintaining them in an LRU cache. See the 
+.Fl k
+option for the size of the buffers. Maximum is 512, the size of the cache is
+limited to 15% of the avail RAM by default.
 .It Fl s Ar feet
 Attempt to calculate the amount of tape needed
 at a particular density.
Index: dump.h
===================================================================
RCS file: /cvsroot/src/sbin/dump/dump.h,v
retrieving revision 1.15
diff -u -r1.15 dump.h
--- dump.h	1999/01/15 13:32:06	1.15
+++ dump.h	1999/03/19 11:12:35
@@ -146,10 +146,15 @@
 
 /* file dumping routines */
 void	blksout __P((daddr_t *blkp, int frags, ino_t ino));
-void	bread __P((daddr_t blkno, char *buf, int size));	
 void	dumpino __P((struct dinode *dp, ino_t ino));
 void	dumpmap __P((char *map, int type, ino_t ino));
 void	writeheader __P((ino_t ino));
+
+/* data block caching */
+void	bread __P((daddr_t blkno, char *buf, int size));	
+void	rawread __P((daddr_t, char *, int));
+void	initcache __P((int, int));
+void	printcachestats __P((void));
 
 /* tape writing routines */
 int	alloctape __P((void));
Index: main.c
===================================================================
RCS file: /cvsroot/src/sbin/dump/main.c,v
retrieving revision 1.21
diff -u -r1.21 main.c
--- main.c	1999/01/03 02:17:46	1.21
+++ main.c	1999/03/19 11:12:35
@@ -93,6 +93,8 @@
 long	dev_bsize = 1;		/* recalculated below */
 long	blocksperfile = 0;	/* output blocks per file */
 char	*host = NULL;		/* remote host (if any) */
+int	readcache = -1;		/* read cache size (in readblksize blks) */
+int	readblksize = 32 * 1024; /* read block size */
 
 int	main __P((int, char *[]));
 static long numarg __P((char *, long, long));
@@ -136,7 +138,7 @@
 
 	obsolete(&argc, &argv);
 	while ((ch = getopt(argc, argv,
-	    "0123456789B:b:cd:f:h:L:ns:ST:uWw")) != -1)
+	    "0123456789B:b:cd:f:h:k:L:nr:s:ST:uWw")) != -1)
 		switch (ch) {
 		/* dump level */
 		case '0': case '1': case '2': case '3': case '4':
@@ -171,6 +173,10 @@
 			honorlevel = numarg("honor level", 0L, 10L);
 			break;
 
+		case 'k':
+			readblksize = numarg("read block size", 0, 64) * 1024;
+			break;
+
 		case 'L':
 			/*
 			 * Note that although there are LBLSIZE characters,
@@ -191,6 +197,10 @@
 			notify = 1;
 			break;
 
+		case 'r':		/* read cache size */
+			readcache = numarg("read cache size", 0, 512);
+			break;
+		
 		case 's':		/* tape size, feet */
 			tsize = numarg("tape size", 1L, 0L) * 12 * 10;
 			break;
@@ -379,7 +389,7 @@
 	}
 	sync();
 	sblock = (struct fs *)sblock_buf;
-	bread(SBOFF, (char *) sblock, SBSIZE);
+	rawread(SBOFF, (char *) sblock, SBSIZE);
 	if (sblock->fs_magic != FS_MAGIC) {
 		if (sblock->fs_magic == bswap32(FS_MAGIC)) {
 			ffs_sb_swap(sblock, sblock, 0);
@@ -442,6 +452,8 @@
 
 	nonodump = iswap32(spcl.c_level) < honorlevel;
 
+	initcache(readcache, readblksize);
+	
 	(void)signal(SIGINFO, statussig);
 
 	msg("mapping (Pass I) [regular files]\n");
@@ -582,9 +594,10 @@
 usage()
 {
 
-	(void)fprintf(stderr, "%s\n%s\n%s\n",
+	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
 "usage: dump [-0123456789cnu] [-B records] [-b blocksize] [-d density]",
-"            [-f file] [-h level] [-L label] [-s feet] [-T date] filesystem",
+"            [-f file] [-h level] [-k read block size] [-L label]",
+"            [-r read cache size] [-s feet] [-T date] filesystem",
 "       dump [-W | -w]");
 	exit(1);
 }
Index: rcache.c
===================================================================
RCS file: rcache.c
diff -N rcache.c
--- /dev/null	Fri Mar 19 00:42:00 1999
+++ rcache.c	Fri Mar 19 03:12:36 1999
@@ -0,0 +1,445 @@
+/*      $NetBSD$       */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Martin J. Laubach <mjl@emsi.priv.at> and 
+ *    Manuel Bouyer <Manuel.Bouyer@lip6.fr>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by the NetBSD
+ *      Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*-----------------------------------------------------------------------*/
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#include "dump.h"
+
+/*-----------------------------------------------------------------------*/
+#define MAXCACHEBUFS	512	/* max 512 buffers */
+#define MAXMEMPART	6	/* max 15% of the user mem */
+
+/*-----------------------------------------------------------------------*/
+struct cheader {
+	volatile size_t count;
+};
+
+struct cdesc {
+	volatile daddr_t blkstart;
+	volatile daddr_t blkend;/* start + nblksread */
+	volatile daddr_t blocksRead;
+	volatile size_t time;
+#ifdef DIAGNOSTICS
+	volatile pid_t owner;
+#endif
+};
+
+static int findlru __P((void));
+
+static void *sharebuffer = NULL;
+static struct cheader *cheader;
+static struct cdesc *cdesc;
+static char *cdata;
+static int cachebufs;
+static int nblksread;
+
+#ifdef STATS
+static int nreads;
+static int nphysread;
+static int64_t readsize;
+static int64_t physreadsize;
+#endif
+
+#define CDATA(i)	(cdata + ((i) * nblksread * dev_bsize))
+
+/*-----------------------------------------------------------------------*/
+void 
+initcache(cachesize, readblksize)
+	int cachesize;
+	int readblksize;
+{
+	size_t len;
+	size_t  sharedsize;
+
+	nblksread = readblksize / dev_bsize;
+	if(cachesize == -1) {	/* Compute from memory available */
+		int usermem;
+		int mib[2] = { CTL_HW, HW_USERMEM };
+		
+		len = sizeof(usermem);
+		if (sysctl(mib, 2, &usermem, &len, NULL, 0) < 0) {
+			msg("sysctl(hw.usermem) failed: %s\n", strerror(errno));
+			return;
+		}
+		cachebufs = (usermem / MAXMEMPART) / (nblksread * dev_bsize);
+	} else {		/* User specified */
+		cachebufs = cachesize;
+	}
+	
+	if(cachebufs) {	/* Don't allocate if zero --> no caching */
+		if (cachebufs > MAXCACHEBUFS)
+			cachebufs = MAXCACHEBUFS;
+
+		sharedsize = sizeof(struct cheader) +
+	   	    sizeof(struct cdesc) * cachebufs +
+	   	    nblksread * cachebufs * dev_bsize;
+#ifdef STATS	
+		fprintf(stderr, "Using %d buffers (%d bytes)\n", cachebufs,
+	   	    sharedsize);
+#endif
+		sharebuffer = mmap(NULL, sharedsize, PROT_READ | PROT_WRITE,
+	   	    MAP_ANON | MAP_SHARED, -1, 0);
+		if (sharebuffer == (void *)-1) {
+			msg("can't mmap shared memory for buffer: %s\n",
+			    strerror(errno));
+			return;
+		}
+		cheader = sharebuffer;
+		cdesc = (struct cdesc *) (((char *) sharebuffer) +
+		    sizeof(struct cheader));
+		cdata = ((char *) sharebuffer) + sizeof(struct cheader) +
+	   	    sizeof(struct cdesc) * cachebufs;
+
+		memset(sharebuffer, '\0', sharedsize);
+	}
+}
+/*-----------------------------------------------------------------------*/
+/* Find the cache buffer descriptor that shows the minimal access time */
+
+static int 
+findlru()
+{
+	int     i;
+	int     mintime = cdesc[0].time;
+	int     minidx = 0;
+
+	for (i = 0; i < cachebufs; i++) {
+		if (cdesc[i].time < mintime) {
+			minidx = i;
+			mintime = cdesc[i].time;
+		}
+	}
+
+	return minidx;
+}
+/*-----------------------------------------------------------------------*/
+/*
+ * Read data directly from disk, with smart error handling.
+ * Try to recover from hard errors by reading in sector sized pieces.
+ * Error recovery is attempted at most BREADEMAX times before seeking
+ * consent from the operator to continue.
+ */
+
+
+static int breaderrors = 0;
+#define BREADEMAX 32
+
+void 
+rawread(blkno, buf, size)
+	daddr_t blkno;
+	char *buf;
+	int size;
+{
+	int cnt, i;
+#ifdef STATS
+	nphysread++;
+	physreadsize += size;
+#endif
+
+	if (lseek(diskfd, ((off_t) blkno << dev_bshift), 0) < 0) {
+		msg("rawread: lseek fails\n");
+		goto err;
+	}
+	if ((cnt =  read(diskfd, buf, size)) == size)
+		return;
+	if (cnt == -1)
+		msg("read error from %s: %s: [block %d]: count=%d\n",
+			disk, strerror(errno), blkno, size);
+	else
+		msg("short read error from %s: [block %d]: count=%d, got=%d\n",
+			disk, blkno, size, cnt);
+err:
+	if (++breaderrors > BREADEMAX) {
+		msg("More than %d block read errors from %d\n",
+			BREADEMAX, disk);
+		broadcast("DUMP IS AILING!\n");
+		msg("This is an unrecoverable error.\n");
+		if (!query("Do you want to attempt to continue?")){
+			dumpabort(0);
+			/*NOTREACHED*/
+		} else
+			breaderrors = 0;
+	}
+	/*
+	 * Zero buffer, then try to read each sector of buffer separately.
+	 */
+	memset(buf, 0, size);
+	for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
+		if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0) {
+			msg("rawread: lseek2 fails: %s!\n",
+			    strerror(errno));
+			continue;
+		}
+		if ((cnt = read(diskfd, buf, (int)dev_bsize)) == dev_bsize)
+			continue;
+		if (cnt == -1) {
+			msg("read error from %s: %s: [sector %d]: count=%d: "
+			    "%s\n", disk, strerror(errno), blkno, dev_bsize,
+			    strerror(errno));
+			continue;
+		}
+		msg("short read error from %s: [sector %d]: count=%d, got=%d\n",
+		    disk, blkno, dev_bsize, cnt);
+	}
+}
+
+/*-----------------------------------------------------------------------*/
+#define min(a,b)	(((a) < (b)) ? (a) : (b))
+
+void 
+bread(blkno, buf, size)
+	daddr_t blkno;
+	char *buf;
+	int size;
+{
+	int     osize = size;
+	daddr_t oblkno = blkno;
+	char   *obuf = buf;
+	daddr_t numblocks = (size + dev_bsize -1) / dev_bsize;
+
+#ifdef STATS
+	nreads++;
+	readsize += size;
+#endif
+
+	if (!sharebuffer) {
+		rawread(blkno, buf, size);
+		return;
+	}
+
+	if (flock(diskfd, LOCK_EX)) {
+		msg("flock(LOCK_EX) failed: %s\n",
+		    strerror(errno));
+		rawread(blkno, buf, size);
+		return;
+	}
+
+
+retry:
+	while(size > 0) {
+		int     i;
+		
+		for (i = 0; i < cachebufs; i++) {
+			struct cdesc *curr = &cdesc[i];
+
+#ifdef DIAGNOSTICS
+			if (curr->owner) {
+				fprintf(stderr, "Owner is set (%d, me=%d), can"
+				    "not happen.\n", curr->owner, getpid());
+			}
+#endif
+
+			if (curr->blkend == 0)
+				continue;
+			/*
+			 * If we find a bit of the read in the buffers,
+			 * now compute how many blocks we can copy,
+			 * copy them out, adjust blkno, buf and size,
+			 * and restart
+			 */
+			if (curr->blkstart <= blkno &&
+			    blkno < curr->blkend) {
+				/* Number of data blocks to be copied */
+				int tocopy = min(size,
+				    (curr->blkend - blkno) * dev_bsize);
+#ifdef DIAGNOSTICS
+				if (tocopy <= 0 ||
+				    tocopy > nblksread * dev_bsize) {
+					fprintf(stderr, "tocopy %d !\n",
+					    tocopy);
+					dumpabort(0);
+				}
+				if (CDATA(i) + (blkno - curr->blkstart) *
+			   	    dev_bsize < CDATA(i) ||
+			   	    CDATA(i) + (blkno - curr->blkstart) *
+			   	    dev_bsize >
+				    CDATA(i) + nblksread * dev_bsize) {
+					fprintf(stderr, "%p < %p !!!\n",
+				   	   CDATA(i) + (blkno -
+						curr->blkstart) * dev_bsize,
+					   CDATA(i));
+					fprintf(stderr, "cdesc[i].blkstart %d "
+					    "blkno %d dev_bsize %ld\n", 
+				   	    curr->blkstart, blkno, dev_bsize);
+					dumpabort(0);
+				}
+#endif
+				memcpy(buf, CDATA(i) +
+				    (blkno - curr->blkstart) * dev_bsize,
+			   	    tocopy);
+
+				buf 	+= tocopy;
+				size 	-= tocopy;
+				blkno 	+= (tocopy + dev_bsize - 1) / dev_bsize;
+				numblocks -=
+				    (tocopy  + dev_bsize - 1) / dev_bsize;
+
+				curr->time = cheader->count++;
+
+				/*
+				 * If all data of a cache block have been
+				 * read, chances are good no more reads
+				 * will occur, so expire the cache immediately
+				 */
+
+				curr->blocksRead +=
+				    (tocopy + dev_bsize -1) / dev_bsize;
+				if (curr->blocksRead >= nblksread)
+					curr->time = 0;
+
+				goto retry;
+			}
+		}
+
+		/* No more to do? */
+		if (size == 0)
+			break;
+			
+		/*
+		 * This does actually not happen if fs blocks are not greater
+		 * than nblksread.
+		 */
+		if (numblocks > nblksread) {
+			rawread(oblkno, obuf, osize);
+			break;
+		} else {
+			int     idx;
+			ssize_t rsize;
+			daddr_t blockblkno;
+
+			blockblkno = (blkno / nblksread) * nblksread;
+			idx = findlru();
+			rsize = min(nblksread,
+			    fsbtodb(sblock, sblock->fs_size) - blockblkno) *
+			    dev_bsize;
+
+#ifdef DIAGNOSTICS
+			if (cdesc[idx].owner)
+				fprintf(stderr, "Owner is set (%d, me=%d), can"
+				    "not happen(2).\n", cdesc[idx].owner,
+				    getpid());
+			cdesc[idx].owner = getpid();
+#endif
+			cdesc[idx].time = cheader->count++;
+			cdesc[idx].blkstart = blockblkno;
+			cdesc[idx].blocksRead = 0;
+
+			if (lseek(diskfd,
+			    ((off_t) (blockblkno) << dev_bshift), 0) < 0) {
+				msg("readBlocks: lseek fails: %s\n",
+				    strerror(errno));
+				rsize = -1;
+			} else {
+				rsize = read(diskfd, CDATA(idx), rsize);
+				if (rsize < 0) {
+					msg("readBlocks: read fails: %s\n",
+					    strerror(errno));
+				}
+			}
+
+			/* On errors, panic, punt, try to read without
+			 * cache and let raw read routine do the rest.
+			 */
+
+			if (rsize <= 0) {
+				rawread(oblkno, obuf, osize);
+#ifdef DIAGNOSTICS
+				if (cdesc[idx].owner != getpid())
+					fprintf(stderr, "Owner changed from "
+					    "%d to %d, can't happen\n",
+					    getpid(), cdesc[idx].owner);
+				cdesc[idx].owner = 0;
+#endif
+				break;
+			}
+
+			/* On short read, just note the fact and go on */
+			cdesc[idx].blkend = blockblkno + rsize / dev_bsize;
+
+#ifdef STATS
+			nphysread++;
+			physreadsize += rsize;
+#endif
+#ifdef DIAGNOSTICS
+			if (cdesc[idx].owner != getpid())
+				fprintf(stderr, "Owner changed from "
+				    "%d to %d, can't happen\n",
+				    getpid(), cdesc[idx].owner);
+			cdesc[idx].owner = 0;
+#endif
+			/*
+			 * We swapped some of data in, let the loop fetch
+			 * them from cache
+			 */
+		}
+	}
+	
+	if (flock(diskfd, LOCK_UN))
+		msg("flock(LOCK_UN) failed: %s\n",
+		    strerror(errno));
+	return;
+}
+
+/*-----------------------------------------------------------------------*/
+void
+printcachestats()
+{
+#ifdef STATS
+	fprintf(stderr, "Pid %d: %d reads (%u bytes) "
+	    "%d physical reads (%u bytes) %d%% hits, %d%% overhead\n",
+	    getpid(), nreads, (u_int) readsize, nphysread,
+	    (u_int) physreadsize, (nreads - nphysread) * 100 / nreads,
+	    (int) (((physreadsize - readsize) * 100) / readsize));
+#endif
+}
+
+/*-----------------------------------------------------------------------*/
Index: tape.c
===================================================================
RCS file: /cvsroot/src/sbin/dump/tape.c,v
retrieving revision 1.17
diff -u -r1.17 tape.c
--- tape.c	1998/07/18 05:04:36	1.17
+++ tape.c	1999/03/19 11:12:36
@@ -866,7 +866,7 @@
 				wrote = write(tapefd, slp->tblock[0]+size,
 				    writesize-size);
 #ifdef WRITEDEBUG
-			printf("slave %d wrote %d\n", slave_number, wrote);
+			fprintf(stderr, "slave %d wrote %d\n", slave_number, wrote);
 #endif
 			if (wrote < 0) 
 				break;
@@ -877,7 +877,7 @@
 
 #ifdef WRITEDEBUG
 		if (size != writesize) 
-		 printf("slave %d only wrote %d out of %d bytes and gave up.\n",
+		 fprintf(stderr, "slave %d only wrote %d out of %d bytes and gave up.\n",
 		     slave_number, size, writesize);
 #endif
 
@@ -907,6 +907,7 @@
 		 */
 		(void) kill(nextslave, SIGUSR2);
 	}
+	printcachestats();
 	if (nread != 0)
 		quit("error reading command pipe: %s\n", strerror(errno));
 }
Index: traverse.c
===================================================================
RCS file: /cvsroot/src/sbin/dump/traverse.c,v
retrieving revision 1.23
diff -u -r1.23 traverse.c
--- traverse.c	1999/03/09 17:25:52	1.23
+++ traverse.c	1999/03/19 11:12:36
@@ -696,76 +696,3 @@
 	maxino = minino + INOPB(sblock);
 	return (&inoblock[inum - minino]);
 }
-
-/*
- * Read a chunk of data from the disk.
- * Try to recover from hard errors by reading in sector sized pieces.
- * Error recovery is attempted at most BREADEMAX times before seeking
- * consent from the operator to continue.
- */
-int	breaderrors = 0;		
-#define	BREADEMAX 32
-
-void
-bread(blkno, buf, size)
-	daddr_t blkno;
-	char *buf;
-	int size;	
-{
-	int cnt, i;
-	extern int errno;
-
-loop:
-	if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0)
-		msg("bread: lseek fails\n");
-	if ((cnt = read(diskfd, buf, size)) == size)
-		return;
-	if (blkno + (size / dev_bsize) > fsbtodb(sblock, sblock->fs_size)) {
-		/*
-		 * Trying to read the final fragment.
-		 *
-		 * NB - dump only works in TP_BSIZE blocks, hence
-		 * rounds `dev_bsize' fragments up to TP_BSIZE pieces.
-		 * It should be smarter about not actually trying to
-		 * read more than it can get, but for the time being
-		 * we punt and scale back the read only when it gets
-		 * us into trouble. (mkm 9/25/83)
-		 */
-		size -= dev_bsize;
-		goto loop;
-	}
-	if (cnt == -1)
-		msg("read error from %s: %s: [block %d]: count=%d\n",
-			disk, strerror(errno), blkno, size);
-	else
-		msg("short read error from %s: [block %d]: count=%d, got=%d\n",
-			disk, blkno, size, cnt);
-	if (++breaderrors > BREADEMAX) {
-		msg("More than %d block read errors from %d\n",
-			BREADEMAX, disk);
-		broadcast("DUMP IS AILING!\n");
-		msg("This is an unrecoverable error.\n");
-		if (!query("Do you want to attempt to continue?")){
-			dumpabort(0);
-			/*NOTREACHED*/
-		} else
-			breaderrors = 0;
-	}
-	/*
-	 * Zero buffer, then try to read each sector of buffer separately.
-	 */
-	memset(buf, 0, size);
-	for (i = 0; i < size; i += dev_bsize, buf += dev_bsize, blkno++) {
-		if (lseek(diskfd, ((off_t)blkno << dev_bshift), 0) < 0)
-			msg("bread: lseek2 fails!\n");
-		if ((cnt = read(diskfd, buf, (int)dev_bsize)) == dev_bsize)
-			continue;
-		if (cnt == -1) {
-			msg("read error from %s: %s: [sector %d]: count=%d\n",
-				disk, strerror(errno), blkno, dev_bsize);
-			continue;
-		}
-		msg("short read error from %s: [sector %d]: count=%d, got=%d\n",
-			disk, blkno, dev_bsize, cnt);
-	}
-}

--WIyZ46R2i8wDzkSu--