File: diskmap.c

package info (click to toggle)
jfsutils 1.1.11-1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 2,704 kB
  • ctags: 3,610
  • sloc: ansic: 35,019; sh: 787; makefile: 78
file content (314 lines) | stat: -rw-r--r-- 8,865 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
/*
 *   Copyright (c) International Business Machines Corp., 2000-2002
 *
 *   This program is free software;  you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
#include <config.h>
#include "jfs_types.h"
#include "jfs_dmap.h"
#include "diskmap.h"

/*
 * budtab[]
 *
 * used to determine the maximum free string in a character
 * of a wmap word.  the actual bit values of the character
 * serve as the index into this array and the value of the
 * array at that index is the max free string.
 *
 */
static int8_t budtab[256] = {
	3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
	2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
	2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
	2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
	2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
	2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
	2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
	2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
	2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
	2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1
};

/*
 * NAME: ujfs_maxbuddy
 *
 * FUNCTION: Determines the maximum string of free blocks within a word of the
 *	wmap or pmap consistent with binary buddy.
 *
 * PRE CONDITIONS:
 *
 * POST CONDITIONS:
 *
 * PARAMETERS:
 *	cp	- Pointer to wmap or pmap word.
 *
 * NOTES:
 *
 * DATA STRUCTURES:
 *
 * RETURNS: Maximum string of free blocks within word.
 */
int8_t ujfs_maxbuddy(unsigned char *cp)
{
	/*
	 * Check if the wmap or pmap word is all free. If so, the free buddy size is
	 * BUDMIN.
	 */
	if (*((uint32_t *) cp) == 0) {
		return (BUDMIN);
	}

	/*
	 * Check if the wmap or pmap word is half free. If so, the free buddy size
	 * is BUDMIN-1.
	 */
	if (*((uint16_t *) cp) == 0 || *((uint16_t *) cp + 1) == 0) {
		return (BUDMIN - 1);
	}

	/*
	 * Not all free or half free. Determine the free buddy size through table
	 * lookup using quarters of the wmap or pmap word.
	 */
	return (MAX(MAX(budtab[*cp], budtab[*(cp + 1)]),
		    MAX(budtab[*(cp + 2)], budtab[*(cp + 3)])));
}

/*
 * NAME: ujfs_adjtree
 *
 * FUNCTION: Calculate the tree of a dmap or dmapctl.
 *
 * PRE CONDITIONS:
 *
 * POST CONDITIONS:
 *
 * PARAMETERS:
 *	cp	- Pointer to the top of the tree
 *	l2leaves- Number of leaf nodes as a power of 2
 *	l2min	- Number of disk blocks actually covered by a leaf of the tree;
 *		  specified as a power of 2
 *
 * NOTES: This routine first works against the leaves of the tree to calculate
 *	the maximum free string for leaf buddys.  Once this is accomplished the
 *	values of the leaf nodes are bubbled up the tree.
 *
 * DATA STRUCTURES:
 *
 * RETURNS:
 */
int8_t ujfs_adjtree(int8_t * treep, int32_t l2leaves, int32_t l2min)
{
	int32_t nleaves, leaf_index, l2max, nextb, bsize, index;
	int32_t l2free, leaf, num_this_level, nextp;
	int8_t *cp0, *cp1, *cp = treep;

	/*
	 * Determine the number of leaves of the tree and the
	 * index of the first leaf.
	 * Note: I don't know why the leaf_index calculation works, but it does.
	 */
	nleaves = (1 << l2leaves);
	leaf_index = (nleaves - 1) / 3;

	/*
	 * Determine the maximum free string possible for the leaves.
	 */
	l2max = l2min + l2leaves;

	/*
	 * Try to combine buddies starting with a buddy size of 1 (i.e. two leaves).
	 * At a buddy size of 1 two buddy leaves can be combined if both buddies
	 * have a maximum free of l2min; the combination will result in the
	 * left-most buddy leaf having a maximum free of l2min+1.  After processing
	 * all buddies for a certain size, process buddies at the next higher buddy
	 * size (i.e. current size * 2) and the next maximum free
	 * (current free + 1).  This continues until the maximum possible buddy
	 * combination yields maximum free.
	 */
	for (l2free = l2min, bsize = 1; l2free < l2max; l2free++, bsize = nextb) {
		nextb = bsize << 1;

		for (cp0 = cp + leaf_index, index = 0; index < nleaves;
		     index += nextb, cp0 += nextb) {
			if (*cp0 == l2free && *(cp0 + bsize) == l2free) {
				*cp0 = l2free + 1;
				*(cp0 + bsize) = -1;
			}
		}
	}

	/*
	 * With the leaves reflecting maximum free values bubble this information up
	 * the tree.  Starting at the leaf node level, the four nodes described by
	 * the higher level parent node are compared for a maximum free and this
	 * maximum becomes the value of the parent node.  All lower level nodes are
	 * processed in this fashion then we move up to the next level (parent
	 * becomes a lower level node) and continue the process for that level.
	 */
	for (leaf = leaf_index, num_this_level = nleaves >> 2; num_this_level > 0;
	     num_this_level >>= 2, leaf = nextp) {
		nextp = (leaf - 1) >> 2;

		/*
		 * Process all lower level nodes at this level setting the value of the
		 * parent node as the maximum of the four lower level nodes.
		 */
		for (cp0 = cp + leaf, cp1 = cp + nextp, index = 0;
		     index < num_this_level; index++, cp0 += 4, cp1++) {
			*cp1 = TREEMAX(cp0);
		}
	}

	return (*cp);
}

