1 | /* Second level block cache to supplement the file system cache. The block
|
---|
2 | * cache of a 16-bit Minix system is very small, too small to prevent trashing.
|
---|
3 | * A generic 32-bit system also doesn't have a very large cache to allow it
|
---|
4 | * to run on systems with little memory. On a system with lots of memory one
|
---|
5 | * can use the RAM disk as a read-only second level cache. Any blocks pushed
|
---|
6 | * out of the primary cache are cached on the RAM disk. This code manages the
|
---|
7 | * second level cache. The cache is a simple FIFO where old blocks are put
|
---|
8 | * into and drop out at the other end. Must be searched backwards.
|
---|
9 | *
|
---|
10 | * The entry points into this file are:
|
---|
11 | * init_cache2: initialize the second level cache
|
---|
12 | * get_block2: get a block from the 2nd level cache
|
---|
13 | * put_block2: store a block in the 2nd level cache
|
---|
14 | * invalidate2: remove all the cache blocks on some device
|
---|
15 | */
|
---|
16 |
|
---|
17 | #include "fs.h"
|
---|
18 | #include <minix/com.h>
|
---|
19 | #include "buf.h"
|
---|
20 |
|
---|
21 | #if ENABLE_CACHE2
|
---|
22 |
|
---|
23 | #define MAX_BUF2 (256 * sizeof(char *))
|
---|
24 |
|
---|
25 | PRIVATE struct buf2 { /* 2nd level cache per block administration */
|
---|
26 | block_t b2_blocknr; /* block number */
|
---|
27 | dev_t b2_dev; /* device number */
|
---|
28 | u16_t b2_count; /* count of in-cache block groups */
|
---|
29 | } buf2[MAX_BUF2];
|
---|
30 |
|
---|
31 | PRIVATE unsigned nr_buf2; /* actual cache size */
|
---|
32 | PRIVATE unsigned buf2_idx; /* round-robin reuse index */
|
---|
33 |
|
---|
34 | #define hash2(block) ((unsigned) ((block) & (MAX_BUF2 - 1)))
|
---|
35 |
|
---|
36 | /*===========================================================================*
|
---|
37 | * init_cache2 *
|
---|
38 | *===========================================================================*/
|
---|
39 | PUBLIC void init_cache2(size)
|
---|
40 | unsigned long size;
|
---|
41 | {
|
---|
42 | /* Initialize the second level disk buffer cache of 'size' blocks. */
|
---|
43 |
|
---|
44 | nr_buf2 = size > MAX_BUF2 ? MAX_BUF2 : (unsigned) size;
|
---|
45 | }
|
---|
46 |
|
---|
47 | /*===========================================================================*
|
---|
48 | * get_block2 *
|
---|
49 | *===========================================================================*/
|
---|
50 | PUBLIC int get_block2(bp, only_search)
|
---|
51 | struct buf *bp; /* buffer to get from the 2nd level cache */
|
---|
52 | int only_search; /* if NO_READ, do nothing, else act normal */
|
---|
53 | {
|
---|
54 | /* Fill a buffer from the 2nd level cache. Return true iff block acquired. */
|
---|
55 | unsigned b;
|
---|
56 | struct buf2 *bp2;
|
---|
57 |
|
---|
58 | /* If the block wanted is in the RAM disk then our game is over. */
|
---|
59 | if (bp->b_dev == DEV_RAM) nr_buf2 = 0;
|
---|
60 |
|
---|
61 | /* Cache enabled? NO_READ? Any blocks with the same hash key? */
|
---|
62 | if (nr_buf2 == 0 || only_search == NO_READ
|
---|
63 | || buf2[hash2(bp->b_blocknr)].b2_count == 0) return(0);
|
---|
64 |
|
---|
65 | /* Search backwards (there may be older versions). */
|
---|
66 | b = buf2_idx;
|
---|
67 | for (;;) {
|
---|
68 | if (b == 0) b = nr_buf2;
|
---|
69 | bp2 = &buf2[--b];
|
---|
70 | if (bp2->b2_blocknr == bp->b_blocknr && bp2->b2_dev == bp->b_dev) break;
|
---|
71 | if (b == buf2_idx) return(0);
|
---|
72 | }
|
---|
73 |
|
---|
74 | /* Block is in the cache, get it. */
|
---|
75 | if (dev_io(DEV_READ, DEV_RAM, FS_PROC_NR, bp->b_data,
|
---|
76 | (off_t) b * BLOCK_SIZE, BLOCK_SIZE, 0) == BLOCK_SIZE) {
|
---|
77 | return(1);
|
---|
78 | }
|
---|
79 | return(0);
|
---|
80 | }
|
---|
81 |
|
---|
82 | /*===========================================================================*
|
---|
83 | * put_block2 *
|
---|
84 | *===========================================================================*/
|
---|
85 | PUBLIC void put_block2(bp)
|
---|
86 | struct buf *bp; /* buffer to store in the 2nd level cache */
|
---|
87 | {
|
---|
88 | /* Store a buffer into the 2nd level cache. */
|
---|
89 | unsigned b;
|
---|
90 | struct buf2 *bp2;
|
---|
91 |
|
---|
92 | if (nr_buf2 == 0) return; /* no 2nd level cache */
|
---|
93 |
|
---|
94 | b = buf2_idx++;
|
---|
95 | if (buf2_idx == nr_buf2) buf2_idx = 0;
|
---|
96 |
|
---|
97 | bp2 = &buf2[b];
|
---|
98 |
|
---|
99 | if (dev_io(DEV_WRITE, DEV_RAM, FS_PROC_NR, bp->b_data,
|
---|
100 | (off_t) b * BLOCK_SIZE, BLOCK_SIZE, 0) == BLOCK_SIZE) {
|
---|
101 | if (bp2->b2_dev != NO_DEV) buf2[hash2(bp2->b2_blocknr)].b2_count--;
|
---|
102 | bp2->b2_dev = bp->b_dev;
|
---|
103 | bp2->b2_blocknr = bp->b_blocknr;
|
---|
104 | buf2[hash2(bp2->b2_blocknr)].b2_count++;
|
---|
105 | }
|
---|
106 | }
|
---|
107 |
|
---|
108 | /*===========================================================================*
|
---|
109 | * invalidate2 *
|
---|
110 | *===========================================================================*/
|
---|
111 | PUBLIC void invalidate2(device)
|
---|
112 | dev_t device;
|
---|
113 | {
|
---|
114 | /* Invalidate all blocks from a given device in the 2nd level cache. */
|
---|
115 | unsigned b;
|
---|
116 | struct buf2 *bp2;
|
---|
117 |
|
---|
118 | for (b = 0; b < nr_buf2; b++) {
|
---|
119 | bp2 = &buf2[b];
|
---|
120 | if (bp2->b2_dev == device) {
|
---|
121 | bp2->b2_dev = NO_DEV;
|
---|
122 | buf2[hash2(bp2->b2_blocknr)].b2_count--;
|
---|
123 | }
|
---|
124 | }
|
---|
125 | }
|
---|
126 | #endif /* ENABLE_CACHE2 */
|
---|