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_logfile.h"
  53 
  54 /*
  55  *                      cfsd_logfile_create
  56  *
  57  * Description:
  58  * Arguments:
  59  * Returns:
  60  * Preconditions:
  61  */
  62 cfsd_logfile_object_t *
  63 cfsd_logfile_create(void)
  64 {
  65         cfsd_logfile_object_t *logfile_object_p;
  66 
  67         dbug_enter("cfsd_logfile_create");
  68 
  69         logfile_object_p = cfsd_calloc(sizeof (cfsd_logfile_object_t));
  70         logfile_object_p->i_fid = -1;
  71         logfile_object_p->i_map_entry.i_pa = NULL;
  72         logfile_object_p->i_map_entry.i_paoff = 0;
  73         logfile_object_p->i_map_entry.i_paend = 0;
  74         logfile_object_p->i_map_entry.i_palen = 0;
  75         logfile_object_p->i_map_offset.i_pa = NULL;
  76         logfile_object_p->i_map_offset.i_paoff = 0;
  77         logfile_object_p->i_map_offset.i_paend = 0;
  78         logfile_object_p->i_map_offset.i_palen = 0;
  79         logfile_object_p->i_cur_offset = 0;
  80         logfile_object_p->i_cur_entry = NULL;
  81         dbug_leave("cfsd_logfile_create");
  82         return (logfile_object_p);
  83 }
  84 
  85 /*
  86  *                      cfsd_logfile_destroy
  87  *
  88  * Description:
  89  * Arguments:
  90  * Returns:
  91  * Preconditions:
  92  */
  93 void
  94 cfsd_logfile_destroy(cfsd_logfile_object_t *logfile_object_p)
  95 {
  96         dbug_enter("cfsd_logfile_destroy");
  97         logfile_sync(logfile_object_p);
  98         logfile_teardown(logfile_object_p);
  99         cfsd_free(logfile_object_p);
 100         dbug_leave("cfsd_logfile_destroy");
 101 }
 102 
 103 /*
 104  *                      logfile_domap
 105  *
 106  * Description:
 107  *      Maps in the specified section of the file.
 108  * Arguments:
 109  *      off     The offset to map in.  Must be i_pagesize aligned.
 110  *      map     0 means use map_entry, 1 means use map_offset
 111  * Returns:
 112  *      Returns 0 for success or an errno value on failure.
 113  * Preconditions:
 114  */
 115 int
 116 logfile_domap(cfsd_logfile_object_t *logfile_object_p, off_t off, int map)
 117 {
 118         int xx;
 119         int len;
 120         mmap_info_t *mmp;
 121 
 122         dbug_enter("logfile_domap");
 123         dbug_precond(logfile_object_p->i_fid >= 0);
 124 
 125         len = logfile_object_p->i_maplen;
 126         mmp = (map == 0) ?
 127                 &logfile_object_p->i_map_entry :
 128                 &logfile_object_p->i_map_offset;
 129 
 130         logfile_object_p->i_stat_mapmove++;
 131 
 132         /* destroy old mapping if it exists */
 133         if (mmp->i_pa) {
 134                 /* determine how far we have to move the map */
 135                 logfile_object_p->i_stat_mapdist += abs(mmp->i_paoff - off);
 136 
 137                 /* remove the map */
 138                 xx = munmap(mmp->i_pa, mmp->i_palen);
 139                 if (xx == -1) {
 140                         xx = errno;
 141                         dbug_print(("error", "Could not unmap %s, %d, %p, %d",
 142                             logfile_object_p->i_name, xx, mmp->i_pa,
 143                             mmp->i_palen));
 144                 }
 145                 mmp->i_pa = NULL;
 146                 mmp->i_palen = 0;
 147                 mmp->i_paoff = 0;
 148                 mmp->i_paend = 0;
 149         }
 150 
 151         /* do the mapping */
 152         mmp->i_pa = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED,
 153             logfile_object_p->i_fid, off);
 154         if (mmp->i_pa == MAP_FAILED) {
 155                 xx = errno;
 156                 dbug_print(("error",
 157                     "Could not map %s, error %d, off %d, len %d",
 158                     logfile_object_p->i_name, xx, off, len));
 159                 mmp->i_pa = NULL;
 160                 dbug_leave("logfile_domap");
 161                 return (xx);
 162         }
 163 
 164         mmp->i_palen = len;
 165         mmp->i_paoff = off;
 166         mmp->i_paend = off + len - 1;
 167         dbug_leave("logfile_domap");
 168         return (0);
 169 }
 170 
 171 /*
 172  *                      logfile_getaddr
 173  *
 174  * Description:
 175  *      Returns an address of a particular offset in the file.
 176  *      The size of the item to map is i_maxmap
 177  *      This routine assumes that if we have to remap that i_maxmap
 178  *      will fit inside the default mapping size.
 179  * Arguments:
 180  *      start   offset in the file to map
 181  *      map     0 means use map_entry, 1 means use map_offset
 182  * Returns:
 183  *      Returns NULL for a failure with the mapping file.
 184  * Preconditions:
 185  */
 186 caddr_t
 187 logfile_getaddr(cfsd_logfile_object_t *logfile_object_p, off_t start, int map)
 188 {
 189         mmap_info_t *mmp;
 190         caddr_t pa;
 191         off_t end;
 192 
 193         dbug_enter("logfile_getaddr");
 194 
 195         mmp = (map == 0) ?
 196             &logfile_object_p->i_map_entry :
 197             &logfile_object_p->i_map_offset;
 198 
 199         /* determine the end of the item */
 200         end = start + logfile_object_p->i_maxmap - 1;
 201 
 202         /* map the entry in if necessary */
 203         if ((start < mmp->i_paoff) || (mmp->i_paend < end)) {
 204                 if (logfile_domap(logfile_object_p,
 205                     start & logfile_object_p->i_pagemask, map)) {
 206                         dbug_leave("logfile_getaddr");
 207                         return (NULL);
 208                 }
 209                 dbug_assert((mmp->i_paoff <= start) && (end <= mmp->i_paend));
 210         }
 211 
 212         /* make an address and return it */
 213         pa = mmp->i_pa + (start - mmp->i_paoff);
 214         dbug_leave("logfile_getaddr");
 215         return (pa);
 216 }
 217 
 218 /*
 219  *                      logfile_setup
 220  *
 221  * Description:
 222  *      Sets up to use the specified file.
 223  *      Call this routine before using any of the other routines.
 224  * Arguments:
 225  *      filename        file to use
 226  *      maxmap          max amount needed after a map
 227  * Returns:
 228  *      Returns 0 for success or an errno value.
 229  * Preconditions:
 230  *      precond(filename)
 231  */
 232 int
 233 logfile_setup(cfsd_logfile_object_t *logfile_object_p,
 234         const char *filename, int maxmap)
 235 {
 236         int xx;
 237         struct stat sinfo;
 238         long *versionp;
 239 
 240         dbug_enter("logfile_setup");
 241         dbug_precond(filename);
 242 
 243         /* clean up from a previous setup */
 244         logfile_teardown(logfile_object_p);
 245 
 246         strlcpy(logfile_object_p->i_name, filename,
 247             sizeof (logfile_object_p->i_name));
 248         dbug_print(("info", "filename %s", logfile_object_p->i_name));
 249         logfile_object_p->i_maxmap = maxmap;
 250 
 251         /* get the page info */
 252         logfile_object_p->i_pagesize = PAGESIZE;
 253         logfile_object_p->i_pagemask = PAGEMASK;
 254         logfile_object_p->i_maplen = logfile_object_p->i_pagesize * 100;
 255 
 256         /* open the file */
 257         logfile_object_p->i_fid = open(logfile_object_p->i_name,
 258             O_RDWR | O_NONBLOCK);
 259         if (logfile_object_p->i_fid == -1) {
 260                 xx = errno;
 261                 dbug_print(("error", "Could not open %s, %d",
 262                     logfile_object_p->i_name, xx));
 263                 dbug_leave("logfile_setup");
 264                 return (xx);
 265         }
 266 
 267         /* get the size and type of file */
 268         xx = fstat(logfile_object_p->i_fid, &sinfo);
 269         if (xx) {
 270                 xx = errno;
 271                 if (xx == ENOENT) {
 272                         dbug_print(("info", "No log file to roll"));
 273                 } else {
 274                         dbug_print(("error", "Could not stat %s, %d",
 275                             logfile_object_p->i_name, xx));
 276                 }
 277                 dbug_leave("logfile_setup");
 278                 return (xx);
 279         }
 280         logfile_object_p->i_size = sinfo.st_size;
 281 
 282         /* sanity check, better be a regular file */
 283         if (!S_ISREG(sinfo.st_mode)) {
 284                 xx = ENOTSUP;
 285                 dbug_print(("error", "%s Not a regular file.",
 286                     logfile_object_p->i_name));
 287                 dbug_leave("logfile_setup");
 288                 return (xx);
 289         }
 290 
 291         /* better not be too small */
 292         if (logfile_object_p->i_size < LOGFILE_ENTRY_START) {
 293                 dbug_print(("error", "File %s is too small %d.",
 294                     logfile_object_p->i_name, logfile_object_p->i_size));
 295                 dbug_leave("logfile_setup");
 296                 return (EINVAL);
 297         }
 298 
 299         /* initialize statistic gathering */
 300         logfile_object_p->i_stat_mapmove = 0;
 301         logfile_object_p->i_stat_mapdist = 0;
 302 
 303         /* check the version number */
 304         versionp = (long *)logfile_getaddr(logfile_object_p, 0, 1);
 305         if (versionp == NULL) {
 306                 dbug_leave("logfile_setup");
 307                 return (EIO);
 308         }
 309         if (*versionp != CFS_DLOG_VERSION) {
 310                 dbug_print(("error", "Log file version mismatch %d != %d",
 311                     *versionp, CFS_DLOG_VERSION));
 312                 dbug_leave("logfile_setup");
 313                 return (EINVAL);
 314         }
 315 
 316         /* return success */
 317         dbug_leave("logfile_setup");
 318         return (0);
 319 }
 320 
 321 /*
 322  *                      logfile_teardown
 323  *
 324  * Description:
 325  *      Uninitializes the object.
 326  *      Call logfile_setup before using this object again.
 327  * Arguments:
 328  * Returns:
 329  * Preconditions:
 330  */
 331 void
 332 logfile_teardown(cfsd_logfile_object_t *logfile_object_p)
 333 {
 334         int xx;
 335 
 336         dbug_enter("logfile_teardown");
 337 
 338         if (logfile_object_p->i_map_entry.i_pa) {
 339                 xx = munmap(logfile_object_p->i_map_entry.i_pa,
 340                     logfile_object_p->i_map_entry.i_palen);
 341                 if (xx == -1) {
 342                         xx = errno;
 343                         dbug_print(("error", "Could not unmap %s, %d, %p, %d",
 344                             logfile_object_p->i_name, xx,
 345                             logfile_object_p->i_map_entry.i_pa,
 346                             logfile_object_p->i_map_entry.i_palen));
 347                 }
 348                 logfile_object_p->i_map_entry.i_pa = NULL;
 349         }
 350         logfile_object_p->i_map_entry.i_paoff = 0;
 351         logfile_object_p->i_map_entry.i_paend = 0;
 352         logfile_object_p->i_map_entry.i_palen = 0;
 353 
 354         if (logfile_object_p->i_map_offset.i_pa) {
 355                 xx = munmap(logfile_object_p->i_map_offset.i_pa,
 356                     logfile_object_p->i_map_offset.i_palen);
 357                 if (xx == -1) {
 358                         xx = errno;
 359                         dbug_print(("error", "Could not unmap %s, %d, %p, %d",
 360                             logfile_object_p->i_name, xx,
 361                             logfile_object_p->i_map_offset.i_pa,
 362                             logfile_object_p->i_map_offset.i_palen));
 363                 }
 364                 logfile_object_p->i_map_offset.i_pa = NULL;
 365         }
 366         logfile_object_p->i_map_offset.i_paoff = 0;
 367         logfile_object_p->i_map_offset.i_paend = 0;
 368         logfile_object_p->i_map_offset.i_palen = 0;
 369 
 370         if (logfile_object_p->i_fid != -1) {
 371                 if (close(logfile_object_p->i_fid))
 372                         dbug_print(("error", "Could not close %s, %d",
 373                             logfile_object_p->i_name, errno));
 374                 logfile_object_p->i_fid = -1;
 375         }
 376         logfile_object_p->i_cur_offset = 0;
 377         logfile_object_p->i_cur_entry = NULL;
 378         dbug_leave("logfile_teardown");
 379 }
 380 
 381 /*
 382  *                      logfile_entry
 383  *
 384  * Description:
 385  *      Sets addrp to the address of the log entry at offset
 386  *      The mapping remains in effect until:
 387  *              a) this routine is called again
 388  *              b) logfile_teardown is called
 389  *              c) this object is destroyed
 390  * Arguments:
 391  *      offset  offset to start of entry
 392  *      entpp   place to store address
 393  * Returns:
 394  *      Returns 0 for success, 1 for EOF, -1 if a fatal error occurs.
 395  * Preconditions:
 396  *      precond(addrp)
 397  */
 398 int
 399 logfile_entry(cfsd_logfile_object_t *logfile_object_p,
 400         off_t offset,
 401         cfs_dlog_entry_t **entpp)
 402 {
 403         cfs_dlog_entry_t *entp;
 404 
 405         dbug_enter("logfile_entry");
 406         dbug_precond(entpp);
 407         dbug_precond(offset >= sizeof (long));
 408 
 409 
 410         logfile_object_p->i_stat_nextcnt++;
 411 
 412         /* check for eof */
 413         if (offset >= logfile_object_p->i_size) {
 414                 dbug_leave("logfile_entry");
 415                 return (1);
 416         }
 417         dbug_assert((offset & 3) == 0);
 418 
 419         /* get the address of the entry */
 420         entp = (cfs_dlog_entry_t *)logfile_getaddr(logfile_object_p, offset, 0);
 421         if (entp == NULL) {
 422                 dbug_leave("logfile_entry");
 423                 return (-1);
 424         }
 425         /* sanity check, record should be alligned */
 426         if (entp->dl_len & 3) {
 427                 dbug_print(("error",
 428                     "Record at offset %d length is not alligned %d",
 429                     offset, entp->dl_len));
 430                 dbug_leave("logfile_entry");
 431                 return (-1);
 432         }
 433 
 434         /* sanity check record should a reasonable size */
 435         if ((entp->dl_len < CFS_DLOG_ENTRY_MINSIZE) ||
 436             (entp->dl_len > CFS_DLOG_ENTRY_MAXSIZE)) {
 437                 dbug_print(("error",
 438                     "Record at offset %d has an invalid size %d", offset,
 439                     entp->dl_len));
 440                 dbug_leave("logfile_entry");
 441                 return (-1);
 442         }
 443 
 444         /* preserve offset and pointer */
 445         logfile_object_p->i_cur_offset = offset;
 446         logfile_object_p->i_cur_entry = entp;
 447 
 448         /* return success */
 449         *entpp = entp;
 450         dbug_leave("logfile_entry");
 451         return (0);
 452 }
 453 
 454 /*
 455  *                      logfile_offset
 456  *
 457  * Description:
 458  *      Sets addrp to the address of the specified offset.
 459  *      The mapping remains in effect until:
 460  *              a) this routine is called again
 461  *              b) logfile_teardown is called
 462  *              c) this object is destroyed
 463  * Arguments:
 464  *      offset  offset into file, must be 0 <= offset < i_size
 465  *      addrp   returns mapped address
 466  * Returns:
 467  *      Returns 0 for success, -1 if a fatal error occurs.
 468  * Preconditions:
 469  *      precond(addrp)
 470  */
 471 int
 472 logfile_offset(cfsd_logfile_object_t *logfile_object_p,
 473         off_t offset,
 474         caddr_t *addrp)
 475 {
 476         caddr_t pa;
 477 
 478         dbug_enter("logfile_offset");
 479         dbug_precond(addrp);
 480         dbug_precond((0 <= offset) && (offset < logfile_object_p->i_size));
 481 
 482         logfile_object_p->i_stat_offcnt++;
 483 
 484         /* get the address for the offset */
 485         pa = logfile_getaddr(logfile_object_p, offset, 1);
 486         if (pa == NULL) {
 487                 dbug_leave("logfile_offset");
 488                 return (-1);
 489         }
 490         /* return success */
 491         *addrp = pa;
 492         dbug_leave("logfile_offset");
 493         return (0);
 494 }
 495 
 496 /*
 497  *                      logfile_sync
 498  *
 499  * Description:
 500  *      Performs an fsync on the log file.
 501  * Arguments:
 502  * Returns:
 503  *      Returns 0 for success or an errno value on failure.
 504  * Preconditions:
 505  */
 506 int
 507 logfile_sync(cfsd_logfile_object_t *logfile_object_p)
 508 {
 509         int xx;
 510 
 511         dbug_enter("logfile_sync");
 512 
 513         if (logfile_object_p->i_fid == -1) {
 514                 dbug_leave("logfile_sync");
 515                 return (0);
 516         }
 517         xx = fsync(logfile_object_p->i_fid);
 518         if (xx) {
 519                 xx = errno;
 520                 dbug_print(("error", "fsync failed %d", xx));
 521         }
 522         dbug_leave("logfile_sync");
 523         return (xx);
 524 }
 525 
 526 /*
 527  *                      logfile_dumpstats
 528  *
 529  * Description:
 530  *      Prints out various stats about the hashing.
 531  * Arguments:
 532  * Returns:
 533  * Preconditions:
 534  */
 535 void
 536 logfile_dumpstats(cfsd_logfile_object_t *logfile_object_p)
 537 {
 538         int xx;
 539         double dd;
 540 
 541         dbug_enter("logfile_dumpstats");
 542 
 543         dbug_print(("dump", "Request - next %d",
 544             logfile_object_p->i_stat_nextcnt));
 545         dbug_print(("dump", "Request - offset %d",
 546             logfile_object_p->i_stat_offcnt));
 547         dbug_print(("dump", "Map Moves %d", logfile_object_p->i_stat_mapmove));
 548         dbug_print(("dump", "Mapping Size %d", logfile_object_p->i_maplen));
 549         dbug_print(("dump", "Item Size %d", logfile_object_p->i_maxmap));
 550         dbug_print(("dump", "File Size %d", logfile_object_p->i_size));
 551         if (logfile_object_p->i_stat_mapmove == 0) {
 552                 dbug_leave("logfile_dumpstats");
 553                 return;
 554         }
 555 
 556         dd = (double)logfile_object_p->i_stat_mapmove /
 557             (logfile_object_p->i_stat_nextcnt +
 558             logfile_object_p->i_stat_offcnt);
 559         dbug_print(("dump", "Mmap moves per Request %.2f", dd));
 560 
 561         xx = logfile_object_p->i_stat_mapdist /
 562             logfile_object_p->i_stat_mapmove;
 563         dbug_print(("dump", "Average distance per mmap moves %d", xx));
 564         dbug_leave("logfile_dumpstats");
 565 }