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 }