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
35typedef 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
44static char  *DataBase;
45static Block **BlockHash;
46static int   BlockSize;
47static int   HSize;
48static int   NBlocks;
49
50static void
51cinit(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
78ssize_t
79cread(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