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