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 *
cfsd_maptbl_create(void)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
cfsd_maptbl_destroy(cfsd_maptbl_object_t * maptbl_object_p)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
maptbl_domap(cfsd_maptbl_object_t * maptbl_object_p,off_t off)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
maptbl_getaddr(cfsd_maptbl_object_t * maptbl_object_p,int index)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
maptbl_cidhashaddr(cfsd_maptbl_object_t * maptbl_object_p,cfs_cid_t cid,caddr_t * addrp)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
maptbl_hash1(cfsd_maptbl_object_t * maptbl_object_p,cfs_cid_t cid)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
maptbl_hash2(cfsd_maptbl_object_t * maptbl_object_p,cfs_cid_t cid,int index)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
maptbl_setup(cfsd_maptbl_object_t * maptbl_object_p,const char * filename)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
maptbl_teardown(cfsd_maptbl_object_t * maptbl_object_p)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
maptbl_get(cfsd_maptbl_object_t * maptbl_object_p,cfs_cid_t cid,struct cfs_dlog_mapping_space * valuep)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
maptbl_set(cfsd_maptbl_object_t * maptbl_object_p,struct cfs_dlog_mapping_space * valuep,int insert)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
maptbl_dumpstats(cfsd_maptbl_object_t * maptbl_object_p)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