xref: /freebsd/sbin/dump/cache.c (revision 2be1a816b9ff69588e55be0a84cbe2a31efc0f2f)
1 /*
2  * CACHE.C
3  *
4  *	Block cache for dump
5  *
6  * $FreeBSD$
7  */
8 
9 #include <sys/param.h>
10 #include <sys/stat.h>
11 #include <sys/mman.h>
12 
13 #ifdef sunos
14 #include <sys/vnode.h>
15 
16 #include <ufs/fs.h>
17 #include <ufs/fsdir.h>
18 #include <ufs/inode.h>
19 #else
20 #include <ufs/ufs/dir.h>
21 #include <ufs/ufs/dinode.h>
22 #include <ufs/ffs/fs.h>
23 #endif
24 
25 #include <protocols/dumprestore.h>
26 
27 #include <ctype.h>
28 #include <stdio.h>
29 #ifdef __STDC__
30 #include <errno.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #endif
35 #include "dump.h"
36 
37 typedef struct Block {
38 	struct Block	*b_HNext;	/* must be first field */
39 	off_t		b_Offset;
40 	char		*b_Data;
41 } Block;
42 
43 #define HFACTOR		4
44 #define BLKFACTOR	4
45 
46 static char  *DataBase;
47 static Block **BlockHash;
48 static int   BlockSize;
49 static int   HSize;
50 static int   NBlocks;
51 
52 static void
53 cinit(void)
54 {
55 	int i;
56 	int hi;
57 	Block *base;
58 
59 	if ((BlockSize = sblock->fs_bsize * BLKFACTOR) > MAXBSIZE)
60 		BlockSize = MAXBSIZE;
61 	NBlocks = cachesize / BlockSize;
62 	HSize = NBlocks / HFACTOR;
63 
64 	msg("Cache %d MB, blocksize = %d\n",
65 	    NBlocks * BlockSize / (1024 * 1024), BlockSize);
66 
67 	base = calloc(sizeof(Block), NBlocks);
68 	BlockHash = calloc(sizeof(Block *), HSize);
69 	DataBase = mmap(NULL, NBlocks * BlockSize,
70 			PROT_READ|PROT_WRITE, MAP_ANON, -1, 0);
71 	for (i = 0; i < NBlocks; ++i) {
72 		base[i].b_Data = DataBase + i * BlockSize;
73 		base[i].b_Offset = (off_t)-1;
74 		hi = i / HFACTOR;
75 		base[i].b_HNext = BlockHash[hi];
76 		BlockHash[hi] = &base[i];
77 	}
78 }
79 
80 ssize_t
81 cread(int fd, void *buf, size_t nbytes, off_t offset)
82 {
83 	Block *blk;
84 	Block **pblk;
85 	Block **ppblk;
86 	int hi;
87 	int n;
88 	off_t mask;
89 
90 	/*
91 	 * If the cache is disabled, or we do not yet know the filesystem
92 	 * block size, then revert to pread.  Otherwise initialize the
93 	 * cache as necessary and continue.
94 	 */
95 	if (cachesize <= 0 || sblock->fs_bsize == 0)
96 		return(pread(fd, buf, nbytes, offset));
97 	if (DataBase == NULL)
98 		cinit();
99 
100 	/*
101 	 * If the request crosses a cache block boundary, or the
102 	 * request is larger or equal to the cache block size,
103 	 * revert to pread().  Full-block-reads are typically
104 	 * one-time calls and caching would be detrimental.
105 	 */
106 	mask = ~(off_t)(BlockSize - 1);
107 	if (nbytes >= BlockSize ||
108 	    ((offset ^ (offset + nbytes - 1)) & mask) != 0) {
109 		return(pread(fd, buf, nbytes, offset));
110 	}
111 
112 	/*
113 	 * Obtain and access the cache block.  Cache a successful
114 	 * result.  If an error occurs, revert to pread() (this might
115 	 * occur near the end of the media).
116 	 */
117 	hi = (offset / BlockSize) % HSize;
118 	pblk = &BlockHash[hi];
119 	ppblk = NULL;
120 	while ((blk = *pblk) != NULL) {
121 		if (((blk->b_Offset ^ offset) & mask) == 0)
122 			break;
123 		ppblk = pblk;
124 		pblk = &blk->b_HNext;
125 	}
126 	if (blk == NULL) {
127 		blk = *ppblk;
128 		pblk = ppblk;
129 		blk->b_Offset = offset & mask;
130 		n = pread(fd, blk->b_Data, BlockSize, blk->b_Offset);
131 		if (n != BlockSize) {
132 			blk->b_Offset = (off_t)-1;
133 			blk = NULL;
134 		}
135 	}
136 	if (blk) {
137 		bcopy(blk->b_Data + (offset - blk->b_Offset), buf, nbytes);
138 		*pblk = blk->b_HNext;
139 		blk->b_HNext = BlockHash[hi];
140 		BlockHash[hi] = blk;
141 		return(nbytes);
142 	} else {
143 		return(pread(fd, buf, nbytes, offset));
144 	}
145 }
146 
147