xref: /titanic_44/usr/src/cmd/fs.d/cachefs/cfsd/cfsd_maptbl.c (revision d2ec54f7875f7e05edd56195adbeb593c947763f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1994-2002 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Methods of the cfsd_maptbl classes.
31  */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stddef.h>
36 #include <string.h>
37 #include <synch.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <errno.h>
41 #include <sys/utsname.h>
42 #include <sys/vfs.h>
43 #include <sys/cred.h>
44 #include <sys/param.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/mman.h>
48 #include <sys/fs/cachefs_fs.h>
49 #include <sys/fs/cachefs_dlog.h>
50 #include <mdbug/mdbug.h>
51 #include "cfsd.h"
52 #include "cfsd_maptbl.h"
53 
54 /*
55  *			cfsd_maptbl_create
56  *
57  * Description:
58  *	Constructor for the cfsd_maptbl class.
59  *	Just does some setup not much else.
60  * Arguments:
61  * Returns:
62  * Preconditions:
63  */
64 cfsd_maptbl_object_t *
65 cfsd_maptbl_create(void)
66 {
67 	cfsd_maptbl_object_t *maptbl_object_p;
68 
69 	dbug_enter("cfsd_maptbl_create");
70 
71 	maptbl_object_p = cfsd_calloc(sizeof (cfsd_maptbl_object_t));
72 
73 	maptbl_object_p->i_fid = -1;
74 	maptbl_object_p->i_pa = NULL;
75 	maptbl_object_p->i_paoff = 0;
76 	maptbl_object_p->i_paend = 0;
77 	maptbl_object_p->i_palen = 0;
78 	dbug_leave("cfsd_maptbl_create");
79 	return (maptbl_object_p);
80 }
81 
82 /*
83  *			cfsd_maptbl_destroy
84  *
85  * Description:
86  *	Destructor for the cfsd_maptbl class.
87  * Arguments:
88  * Returns:
89  * Preconditions:
90  */
91 void
92 cfsd_maptbl_destroy(cfsd_maptbl_object_t *maptbl_object_p)
93 {
94 	dbug_enter("cfsd_maptbl_destroy");
95 	dbug_precond(maptbl_object_p);
96 	maptbl_teardown(maptbl_object_p);
97 	cfsd_free(maptbl_object_p);
98 	dbug_leave("cfsd_maptbl_destroy");
99 }
100 
101 /*
102  *			maptbl_domap
103  *
104  * Description:
105  *	Maps in the specified section of the file.
106  * Arguments:
107  *	off	The offset to map in.  Must be i_pagesize aligned.
108  * Returns:
109  *	Returns 0 for success or an errno value on failure.
110  * Preconditions:
111  */
112 int
113 maptbl_domap(cfsd_maptbl_object_t *maptbl_object_p, off_t off)
114 {
115 	int xx;
116 	int len;
117 
118 	dbug_enter("maptbl_domap");
119 	dbug_precond(maptbl_object_p);
120 	dbug_precond(maptbl_object_p->i_fid >= 0);
121 
122 	len = maptbl_object_p->i_maplen;
123 
124 	maptbl_object_p->i_stat_mapmove++;
125 
126 	/* destroy old mapping if it exists */
127 	if (maptbl_object_p->i_pa) {
128 		/* determine how far we have to move the map */
129 		maptbl_object_p->i_stat_mapdist +=
130 		    abs(maptbl_object_p->i_paoff - off);
131 
132 		/* remove the map */
133 		xx = munmap(maptbl_object_p->i_pa, maptbl_object_p->i_palen);
134 		if (xx == -1) {
135 			xx = errno;
136 			dbug_print(("error", "Could not unmap %s, %d, %p, %d",
137 			    maptbl_object_p->i_name, xx, maptbl_object_p->i_pa,
138 			    maptbl_object_p->i_palen));
139 		}
140 		maptbl_object_p->i_pa = NULL;
141 		maptbl_object_p->i_palen = 0;
142 		maptbl_object_p->i_paoff = 0;
143 		maptbl_object_p->i_paend = 0;
144 	}
145 
146 	/* do the mapping */
147 	maptbl_object_p->i_pa =
148 	    mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED,
149 	    maptbl_object_p->i_fid, off);
150 	if (maptbl_object_p->i_pa == MAP_FAILED) {
151 		xx = errno;
152 		dbug_print(("error",
153 		    "Could not map %s, error %d, off %d, len %d",
154 		    maptbl_object_p->i_name, xx, off, len));
155 		maptbl_object_p->i_pa = NULL;
156 		dbug_leave("maptbl_domap");
157 		return (xx);
158 	}
159 
160 	maptbl_object_p->i_palen = len;
161 	maptbl_object_p->i_paoff = off;
162 	maptbl_object_p->i_paend = off + len - 1;
163 	dbug_leave("maptbl_domap");
164 	return (0);
165 }
166 
167 /*
168  *			maptbl_getaddr
169  *
170  * Description:
171  *	Returns an address of a particular entry in the file.
172  * Arguments:
173  *	index
174  * Returns:
175  *	Returns NULL for a failure with the mapping file.
176  * Preconditions:
177  */
178 caddr_t
179 maptbl_getaddr(cfsd_maptbl_object_t *maptbl_object_p, int index)
180 {
181 	off_t start;
182 	off_t end;
183 	caddr_t pa;
184 
185 	dbug_enter("maptbl_getaddr");
186 	dbug_precond(maptbl_object_p);
187 	dbug_precond(index < maptbl_object_p->i_entries);
188 
189 	/* find the boundaries of the entry */
190 	start = index * sizeof (struct cfs_dlog_mapping_space);
191 	end = start + sizeof (struct cfs_dlog_mapping_space) - 1;
192 
193 	/* map the entry in if necessary */
194 	if ((start < maptbl_object_p->i_paoff) ||
195 		(maptbl_object_p->i_paend < end)) {
196 		if (maptbl_domap(maptbl_object_p,
197 		    start & maptbl_object_p->i_pagemask)) {
198 			dbug_leave("maptbl_getaddr");
199 			return (NULL);
200 		}
201 	}
202 
203 	/* make an address and return it */
204 	pa = maptbl_object_p->i_pa + (start - maptbl_object_p->i_paoff);
205 	dbug_leave("maptbl_getaddr");
206 	return (pa);
207 }
208 
209 /*
210  *			maptbl_cidhashaddr
211  *
212  * Description:
213  *	Finds the address of the specified cid by hashing to
214  *	the appropriate entry.  If the cid does not already
215  *	exist in the file, then the address of where it should
216  *	reside is returned.
217  * Arguments:
218  *	cid
219  *	addrp
220  * Returns:
221  *	Returns 0 for success, 1 if entry not found, -1 if an
222  *	error occurs in the mapping file.
223  * Preconditions:
224  */
225 int
226 maptbl_cidhashaddr(cfsd_maptbl_object_t *maptbl_object_p,
227 	cfs_cid_t cid,
228 	caddr_t *addrp)
229 {
230 	ino64_t *pa;
231 	int index;
232 	ino64_t fileno;
233 	int start_index;
234 
235 	dbug_enter("maptbl_cidhashaddr");
236 	dbug_precond(maptbl_object_p);
237 	dbug_precond(addrp);
238 
239 	maptbl_object_p->i_stat_requests++;
240 
241 	/* get the index from the first hash function */
242 	index = maptbl_hash1(maptbl_object_p, cid);
243 
244 	maptbl_object_p->i_stat_probes++;
245 
246 	/* get the address of the entry */
247 	pa = (ino64_t *)maptbl_getaddr(maptbl_object_p, index);
248 	if (pa == NULL) {
249 		dbug_leave("maptbl_cidhashaddr");
250 		return (-1);
251 	}
252 	fileno = *pa;
253 
254 	/* check for match */
255 	if (fileno == cid.cid_fileno) {
256 		*addrp = (caddr_t)pa;
257 		dbug_leave("maptbl_cidhashaddr");
258 		return (0);
259 	}
260 
261 	/* check for not found */
262 	if (fileno == 0) {
263 		*addrp = (caddr_t)pa;
264 		dbug_leave("maptbl_cidhashaddr");
265 		return (1);
266 	}
267 
268 	/* get the index from the second hash function */
269 	index = maptbl_hash2(maptbl_object_p, cid, index);
270 
271 	/* do a linear search for a match or empty entry */
272 	start_index = index;
273 	do {
274 		maptbl_object_p->i_stat_probes++;
275 
276 		/* get the address of the entry */
277 		pa = (ino64_t *)maptbl_getaddr(maptbl_object_p, index);
278 		if (pa == NULL) {
279 			dbug_leave("maptbl_cidhashaddr");
280 			return (-1);
281 		}
282 		fileno = *pa;
283 
284 		/* check for match */
285 		if (fileno == cid.cid_fileno) {
286 			*addrp = (caddr_t)pa;
287 			dbug_leave("maptbl_cidhashaddr");
288 			return (0);
289 		}
290 
291 		/* check for not found */
292 		if (fileno == 0) {
293 			*addrp = (caddr_t)pa;
294 			dbug_leave("maptbl_cidhashaddr");
295 			return (1);
296 		}
297 
298 		/* move to the next entry */
299 		index++;
300 		index = index % maptbl_object_p->i_entries;
301 	} while (start_index != index);
302 
303 	/* table full, this is bad */
304 	dbug_print(("error", "Table is full"));
305 	dbug_leave("maptbl_cidhashaddr");
306 	return (-1);
307 }
308 
309 /*
310  *			maptbl_hash1
311  *
312  * Description:
313  *	Hashes a cid into an index into the table.
314  * Arguments:
315  *	cid
316  * Returns:
317  *	Returns the index.
318  * Preconditions:
319  */
320 int
321 maptbl_hash1(cfsd_maptbl_object_t *maptbl_object_p, cfs_cid_t cid)
322 {
323 	unsigned int xx;
324 	unsigned int a, b;
325 
326 	dbug_precond(maptbl_object_p);
327 #if 0
328 	xx = cid.cid_fileno % i_entries;
329 #else
330 	a = cid.cid_fileno >> 16;
331 	b = a ^ cid.cid_fileno;
332 	xx = b % maptbl_object_p->i_entries;
333 #endif
334 	return (xx);
335 }
336 
337 /*
338  *			maptbl_hash2
339  *
340  * Description:
341  *	Hashes a cid into an index into the table.
342  * Arguments:
343  *	cid
344  *	index
345  * Returns:
346  *	Returns the index.
347  * Preconditions:
348  */
349 int
350 maptbl_hash2(cfsd_maptbl_object_t *maptbl_object_p, cfs_cid_t cid, int index)
351 {
352 	unsigned int xx;
353 	unsigned int a, b, c, d;
354 
355 	dbug_precond(maptbl_object_p);
356 #if 0
357 	a = cid.cid_fileno & 0x0ff;
358 	b = (cid.cid_fileno >> 8) & 0x0ff;
359 	b = cid.cid_fileno ^ a ^ b;
360 	xx = b % maptbl_object_p->i_hash2mod;
361 #else
362 	a = cid.cid_fileno & 0x0ff;
363 	b = (cid.cid_fileno >> 8) & 0x0ff;
364 	c = (cid.cid_fileno >> 16) & 0x0ff;
365 	d = (cid.cid_fileno >> 24) & 0x0ff;
366 	xx = cid.cid_fileno ^ (a << 8) ^ b ^ c ^ d;
367 	xx = xx % maptbl_object_p->i_hash2mod;
368 #endif
369 	xx = (index + xx) % maptbl_object_p->i_entries;
370 	return (xx);
371 }
372 
373 /*
374  *			maptbl_setup
375  *
376  * Description:
377  *	Performs setup for the cfsd_maptbl class.
378  *	This routine must be called before other routines are used.
379  * Arguments:
380  *	filename
381  * Returns:
382  *	Returns 0 for success or an errno value.
383  * Preconditions:
384  *	precond(filename)
385  */
386 int
387 maptbl_setup(cfsd_maptbl_object_t *maptbl_object_p, const char *filename)
388 {
389 	int xx;
390 	struct stat sinfo;
391 	off_t offset;
392 	long *lp;
393 	size_t cnt;
394 	off_t size;
395 
396 	dbug_enter("maptbl_setup");
397 	dbug_precond(maptbl_object_p);
398 	dbug_precond(filename);
399 
400 	/* clean up from a previous setup */
401 	maptbl_teardown(maptbl_object_p);
402 
403 	strlcpy(maptbl_object_p->i_name, filename,
404 	    sizeof (maptbl_object_p->i_name));
405 	dbug_print(("info", "filename %s", maptbl_object_p->i_name));
406 
407 	/* get the page info */
408 	maptbl_object_p->i_pagesize = PAGESIZE;
409 	maptbl_object_p->i_pagemask = PAGEMASK;
410 	maptbl_object_p->i_maplen = maptbl_object_p->i_pagesize * 100;
411 
412 	/* open the file */
413 	maptbl_object_p->i_fid = open(maptbl_object_p->i_name,
414 	    O_RDWR | O_NONBLOCK);
415 	if (maptbl_object_p->i_fid == -1) {
416 		xx = errno;
417 		dbug_print(("error",
418 		    "Could not open %s, %d", maptbl_object_p->i_name, xx));
419 		dbug_leave("maptbl_setup");
420 		return (xx);
421 	}
422 
423 	/* get the size and type of file */
424 	xx = fstat(maptbl_object_p->i_fid, &sinfo);
425 	if (xx) {
426 		xx = errno;
427 		dbug_print(("error",
428 		    "Could not stat %s, %d", maptbl_object_p->i_name, xx));
429 		dbug_leave("maptbl_setup");
430 		return (xx);
431 	}
432 	maptbl_object_p->i_size = sinfo.st_size;
433 
434 	/* sanity check, better be a regular file */
435 	if (!S_ISREG(sinfo.st_mode)) {
436 		xx = ENOTSUP;
437 		dbug_print(("error",
438 		    "%s Not a regular file.", maptbl_object_p->i_name));
439 		dbug_leave("maptbl_setup");
440 		return (xx);
441 	}
442 
443 	/* determine number of entries */
444 	maptbl_object_p->i_entries =
445 	    maptbl_object_p->i_size / sizeof (struct cfs_dlog_mapping_space);
446 
447 	/* set up modulo value for second hash function */
448 	maptbl_object_p->i_hash2mod = (maptbl_object_p->i_entries / 2) + 1;
449 
450 	/* initialize statistic gathering */
451 	maptbl_object_p->i_stat_requests = 0;
452 	maptbl_object_p->i_stat_probes = 0;
453 	maptbl_object_p->i_stat_mapmove = 0;
454 	maptbl_object_p->i_stat_mapdist = 0;
455 	maptbl_object_p->i_stat_filled = 0;
456 
457 	/* zero the file */
458 	for (offset = 0; offset < maptbl_object_p->i_size;
459 		offset += maptbl_object_p->i_maplen) {
460 		/* map in a section of the file */
461 		xx = maptbl_domap(maptbl_object_p, offset);
462 		if (xx) {
463 			dbug_leave("maptbl_setup");
464 			return (xx);
465 		}
466 		/* zero this section of the file */
467 		lp = (long *)maptbl_object_p->i_pa;
468 		size = maptbl_object_p->i_size - offset;
469 		if (size < maptbl_object_p->i_palen) {
470 			cnt = size / sizeof (long);
471 		} else {
472 			cnt = maptbl_object_p->i_palen / sizeof (long);
473 			dbug_assert((cnt * sizeof (long)) ==
474 			    maptbl_object_p->i_palen);
475 		}
476 		memset(lp, 0, cnt * sizeof (*lp));
477 	}
478 
479 	/* return success */
480 	dbug_leave("maptbl_setup");
481 	return (0);
482 }
483 
484 /*
485  *			maptbl_teardown
486  *
487  * Description:
488  * Arguments:
489  * Returns:
490  * Preconditions:
491  */
492 void
493 maptbl_teardown(cfsd_maptbl_object_t *maptbl_object_p)
494 {
495 	int xx;
496 
497 	dbug_enter("maptbl_teardown");
498 	dbug_precond(maptbl_object_p);
499 
500 	if (maptbl_object_p->i_pa) {
501 		xx = munmap(maptbl_object_p->i_pa, maptbl_object_p->i_palen);
502 		if (xx == -1) {
503 			xx = errno;
504 			dbug_print(("error", "Could not unmap %s, %d, %p, %d",
505 			    maptbl_object_p->i_name, xx, maptbl_object_p->i_pa,
506 			    maptbl_object_p->i_palen));
507 		}
508 		maptbl_object_p->i_pa = NULL;
509 	}
510 	maptbl_object_p->i_paoff = 0;
511 	maptbl_object_p->i_paend = 0;
512 	maptbl_object_p->i_palen = 0;
513 
514 	if (maptbl_object_p->i_fid != -1) {
515 		if (close(maptbl_object_p->i_fid))
516 			dbug_print(("err", "cannot close maptbl fd, error %d",
517 			    errno));
518 		maptbl_object_p->i_fid = -1;
519 	}
520 	dbug_leave("maptbl_teardown");
521 }
522 
523 /*
524  *			maptbl_get
525  *
526  * Description:
527  *	Gets the mapping info for the specified cid.
528  * Arguments:
529  *	cid
530  *	valuep
531  * Returns:
532  *	Returns 0 for success, 1 if entry not found, -1 if an
533  *	error occurs in the mapping file.
534  * Preconditions:
535  *	precond(valuep)
536  */
537 int
538 maptbl_get(cfsd_maptbl_object_t *maptbl_object_p,
539 	cfs_cid_t cid,
540 	struct cfs_dlog_mapping_space *valuep)
541 {
542 	int xx;
543 	struct cfs_dlog_mapping_space *pa;
544 
545 	dbug_enter("maptbl_get");
546 	dbug_precond(maptbl_object_p);
547 	dbug_precond(valuep);
548 
549 	if (maptbl_object_p->i_entries == 0) {
550 		dbug_leave("maptbl_get");
551 		return (1);
552 	}
553 	xx = maptbl_cidhashaddr(maptbl_object_p, cid, (caddr_t *)&pa);
554 	if (xx == 0)
555 		*valuep = *pa;
556 	dbug_leave("maptbl_get");
557 	return (xx);
558 }
559 
560 /*
561  *			maptbl_set
562  *
563  * Description:
564  *	Sets the mapping info for the cid.
565  *	If insert is 1 then if the entry is not found it is put in the
566  *	table.
567  * Arguments:
568  *	valuep
569  *	insert
570  * Returns:
571  *	Returns 0 if mapping info placed in the table, 1 if entry
572  *	is not found an insert is 0, -1 if an error occurs in the
573  *	mapping file.
574  * Preconditions:
575  *	precond(valuep)
576  */
577 int
578 maptbl_set(cfsd_maptbl_object_t *maptbl_object_p,
579 	struct cfs_dlog_mapping_space *valuep,
580 	int insert)
581 {
582 	int xx;
583 	struct cfs_dlog_mapping_space *pa;
584 
585 	dbug_enter("maptbl_set");
586 	dbug_precond(maptbl_object_p);
587 	dbug_precond(valuep);
588 
589 	dbug_assert(maptbl_object_p->i_entries > 0);
590 
591 	xx = maptbl_cidhashaddr(maptbl_object_p, valuep->ms_cid,
592 	    (caddr_t *)&pa);
593 	if ((xx == 0) || ((xx == 1) && insert)) {
594 		*pa = *valuep;
595 		if (xx == 1)
596 			maptbl_object_p->i_stat_filled++;
597 		xx = 0;
598 	}
599 	dbug_leave("maptbl_set");
600 	return (xx);
601 }
602 
603 /*
604  *			maptbl_dumpstats
605  *
606  * Description:
607  *	Prints out various stats about the hashing.
608  * Arguments:
609  * Returns:
610  * Preconditions:
611  */
612 void
613 maptbl_dumpstats(cfsd_maptbl_object_t *maptbl_object_p)
614 {
615 	int xx;
616 	double dd;
617 
618 	dbug_enter("maptbl_dumpstats");
619 	dbug_precond(maptbl_object_p);
620 
621 	dbug_print(("dump", "Total Entries %d", maptbl_object_p->i_entries));
622 	dbug_print(("dump", "Filled Entries %d",
623 	    maptbl_object_p->i_stat_filled));
624 	dbug_print(("dump", "Requests %d", maptbl_object_p->i_stat_requests));
625 	dbug_print(("dump", "Probes %d", maptbl_object_p->i_stat_probes));
626 	dbug_print(("dump", "Map Moves %d", maptbl_object_p->i_stat_mapmove));
627 	dbug_print(("dump", "Mapping Size %d", maptbl_object_p->i_maplen));
628 	dbug_print(("dump", "File Size %d", maptbl_object_p->i_size));
629 	if (maptbl_object_p->i_stat_requests == 0) {
630 		dbug_leave("maptbl_dumpstats");
631 		return;
632 	}
633 	dd = (double)maptbl_object_p->i_stat_probes /
634 	    maptbl_object_p->i_stat_requests;
635 	dbug_print(("dump", "Probes per Request %.2f", dd));
636 
637 	dd = (double)maptbl_object_p->i_stat_mapmove /
638 	    maptbl_object_p->i_stat_requests;
639 	dbug_print(("dump", "Mmap moves per Request %.2f", dd));
640 
641 	xx = maptbl_object_p->i_stat_mapdist / maptbl_object_p->i_stat_mapmove;
642 	dbug_print(("dump", "Average distance per mmap moves %d", xx));
643 
644 	xx = ((100.0 * maptbl_object_p->i_stat_filled) /
645 	    maptbl_object_p->i_entries) + .5;
646 	dbug_print(("dump", "Table filled %d%%", xx));
647 
648 	dbug_leave("maptbl_dumpstats");
649 }
650