/*
 * NAME: ujfs_complete_dmap
 *
 * FUNCTION: Fill in rest of dmap fields from wmap/pmap already initialized.
 *
 * PARAMETERS:
 *	dmap_page	- dmap to complete
 *	blkno		- starting block number for this dmap
 *	treemax		- will be filled in with max free for this dmap
 *
 * RETURNS: NONE
 */
void ujfs_complete_dmap(struct dmap *dmap_page, int64_t blkno, int8_t *treemax)
{
	struct dmaptree *tp;
	int8_t *cp;
	int32_t index;

	dmap_page->start = blkno;

	tp = &dmap_page->tree;
	tp->height = 4;
	tp->leafidx = LEAFIND;
	tp->nleafs = LPERDMAP;
	tp->l2nleafs = L2LPERDMAP;
	tp->budmin = BUDMIN;

	/*
	 * Pick up the pointer to the first leaf of the dmap tree.
	 */
	cp = tp->stree + tp->leafidx;

	/*
	 * Set the initial state for the leaves of the dmap tree.  They will reflect
	 * the current allocation state of the wmap words.
	 */
	for (index = 0; index < LPERDMAP; index++) {
		*(cp + index) = ujfs_maxbuddy((unsigned char *) &dmap_page->wmap[index]);
	}

	/*
	 * With the leaves of the dmap initialized adjust (initialize) the dmap's
	 * tree.
	 */
	*treemax = ujfs_adjtree(tp->stree, L2LPERDMAP, BUDMIN);
}

/*
 * NAME: ujfs_idmap_page
 *
 * FUNCTION: Initialize one dmap page
 *
 * POST CONDITIONS: Blocks which don't actually exist in the aggregate will be
 *	marked as allocated in the dmap page.  The total number of blocks will
 *	only account for the actually existing blocks.
 *
 * PARAMETERS:
 *	map_page	- pointer to page of map
 *	nblocks		- number of blocks this page
 *
 * RETURNS: NONE
 */
void ujfs_idmap_page(struct dmap *map_page, uint32_t nblocks)
{
	uint32_t index, nwords, bit;

	map_page->nblocks = map_page->nfree = nblocks;

	/*
	 * Partial dmap page?
	 * If there are not enough blocks to cover an entire dmap page the ones
	 * which represent blocks which don't exist will be marked as allocated.
	 *
	 * nwords will indicate the first word beyond the end of existing blocks
	 * bit will indicate if this block does not fall on a 32-bit boundary
	 */
	nwords = nblocks / DBWORD;
	bit = nblocks % DBWORD;

	if (bit) {
		/*
		 * Need to mark a partial word allocated
		 */
		map_page->wmap[nwords] = map_page->pmap[nwords] = ONES >> bit;
		nwords++;
	}

	/*
	 * Set the rest of the words in the page to ONES.
	 */
	for (index = nwords; index < LPERDMAP; index++) {
		map_page->pmap[index] = map_page->wmap[index] = ONES;
	}
}

/*
 * NAME: ujfs_getagl2size
 *
 * FUNCTION: Determine log2(allocation group size) based on size of aggregate
 *
 * PARAMETERS:
 *	size		- Number of blocks in aggregate
 *	aggr_block_size	- Aggregate block size
 *
 * RETURNS: log2(allocation group size) in aggregate blocks
 */
int32_t ujfs_getagl2size(int64_t size, int32_t aggr_block_size)
{
	int64_t sz;
	int64_t m;
	int32_t l2sz;

	if (size < BPERDMAP * MAXAG) {
		return (L2BPERDMAP);
	}

	m = ((uint64_t) 1 << (64 - 1));
	for (l2sz = 64; l2sz >= 0; l2sz--, m >>= 1) {
		if (m & size) {
			break;
		}
	}

	sz = (int64_t) 1 << l2sz;
	if (sz < size) {
		l2sz += 1;
	}

	return (l2sz - L2MAXAG);
}