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