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 }