cache.c revision 109187
1/*
2 * CACHE.C
3 *
4 *	Block cache for dump
5 *
6 * $FreeBSD: head/sbin/dump/cache.c 109187 2003-01-13 19:42:41Z dillon $
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
37typedef 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
46static char  *DataBase;
47static Block **BlockHash;
48static int   BlockSize;
49static int   HSize;
50static int   NBlocks;
51
52static void
53cinit(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
80ssize_t
81cread(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, revert to pread.  If the
92	 * cache has not been initialized, initialize the cache.
93	 */
94	if (sblock->fs_bsize && DataBase == NULL) {
95		if (cachesize <= 0)
96			return(pread(fd, buf, nbytes, offset));
97		cinit();
98	}
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#if 0
123			fprintf(stderr, "%08llx %d (%08x)\n", offset, nbytes,
124			    sblock->fs_size * sblock->fs_fsize);
125#endif
126			break;
127		}
128		ppblk = pblk;
129		pblk = &blk->b_HNext;
130	}
131	if (blk == NULL) {
132		blk = *ppblk;
133		pblk = ppblk;
134		blk->b_Offset = offset & mask;
135		n = pread(fd, blk->b_Data, BlockSize, blk->b_Offset);
136		if (n != BlockSize) {
137			blk->b_Offset = (off_t)-1;
138			blk = NULL;
139		}
140	}
141	if (blk) {
142		bcopy(blk->b_Data + (offset - blk->b_Offset), buf, nbytes);
143		*pblk = blk->b_HNext;
144		blk->b_HNext = BlockHash[hi];
145		BlockHash[hi] = blk;
146		return(nbytes);
147	} else {
148		return(pread(fd, buf, nbytes, offset));
149	}
150}
151
152