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 2005 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 #include <sys/cred.h> 30 #include <sys/cmn_err.h> 31 #include <sys/debug.h> 32 #include <sys/systm.h> 33 #include <sys/kmem.h> 34 #include <sys/disp.h> 35 #include <sys/atomic.h> 36 #include <rpc/types.h> 37 #include <nfs/nfs.h> 38 #include <nfs/nfssys.h> 39 #include <nfs/export.h> 40 #include <nfs/rnode.h> 41 #include <rpc/auth.h> 42 #include <rpc/svc.h> 43 #include <rpc/xdr.h> 44 #include <rpc/clnt.h> 45 #include <nfs/nfs_log.h> 46 47 #define NUM_RECORDS_TO_WRITE 256 48 #define NUM_BYTES_TO_WRITE 65536 49 50 extern krwlock_t exported_lock; 51 52 static int nfslog_num_records_to_write = NUM_RECORDS_TO_WRITE; 53 static int nfslog_num_bytes_to_write = NUM_BYTES_TO_WRITE; 54 55 /* 56 * This struct is used to 'hide' the details of managing the log 57 * records internally to the logging code. Allocation routines 58 * are used to obtain pieces of memory for XDR encoding. This struct 59 * is a 'header' to those areas and a opaque cookie is used to pass 60 * this data structure between the allocating function and the put 61 * function. 62 */ 63 struct lr_alloc { 64 struct lr_alloc *next; /* links for write queuing */ 65 struct lr_alloc *prev; 66 #define LR_ALLOC_NOFREE 0x1 /* not present, call free */ 67 int lr_flags; 68 caddr_t log_record; /* address to XDR encoding */ 69 size_t size; /* final size of encoding */ 70 struct kmem_cache *alloc_cache; /* keep track of cache ptr */ 71 struct exportinfo *exi; /* who are we related to? */ 72 struct log_buffer *lb; 73 }; 74 75 struct flush_thread_params { 76 struct nfsl_flush_args tp_args; 77 int tp_error; 78 }; 79 80 static int log_file_create(caddr_t, struct log_file **); 81 static void log_file_rele(struct log_file *); 82 static struct log_buffer *log_buffer_create(caddr_t); 83 static void log_buffer_rele(struct log_buffer *); 84 static int nfslog_record_append2all(struct lr_alloc *); 85 static int nfslog_logbuffer_rename(struct log_buffer *); 86 static void nfslog_logfile_wait(struct log_file *); 87 static int nfslog_logfile_rename(char *, char *); 88 static void nfslog_do_flush(struct flush_thread_params *); 89 static void create_buffer_header(caddr_t *, size_t *, size_t *); 90 91 static int nfslog_write_logrecords(struct log_file *, struct lr_alloc *, int); 92 static void nfslog_free_logrecords(struct lr_alloc *); 93 static int nfslog_records_flush_to_disk(struct log_buffer *); 94 static int nfslog_records_flush_to_disk_nolock(struct log_buffer *); 95 96 /* 97 * Read/Write lock that protects 'nfslog_buffer_list'. 98 * This lock must be held when searching or modifying 'nfslog_buffer_list'. 99 */ 100 static krwlock_t nfslog_buffer_list_lock; 101 102 /* 103 * The list of "log_buffer" structures. 104 */ 105 struct log_buffer *nfslog_buffer_list = NULL; 106 107 108 #define LOG_BUFFER_HOLD(lbp) { \ 109 mutex_enter(&(lbp)->lb_lock); \ 110 (lbp)->lb_refcnt++; \ 111 mutex_exit(&(lbp)->lb_lock); \ 112 } 113 114 #define LOG_FILE_HOLD(lfp) { \ 115 mutex_enter(&(lfp)->lf_lock); \ 116 (lfp)->lf_refcnt++; \ 117 mutex_exit(&(lfp)->lf_lock); \ 118 } 119 120 #define LOG_FILE_RELE(lfp) { \ 121 log_file_rele(lfp); \ 122 } 123 124 /* 125 * These two macros are used to prep a logfile data structure and 126 * associated file for writing data. Note that the lf_lock is 127 * held as a result of the call to the first macro. This is used 128 * for serialization correctness between the logbuffer struct and 129 * the logfile struct. 130 */ 131 #define LOG_FILE_LOCK_TO_WRITE(lfp) { \ 132 mutex_enter(&(lfp)->lf_lock); \ 133 (lfp)->lf_refcnt++; \ 134 (lfp)->lf_writers++; \ 135 } 136 137 #define LOG_FILE_UNLOCK_FROM_WRITE(lfp) { \ 138 (lfp)->lf_writers--; \ 139 if ((lfp)->lf_writers == 0 && ((lfp)->lf_flags & L_WAITING)) { \ 140 (lfp)->lf_flags &= ~L_WAITING; \ 141 cv_broadcast(&(lfp)->lf_cv_waiters); \ 142 } \ 143 mutex_exit(&(lfp)->lf_lock); \ 144 log_file_rele(lfp); \ 145 } 146 147 int rfsl_log_buffer = 0; 148 static int rfsl_log_file = 0; 149 150 /* This array is used for memory allocation of record encoding spaces */ 151 static struct { 152 int size; 153 struct kmem_cache *mem_cache; 154 char *cache_name; 155 } nfslog_mem_alloc[] = { 156 #define SMALL_INDX 0 157 { NFSLOG_SMALL_RECORD_SIZE - sizeof (struct lr_alloc), 158 NULL, NFSLOG_SMALL_REC_NAME }, 159 #define MEDIUM_INDX 1 160 { NFSLOG_MEDIUM_RECORD_SIZE - sizeof (struct lr_alloc), 161 NULL, NFSLOG_MEDIUM_REC_NAME }, 162 #define LARGE_INDX 2 163 { NFSLOG_LARGE_RECORD_SIZE - sizeof (struct lr_alloc), 164 NULL, NFSLOG_LARGE_REC_NAME }, 165 { (-1), NULL } 166 }; 167 168 /* Used to calculate the 'real' allocation size */ 169 #define ALLOC_SIZE(index) \ 170 (nfslog_mem_alloc[index].size + sizeof (struct lr_alloc)) 171 172 /* 173 * Initialize logging data buffer cache 174 */ 175 void 176 nfslog_init() 177 { 178 int indx; 179 180 rw_init(&nfslog_buffer_list_lock, NULL, RW_DEFAULT, NULL); 181 182 /* 183 * Initialize the kmem caches for encoding 184 */ 185 for (indx = 0; nfslog_mem_alloc[indx].size != (-1); indx++) { 186 nfslog_mem_alloc[indx].mem_cache = 187 kmem_cache_create(nfslog_mem_alloc[indx].cache_name, 188 ALLOC_SIZE(indx), 189 0, NULL, NULL, NULL, NULL, NULL, 0); 190 } 191 } 192 193 /* 194 * Sets up the necessary log file and related buffers to enable logging 195 * on the given export point. 196 * Returns 0 on success, non-zero on failure. 197 */ 198 int 199 nfslog_setup(struct exportinfo *exi) 200 { 201 struct exportdata *kex; 202 struct log_buffer *lbp; 203 struct log_buffer *nlbp; 204 205 kex = &exi->exi_export; 206 ASSERT(kex->ex_flags & EX_LOG); 207 208 /* 209 * Logging is enabled for the new export point, check 210 * the existing log_buffer structures to see if the 211 * desired buffer has already been opened. If so, point 212 * the new exportinfo's exi_logbuffer to the existing 213 * one. 214 */ 215 rw_enter(&nfslog_buffer_list_lock, RW_READER); 216 for (lbp = nfslog_buffer_list; lbp != NULL; lbp = lbp->lb_next) { 217 LOGGING_DPRINT((10, 218 "searching for buffer... found log_buffer '%s'\n", 219 lbp->lb_path)); 220 if (strcmp(lbp->lb_path, kex->ex_log_buffer) == 0) { 221 /* Found our match. Ref it and return */ 222 LOG_BUFFER_HOLD(lbp); 223 exi->exi_logbuffer = lbp; 224 LOGGING_DPRINT((10, "\tfound log_buffer for '%s'\n", 225 kex->ex_log_buffer)); 226 rw_exit(&nfslog_buffer_list_lock); 227 return (0); 228 } 229 } 230 rw_exit(&nfslog_buffer_list_lock); 231 232 /* 233 * New buffer needed, allocate it. 234 * The buffer list lock has been dropped so we will need to search 235 * the list again to ensure that another thread has not added 236 * a matching buffer. 237 */ 238 if ((nlbp = log_buffer_create(kex->ex_log_buffer)) == NULL) { 239 /* 240 * Failed the buffer creation for some reason so we 241 * will need to return. 242 */ 243 return (EIO); 244 } 245 246 rw_enter(&nfslog_buffer_list_lock, RW_WRITER); 247 for (lbp = nfslog_buffer_list; lbp != NULL; 248 lbp = lbp->lb_next) { 249 if (strcmp(lbp->lb_path, kex->ex_log_buffer) == 0) { 250 /* 251 * A log_buffer already exists for the 252 * indicated buffer, use it instead. 253 */ 254 LOG_BUFFER_HOLD(lbp); 255 256 exi->exi_logbuffer = lbp; 257 258 LOGGING_DPRINT((10, 259 "found log_buffer for '%s' " 260 "after allocation\n", 261 kex->ex_log_buffer)); 262 263 rw_exit(&nfslog_buffer_list_lock); 264 265 log_buffer_rele(nlbp); 266 267 return (0); 268 } 269 } 270 /* 271 * Didn't find an existing log_buffer for this buffer, 272 * use the the newly created one, and add to list. We 273 * increment the reference count because the node is 274 * entered into the global list. 275 */ 276 LOGGING_DPRINT((10, "exportfs: adding nlbp=%p to list\n", 277 nlbp)); 278 279 nlbp->lb_next = nfslog_buffer_list; 280 nfslog_buffer_list = nlbp; 281 282 LOG_BUFFER_HOLD(nlbp); /* hold is for export entry */ 283 exi->exi_logbuffer = nlbp; 284 285 rw_exit(&nfslog_buffer_list_lock); 286 287 return (0); 288 } 289 290 /* 291 * Disables logging for the given export point. 292 */ 293 void 294 nfslog_disable(struct exportinfo *exi) 295 { 296 log_buffer_rele(exi->exi_logbuffer); 297 } 298 299 /* 300 * Creates the corresponding log_buffer and log_file structures 301 * for the the buffer named 'name'. 302 * Returns a pointer to the log_buffer structure with reference one. 303 */ 304 static struct log_buffer * 305 log_buffer_create(caddr_t name) 306 { 307 struct log_buffer *buffer; 308 struct log_file *logfile; 309 int namelen = strlen(name); 310 311 LOGGING_DPRINT((10, "log_buffer_create: %s\n", name)); 312 if (log_file_create(name, &logfile)) 313 return (NULL); 314 315 buffer = (struct log_buffer *)kmem_alloc(sizeof (*buffer), KM_SLEEP); 316 buffer->lb_refcnt = 1; 317 buffer->lb_rec_id = 0; 318 buffer->lb_path = (caddr_t)kmem_alloc(namelen + 1, KM_SLEEP); 319 bcopy(name, buffer->lb_path, namelen + 1); 320 buffer->lb_logfile = logfile; 321 buffer->lb_records = NULL; 322 buffer->lb_num_recs = 0; 323 buffer->lb_size_queued = 0; 324 mutex_init(&buffer->lb_lock, NULL, MUTEX_DEFAULT, NULL); 325 rfsl_log_buffer++; 326 327 return (buffer); 328 } 329 330 /* 331 * Release a log_buffer structure 332 */ 333 static void 334 log_buffer_rele(struct log_buffer *lbp) 335 { 336 int len; 337 338 mutex_enter(&lbp->lb_lock); 339 if (--lbp->lb_refcnt > 1) { 340 mutex_exit(&lbp->lb_lock); 341 return; 342 } 343 344 if (lbp->lb_refcnt < 0) { 345 panic("log_rele: log_buffer refcnt < 0"); 346 /*NOTREACHED*/ 347 } 348 349 /* 350 * Need to drop the lb_lock before acquiring the 351 * nfslog_buffer_list_lock. To avoid double free we need 352 * to hold an additional reference to the log buffer. 353 * This will ensure that no two threads will simultaneously 354 * be trying to free the same log buffer. 355 */ 356 357 if (lbp->lb_refcnt == 1) { 358 359 /* 360 * If the ref count is 1, then the last 361 * unshare/reference has been given up and we need to 362 * clean up the buffer and remove it from the buffer 363 * list. 364 */ 365 LOGGING_DPRINT((10, 366 "log_buffer_rele lbp=%p disconnecting\n", lbp)); 367 /* 368 * Hold additional reference before dropping the lb_lock 369 */ 370 371 lbp->lb_refcnt++; 372 mutex_exit(&lbp->lb_lock); 373 374 /* 375 * Make sure that all of the buffered records are written. 376 * Don't bother checking the write return value since there 377 * isn't much we can do at this point. 378 */ 379 (void) nfslog_records_flush_to_disk(lbp); 380 381 rw_enter(&nfslog_buffer_list_lock, RW_WRITER); 382 mutex_enter(&lbp->lb_lock); 383 /* 384 * Drop the reference count held above. 385 * If the ref count is still > 1 then someone has 386 * stepped in to use this log buffer. unlock and return. 387 */ 388 if (--lbp->lb_refcnt > 1) { 389 mutex_exit(&lbp->lb_lock); 390 rw_exit(&nfslog_buffer_list_lock); 391 return; 392 } 393 394 if (lbp == nfslog_buffer_list) { 395 nfslog_buffer_list = lbp->lb_next; 396 } else { 397 struct log_buffer *tlbp; 398 399 /* Drop the log_buffer from the master list */ 400 for (tlbp = nfslog_buffer_list; tlbp->lb_next != NULL; 401 tlbp = tlbp->lb_next) { 402 if (tlbp->lb_next == lbp) { 403 tlbp->lb_next = lbp->lb_next; 404 break; 405 } 406 } 407 } 408 409 mutex_exit(&lbp->lb_lock); 410 rw_exit(&nfslog_buffer_list_lock); 411 } 412 /* 413 * ref count zero; finish clean up. 414 */ 415 LOGGING_DPRINT((10, "log_buffer_rele lbp=%p freeing\n", lbp)); 416 417 log_file_rele(lbp->lb_logfile); 418 len = strlen(lbp->lb_path) + 1; 419 kmem_free(lbp->lb_path, len); 420 kmem_free(lbp, sizeof (*lbp)); 421 rfsl_log_buffer--; 422 } 423 424 /* 425 * Creates the corresponding log_file structure for the buffer 426 * named 'log_file_name'. 427 * 'log_file_name' is created by concatenating 'origname' and LOG_INPROG_STRING. 428 * 'logfile' is set to be the log_file structure with reference one. 429 */ 430 static int 431 log_file_create(caddr_t origname, struct log_file **lfpp) 432 { 433 vnode_t *vp = NULL; 434 char *name; 435 int namelen; 436 int error; 437 struct log_file *logfile = NULL; 438 vattr_t va; 439 caddr_t loghdr = NULL; 440 size_t loghdr_len = 0; 441 size_t loghdr_free = 0; 442 443 namelen = strlen(origname) + strlen(LOG_INPROG_STRING); 444 name = (caddr_t)kmem_alloc(namelen + 1, KM_SLEEP); 445 (void) sprintf(name, "%s%s", origname, LOG_INPROG_STRING); 446 447 LOGGING_DPRINT((3, "log_file_create: %s\n", name)); 448 if (error = vn_open(name, UIO_SYSSPACE, FCREAT|FWRITE|FOFFMAX, 449 LOG_MODE, &vp, CRCREAT, 0)) { 450 nfs_cmn_err(error, CE_WARN, 451 "log_file_create: Can not open %s - error %m", name); 452 goto out; 453 } 454 LOGGING_DPRINT((3, "log_file_create: %s vp=%p v_count=%d\n", 455 name, vp, vp->v_count)); 456 457 logfile = (struct log_file *)kmem_zalloc(sizeof (*logfile), KM_SLEEP); 458 logfile->lf_path = name; 459 /* 460 * No need to bump the vnode reference count since it is set 461 * to one by vn_open(). 462 */ 463 logfile->lf_vp = vp; 464 logfile->lf_refcnt = 1; 465 mutex_init(&logfile->lf_lock, NULL, MUTEX_DEFAULT, NULL); 466 rfsl_log_file++; 467 468 va.va_mask = AT_SIZE; 469 error = VOP_GETATTR(vp, &va, 0, CRED()); 470 if (error) { 471 nfs_cmn_err(error, CE_WARN, 472 "log_file_create: Can not stat %s - error = %m", 473 name); 474 goto out; 475 } 476 477 if (va.va_size == 0) { 478 struct lr_alloc lr; 479 480 /* 481 * Write Header. 482 */ 483 create_buffer_header(&loghdr, &loghdr_len, &loghdr_free); 484 /* 485 * Dummy up a lr_alloc struct for the write 486 */ 487 lr.next = lr.prev = &lr; 488 lr.lr_flags = 0; 489 lr.log_record = loghdr; 490 lr.size = loghdr_len; 491 lr.alloc_cache = NULL; 492 lr.exi = NULL; 493 lr.lb = NULL; 494 495 mutex_enter(&logfile->lf_lock); 496 497 error = nfslog_write_logrecords(logfile, &lr, 1); 498 499 mutex_exit(&logfile->lf_lock); 500 501 if (error != 0) { 502 nfs_cmn_err(error, CE_WARN, 503 "log_file_create: Can not write header " 504 "on %s - error = %m", name); 505 goto out; 506 } 507 } 508 *lfpp = logfile; 509 510 if (loghdr != NULL) 511 kmem_free(loghdr, loghdr_free); 512 513 return (0); 514 515 out: 516 if (vp != NULL) { 517 int error1; 518 error1 = VOP_CLOSE(vp, FCREAT|FWRITE|FOFFMAX, 1, (offset_t)0, 519 CRED()); 520 if (error1) { 521 nfs_cmn_err(error1, CE_WARN, 522 "log_file_create: Can not close %s - " 523 "error = %m", name); 524 } 525 VN_RELE(vp); 526 } 527 528 kmem_free(name, namelen + 1); 529 if (logfile != NULL) { 530 mutex_destroy(&logfile->lf_lock); 531 kmem_free(logfile, sizeof (*logfile)); 532 rfsl_log_file--; 533 } 534 if (loghdr != NULL) 535 kmem_free(loghdr, loghdr_free); 536 537 return (error); 538 } 539 540 /* 541 * Release a log_file structure 542 */ 543 static void 544 log_file_rele(struct log_file *lfp) 545 { 546 int len; 547 int error; 548 549 mutex_enter(&lfp->lf_lock); 550 if (--lfp->lf_refcnt > 0) { 551 LOGGING_DPRINT((10, 552 "log_file_rele lfp=%p decremented refcnt to %d\n", 553 lfp, lfp->lf_refcnt)); 554 mutex_exit(&lfp->lf_lock); 555 return; 556 } 557 if (lfp->lf_refcnt < 0) { 558 panic("log_file_rele: log_file refcnt < 0"); 559 /*NOTREACHED*/ 560 } 561 562 LOGGING_DPRINT((10, "log_file_rele lfp=%p freeing node\n", lfp)); 563 564 lfp->lf_flags &= ~(L_PRINTED | L_ERROR); 565 566 ASSERT(lfp->lf_flags == 0); 567 ASSERT(lfp->lf_writers == 0); 568 569 if (error = VOP_CLOSE(lfp->lf_vp, FCREAT|FWRITE|FOFFMAX, 1, (offset_t)0, 570 CRED())) { 571 nfs_cmn_err(error, CE_WARN, 572 "NFS: Could not close log buffer %s - error = %m", 573 lfp->lf_path); 574 #ifdef DEBUG 575 } else { 576 LOGGING_DPRINT((3, 577 "log_file_rele: %s has been closed vp=%p " 578 "v_count=%d\n", 579 lfp->lf_path, lfp->lf_vp, lfp->lf_vp->v_count)); 580 #endif 581 } 582 VN_RELE(lfp->lf_vp); 583 584 len = strlen(lfp->lf_path) + 1; 585 kmem_free(lfp->lf_path, len); 586 kmem_free(lfp, sizeof (*lfp)); 587 rfsl_log_file--; 588 } 589 590 /* 591 * Allocates a record of the size specified. 592 * 'exi' identifies the exportinfo structure being logged. 593 * 'size' indicates how much memory should be allocated 594 * 'cookie' is used to store an opaque value for the caller for later use 595 * 'flags' currently ignored. 596 * 597 * Returns a pointer to the beginning of the allocated memory. 598 * 'cookie' is a pointer to the 'lr_alloc' struct; this will be used 599 * to keep track of the encoded record and contains all the info 600 * for enqueuing the record on the log buffer for later writing. 601 * 602 * nfslog_record_put() must be used to 'free' this record or allocation. 603 */ 604 /* ARGSUSED */ 605 void * 606 nfslog_record_alloc( 607 struct exportinfo *exi, 608 int alloc_indx, 609 void **cookie, 610 int flags) 611 { 612 struct lr_alloc *lrp; 613 614 lrp = (struct lr_alloc *) 615 kmem_cache_alloc(nfslog_mem_alloc[alloc_indx].mem_cache, 616 KM_NOSLEEP); 617 618 if (lrp == NULL) { 619 *cookie = NULL; 620 return (NULL); 621 } 622 623 lrp->next = lrp; 624 lrp->prev = lrp; 625 lrp->lr_flags = 0; 626 627 lrp->log_record = (caddr_t)((uintptr_t)lrp + 628 (uintptr_t)sizeof (struct lr_alloc)); 629 lrp->size = nfslog_mem_alloc[alloc_indx].size; 630 lrp->alloc_cache = nfslog_mem_alloc[alloc_indx].mem_cache; 631 lrp->exi = exi; 632 633 if (exi->exi_export.ex_flags & EX_LOG) { 634 LOG_BUFFER_HOLD(exi->exi_logbuffer); 635 lrp->lb = exi->exi_logbuffer; 636 } else { 637 lrp->lb = NULL; 638 } 639 640 *cookie = (void *)lrp; 641 642 LOGGING_DPRINT((3, 643 "nfslog_record_alloc(log_buffer=%p mem=%p size=%lu)\n", 644 exi->exi_logbuffer, lrp->log_record, lrp->size)); 645 return (lrp->log_record); 646 } 647 648 /* 649 * After the above nfslog_record_alloc() has been called and a record 650 * encoded into the buffer that was returned, this function is called 651 * to handle appropriate disposition of the newly created record. 652 * The cookie value is the one that was returned from nfslog_record_alloc(). 653 * Size is the actual size of the record that was encoded. This is 654 * passed in because the size used for the alloc was just an approximation. 655 * The sync parameter is used to tell us if we need to force this record 656 * to disk and if not it will be queued for later writing. 657 * 658 * Note that if the size parameter has a value of 0, then the record is 659 * not written to the log and the associated data structures are released. 660 */ 661 void 662 nfslog_record_put(void *cookie, size_t size, bool_t sync, 663 unsigned int which_buffers) 664 { 665 struct lr_alloc *lrp = (struct lr_alloc *)cookie; 666 struct log_buffer *lbp = lrp->lb; 667 668 /* 669 * If the caller has nothing to write or if there is 670 * an apparent error, rele the buffer and free. 671 */ 672 if (size == 0 || size > lrp->size) { 673 nfslog_free_logrecords(lrp); 674 return; 675 } 676 677 /* 678 * Reset the size to what actually needs to be written 679 * This is used later on when the iovec is built for 680 * writing the records to the log file. 681 */ 682 lrp->size = size; 683 684 /* append to all if public exi */ 685 if (which_buffers == NFSLOG_ALL_BUFFERS) { 686 (void) nfslog_record_append2all(lrp); 687 nfslog_free_logrecords(lrp); 688 return; 689 } 690 691 /* Insert the record on the list to be written */ 692 mutex_enter(&lbp->lb_lock); 693 if (lbp->lb_records == NULL) { 694 lbp->lb_records = (caddr_t)lrp; 695 lbp->lb_num_recs = 1; 696 lbp->lb_size_queued = lrp->size; 697 } else { 698 insque(lrp, ((struct lr_alloc *)lbp->lb_records)->prev); 699 lbp->lb_num_recs++; 700 lbp->lb_size_queued += lrp->size; 701 } 702 703 /* 704 * Determine if the queue for this log buffer should be flushed. 705 * This is done by either the number of records queued, the total 706 * size of all records queued or by the request of the caller 707 * via the sync parameter. 708 */ 709 if (lbp->lb_size_queued >= nfslog_num_bytes_to_write || 710 lbp->lb_num_recs > nfslog_num_records_to_write || 711 sync == TRUE) { 712 mutex_exit(&lbp->lb_lock); 713 (void) nfslog_records_flush_to_disk(lbp); 714 } else { 715 mutex_exit(&lbp->lb_lock); 716 } 717 718 } 719 720 /* 721 * Examine the log_buffer struct to see if there are queue log records 722 * that need to be written to disk. If some exist, pull them off of 723 * the log buffer and write them to the log file. 724 */ 725 static int 726 nfslog_records_flush_to_disk(struct log_buffer *lbp) 727 { 728 729 mutex_enter(&lbp->lb_lock); 730 731 if (lbp->lb_records == NULL) { 732 mutex_exit(&lbp->lb_lock); 733 return (0); 734 } 735 return (nfslog_records_flush_to_disk_nolock(lbp)); 736 } 737 738 /* 739 * Function requires that the caller holds lb_lock. 740 * Function flushes any records in the log buffer to the disk. 741 * Function drops the lb_lock on return. 742 */ 743 744 static int 745 nfslog_records_flush_to_disk_nolock(struct log_buffer *lbp) 746 { 747 struct log_file *lfp = NULL; 748 struct lr_alloc *lrp_writers; 749 int num_recs; 750 int error = 0; 751 752 ASSERT(MUTEX_HELD(&lbp->lb_lock)); 753 754 lfp = lbp->lb_logfile; 755 756 LOG_FILE_LOCK_TO_WRITE(lfp); 757 ASSERT(lbp->lb_records != NULL); 758 759 lrp_writers = (struct lr_alloc *)lbp->lb_records; 760 lbp->lb_records = NULL; 761 num_recs = lbp->lb_num_recs; 762 lbp->lb_num_recs = 0; 763 lbp->lb_size_queued = 0; 764 mutex_exit(&lbp->lb_lock); 765 error = nfslog_write_logrecords(lfp, lrp_writers, num_recs); 766 767 LOG_FILE_UNLOCK_FROM_WRITE(lfp); 768 769 nfslog_free_logrecords(lrp_writers); 770 return (error); 771 } 772 773 774 /* 775 * Take care of writing the provided log record(s) to the log file. 776 * We group the log records with an iovec and use VOP_WRITE to append 777 * them to the end of the log file. 778 */ 779 static int 780 nfslog_write_logrecords(struct log_file *lfp, 781 struct lr_alloc *lrp_writers, int num_recs) 782 { 783 struct uio uio; 784 struct iovec *iovp; 785 int size_iovecs; 786 vnode_t *vp; 787 struct vattr va; 788 struct lr_alloc *lrp; 789 int i; 790 ssize_t len; 791 int ioflag = FAPPEND; 792 int error = 0; 793 794 ASSERT(MUTEX_HELD(&lfp->lf_lock)); 795 796 vp = lfp->lf_vp; 797 798 size_iovecs = sizeof (struct iovec) * num_recs; 799 iovp = (struct iovec *)kmem_alloc(size_iovecs, KM_NOSLEEP); 800 801 if (iovp == NULL) { 802 error = ENOMEM; 803 goto out; 804 } 805 806 /* Build the iovec based on the list of log records */ 807 i = 0; 808 len = 0; 809 lrp = lrp_writers; 810 do { 811 iovp[i].iov_base = lrp->log_record; 812 iovp[i].iov_len = lrp->size; 813 len += lrp->size; 814 lrp = lrp->next; 815 i++; 816 } while (lrp != lrp_writers); 817 818 ASSERT(i == num_recs); 819 820 uio.uio_iov = iovp; 821 uio.uio_iovcnt = num_recs; 822 uio.uio_loffset = 0; 823 uio.uio_segflg = (short)UIO_SYSSPACE; 824 uio.uio_resid = len; 825 uio.uio_llimit = (rlim64_t)MAXOFFSET_T; 826 uio.uio_fmode = FWRITE; 827 uio.uio_extflg = UIO_COPY_DEFAULT; 828 829 /* 830 * Save the size. If the write fails, reset the size to avoid 831 * corrupted log buffer files. 832 */ 833 va.va_mask = AT_SIZE; 834 835 (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); /* UIO_WRITE */ 836 if ((error = VOP_GETATTR(vp, &va, 0, CRED())) == 0) { 837 if ((len + va.va_size) < (MAXOFF32_T)) { 838 error = VOP_WRITE(vp, &uio, ioflag, CRED(), NULL); 839 if (uio.uio_resid) 840 error = ENOSPC; 841 if (error) 842 (void) VOP_SETATTR(vp, &va, 0, CRED(), NULL); 843 } else { 844 if (!(lfp->lf_flags & L_PRINTED)) { 845 cmn_err(CE_WARN, 846 "NFS Logging: buffer file %s exceeds 2GB; " 847 "stopped writing buffer \n", lfp->lf_path); 848 } 849 error = ENOSPC; 850 } 851 } 852 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); 853 854 kmem_free(iovp, size_iovecs); 855 856 out: 857 if (error) { 858 if (!(lfp->lf_flags & L_PRINTED)) { 859 nfs_cmn_err(error, CE_WARN, 860 "NFS Logging disabled for buffer %s - " 861 "write error = %m\n", lfp->lf_path); 862 lfp->lf_flags |= L_PRINTED; 863 } 864 } else if (lfp->lf_flags & (L_ERROR | L_PRINTED)) { 865 lfp->lf_flags &= ~(L_ERROR | L_PRINTED); 866 cmn_err(CE_WARN, 867 "NFS Logging re-enabled for buffer %s\n", lfp->lf_path); 868 } 869 870 return (error); 871 } 872 873 static void 874 nfslog_free_logrecords(struct lr_alloc *lrp_writers) 875 { 876 struct lr_alloc *lrp = lrp_writers; 877 struct lr_alloc *lrp_free; 878 879 do { 880 lrp_free = lrp; 881 882 lrp = lrp->next; 883 884 /* 885 * Check to see if we are supposed to free this structure 886 * and relese the log_buffer ref count. 887 * It may be the case that the caller does not want this 888 * structure and its record contents freed just yet. 889 */ 890 if ((lrp_free->lr_flags & LR_ALLOC_NOFREE) == 0) { 891 if (lrp_free->lb != NULL) 892 log_buffer_rele(lrp_free->lb); 893 if (lrp_free->alloc_cache) /* double check */ 894 kmem_cache_free(lrp_free->alloc_cache, 895 (void *)lrp_free); 896 } else { 897 /* 898 * after being pulled from the list the 899 * pointers need to be reinitialized. 900 */ 901 lrp_free->next = lrp_free; 902 lrp_free->prev = lrp_free; 903 } 904 905 } while (lrp != lrp_writers); 906 } 907 908 /* 909 * Rename lbp->lb_logfile to reflect the true name requested by 'share' 910 */ 911 static int 912 nfslog_logbuffer_rename(struct log_buffer *lbp) 913 { 914 struct log_file *lf; 915 int error; 916 struct log_file *logfile; 917 918 /* 919 * Try our best to get the cache records into the log file 920 * before the rename occurs. 921 */ 922 (void) nfslog_records_flush_to_disk(lbp); 923 924 /* 925 * Hold lb_lock before retrieving 926 * lb_logfile. 927 * Hold a reference to the 928 * "lf" structure. this is 929 * same as LOG_FILE_HOLD() 930 */ 931 mutex_enter(&(lbp)->lb_lock); 932 lf = lbp->lb_logfile; 933 mutex_enter(&(lf)->lf_lock); 934 mutex_exit(&(lbp)->lb_lock); 935 lf->lf_refcnt++; 936 mutex_exit(&(lf)->lf_lock); 937 938 LOGGING_DPRINT((10, "nfslog_logbuffer_rename: renaming %s to %s\n", 939 lf->lf_path, lbp->lb_path)); 940 941 /* 942 * rename the current buffer to what the daemon expects 943 */ 944 if (error = nfslog_logfile_rename(lf->lf_path, lbp->lb_path)) 945 goto out; 946 947 /* 948 * Create a new working buffer file and have all new data sent there. 949 */ 950 if (error = log_file_create(lbp->lb_path, &logfile)) { 951 /* Attempt to rename to original */ 952 (void) nfslog_logfile_rename(lbp->lb_path, lf->lf_path); 953 goto out; 954 } 955 956 /* 957 * Hold the lb_lock here, this will make 958 * all the threads trying to access lb->logfile block 959 * and get a new logfile structure instead of old one. 960 */ 961 mutex_enter(&(lbp)->lb_lock); 962 lbp->lb_logfile = logfile; 963 mutex_exit(&(lbp)->lb_lock); 964 965 LOG_FILE_RELE(lf); /* release log_buffer's reference */ 966 967 /* 968 * Wait for log_file to be in a quiescent state before we 969 * return to our caller to let it proceed with the reading of 970 * this file. 971 */ 972 nfslog_logfile_wait(lf); 973 974 out: 975 /* 976 * Release our reference on "lf" in two different cases. 977 * 1. Error condition, release only the reference 978 * that we held at the begining of this 979 * routine on "lf" structure. 980 * 2. Fall through condition, no errors but the old 981 * logfile structure "lf" has been replaced with 982 * the new "logfile" structure, so release the 983 * reference that was part of the creation of 984 * "lf" structure to free up the resources. 985 */ 986 987 LOG_FILE_RELE(lf); 988 989 return (error); 990 } 991 992 /* 993 * Renames the 'from' file to 'new'. 994 */ 995 static int 996 nfslog_logfile_rename(char *from, char *new) 997 { 998 int error; 999 1000 if (error = vn_rename(from, new, UIO_SYSSPACE)) { 1001 cmn_err(CE_WARN, 1002 "nfslog_logfile_rename: couldn't rename %s to %s\n", 1003 from, new); 1004 } 1005 return (error); 1006 } 1007 1008 /* 1009 * Wait for the log_file writers to finish before returning 1010 */ 1011 static void 1012 nfslog_logfile_wait(struct log_file *lf) 1013 { 1014 mutex_enter(&lf->lf_lock); 1015 while (lf->lf_writers > 0) { 1016 lf->lf_flags |= L_WAITING; 1017 (void) cv_wait_sig(&lf->lf_cv_waiters, &lf->lf_lock); 1018 } 1019 mutex_exit(&lf->lf_lock); 1020 } 1021 1022 static int 1023 nfslog_record_append2all(struct lr_alloc *lrp) 1024 { 1025 struct log_buffer *lbp, *nlbp; 1026 int error, ret_error = 0; 1027 int lr_flags = lrp->lr_flags; 1028 1029 rw_enter(&nfslog_buffer_list_lock, RW_READER); 1030 if ((lbp = nfslog_buffer_list) != NULL) 1031 LOG_BUFFER_HOLD(lbp); 1032 for (nlbp = NULL; lbp != NULL; lbp = nlbp) { 1033 if ((nlbp = lbp->lb_next) != NULL) { 1034 /* 1035 * Remember next element in the list 1036 */ 1037 LOG_BUFFER_HOLD(nlbp); 1038 } 1039 rw_exit(&nfslog_buffer_list_lock); 1040 1041 /* 1042 * Insert the record on the buffer's list to be written 1043 * and then flush the records to the log file. 1044 * Make sure to set the no free flag so that the 1045 * record can be used for the next write 1046 */ 1047 lrp->lr_flags = LR_ALLOC_NOFREE; 1048 1049 ASSERT(lbp != NULL); 1050 mutex_enter(&lbp->lb_lock); 1051 if (lbp->lb_records == NULL) { 1052 lbp->lb_records = (caddr_t)lrp; 1053 lbp->lb_num_recs = 1; 1054 lbp->lb_size_queued = lrp->size; 1055 } else { 1056 insque(lrp, ((struct lr_alloc *)lbp->lb_records)->prev); 1057 lbp->lb_num_recs++; 1058 lbp->lb_size_queued += lrp->size; 1059 } 1060 1061 /* 1062 * Flush log records to disk. 1063 * Function is called with lb_lock held. 1064 * Function drops the lb_lock on return. 1065 */ 1066 error = nfslog_records_flush_to_disk_nolock(lbp); 1067 1068 if (error) { 1069 ret_error = -1; 1070 nfs_cmn_err(error, CE_WARN, 1071 "rfsl_log_pubfh: could not append record to " 1072 "\"%s\" error = %m\n", lbp->lb_path); 1073 } 1074 log_buffer_rele(lbp); 1075 rw_enter(&nfslog_buffer_list_lock, RW_READER); 1076 } 1077 rw_exit(&nfslog_buffer_list_lock); 1078 1079 lrp->lr_flags = lr_flags; 1080 1081 return (ret_error); 1082 } 1083 1084 #ifdef DEBUG 1085 static int logging_debug = 0; 1086 1087 /* 1088 * 0) no debugging 1089 * 3) current test software 1090 * 10) random stuff 1091 */ 1092 void 1093 nfslog_dprint(const int level, const char *fmt, ...) 1094 { 1095 va_list args; 1096 1097 if (logging_debug == level || 1098 (logging_debug > 10 && (logging_debug - 10) >= level)) { 1099 va_start(args, fmt); 1100 (void) vprintf(fmt, args); 1101 va_end(args); 1102 } 1103 } 1104 1105 #endif /* DEBUG */ 1106 1107 /* 1108 * NFS Log Flush system call 1109 * Caller must check privileges. 1110 */ 1111 /* ARGSUSED */ 1112 int 1113 nfsl_flush(struct nfsl_flush_args *args, model_t model) 1114 { 1115 struct flush_thread_params *tparams; 1116 struct nfsl_flush_args *nfsl_args; 1117 int error = 0; 1118 ulong_t buffer_len; 1119 STRUCT_HANDLE(nfsl_flush_args, uap); 1120 1121 STRUCT_SET_HANDLE(uap, model, args); 1122 1123 tparams = (struct flush_thread_params *) 1124 kmem_zalloc(sizeof (*tparams), KM_SLEEP); 1125 1126 nfsl_args = &tparams->tp_args; 1127 nfsl_args->version = STRUCT_FGET(uap, version); 1128 if (nfsl_args->version != NFSL_FLUSH_ARGS_VERS) { 1129 cmn_err(CE_WARN, "nfsl_flush: exected version %d, got %d", 1130 NFSL_FLUSH_ARGS_VERS, nfsl_args->version); 1131 return (EIO); 1132 } 1133 1134 nfsl_args->directive = STRUCT_FGET(uap, directive); 1135 if ((nfsl_args->directive & NFSL_ALL) == 0) { 1136 /* 1137 * Process a specific buffer 1138 */ 1139 nfsl_args->buff_len = STRUCT_FGET(uap, buff_len); 1140 1141 nfsl_args->buff = (char *) 1142 kmem_alloc(nfsl_args->buff_len, KM_NOSLEEP); 1143 if (nfsl_args->buff == NULL) 1144 return (ENOMEM); 1145 1146 error = copyinstr((const char *)STRUCT_FGETP(uap, buff), 1147 nfsl_args->buff, nfsl_args->buff_len, &buffer_len); 1148 if (error) 1149 return (EFAULT); 1150 1151 if (nfsl_args->buff_len != buffer_len) 1152 return (EFAULT); 1153 } 1154 1155 LOGGING_DPRINT((10, "nfsl_flush: Flushing %s buffer(s)\n", 1156 nfsl_args->directive & NFSL_ALL ? "all" : nfsl_args->buff)); 1157 1158 if (nfsl_args->directive & NFSL_SYNC) { 1159 /* 1160 * Do the work synchronously 1161 */ 1162 nfslog_do_flush(tparams); 1163 error = tparams->tp_error; 1164 kmem_free(nfsl_args->buff, nfsl_args->buff_len); 1165 kmem_free(tparams, sizeof (*tparams)); 1166 } else { 1167 /* 1168 * Do the work asynchronously 1169 */ 1170 (void) thread_create(NULL, 0, nfslog_do_flush, 1171 tparams, 0, &p0, TS_RUN, minclsyspri); 1172 } 1173 1174 return (error); 1175 } 1176 1177 /* 1178 * This is where buffer flushing would occur, but there is no buffering 1179 * at this time. 1180 * Possibly rename the log buffer for processing. 1181 * Sets tparams->ta_error equal to the value of the error that occured, 1182 * 0 otherwise. 1183 * Returns ENOENT if the buffer is not found. 1184 */ 1185 static void 1186 nfslog_do_flush(struct flush_thread_params *tparams) 1187 { 1188 struct nfsl_flush_args *args; 1189 struct log_buffer *lbp; 1190 int error = ENOENT; 1191 int found = 0; 1192 char *buf_inprog; /* name of buff in progress */ 1193 int buf_inprog_len; 1194 1195 /* 1196 * Sanity check on the arguments. 1197 */ 1198 if (!tparams) 1199 return; 1200 args = &tparams->tp_args; 1201 if (!args) 1202 return; 1203 1204 rw_enter(&nfslog_buffer_list_lock, RW_READER); 1205 for (lbp = nfslog_buffer_list; lbp != NULL; lbp = lbp->lb_next) { 1206 if (args->directive & NFSL_ALL) { 1207 (void) nfslog_records_flush_to_disk(lbp); 1208 } else { 1209 if ((strcmp(lbp->lb_path, args->buff) == 0) && 1210 (args->directive & NFSL_RENAME)) { 1211 1212 error = nfslog_logbuffer_rename(lbp); 1213 found++; 1214 break; 1215 } 1216 } 1217 } 1218 rw_exit(&nfslog_buffer_list_lock); 1219 1220 if (!found && ((args->directive & NFSL_ALL) == 0) && 1221 (args->directive & NFSL_RENAME)) { 1222 /* 1223 * The specified buffer is not currently in use, 1224 * simply rename the file indicated. 1225 */ 1226 buf_inprog_len = strlen(args->buff) + 1227 strlen(LOG_INPROG_STRING) + 1; 1228 buf_inprog = (caddr_t)kmem_alloc(buf_inprog_len, KM_SLEEP); 1229 (void) sprintf(buf_inprog, "%s%s", 1230 args->buff, LOG_INPROG_STRING); 1231 1232 error = nfslog_logfile_rename(buf_inprog, args->buff); 1233 1234 kmem_free(buf_inprog, buf_inprog_len); 1235 } 1236 1237 out: 1238 if ((args->directive & NFSL_SYNC) == 0) { 1239 /* 1240 * Work was performed asynchronously, the caller is 1241 * no longer waiting for us. 1242 * Free the thread arguments and exit. 1243 */ 1244 kmem_free(args->buff, args->buff_len); 1245 kmem_free(tparams, sizeof (*tparams)); 1246 thread_exit(); 1247 /* NOTREACHED */ 1248 } 1249 1250 tparams->tp_error = error; 1251 } 1252 1253 /* 1254 * Generate buffer_header. 1255 * 'loghdr' points the the buffer_header, and *reclen 1256 * contains the length of the buffer. 1257 */ 1258 static void 1259 create_buffer_header(caddr_t *loghdr, size_t *reclen, size_t *freesize) 1260 { 1261 timestruc_t now; 1262 nfslog_buffer_header lh; 1263 XDR xdrs; 1264 unsigned int final_size; 1265 1266 1267 /* pick some size that will hold the buffer_header */ 1268 *freesize = NFSLOG_SMALL_RECORD_SIZE; 1269 1270 /* 1271 * Fill header 1272 */ 1273 lh.bh_length = 0; /* don't know yet how large it will be */ 1274 lh.bh_version = NFSLOG_BUF_VERSION; 1275 lh.bh_flags = 0; 1276 lh.bh_offset = 0; 1277 gethrestime(&now); 1278 TIMESPEC_TO_TIMESPEC32(&lh.bh_timestamp, &now); 1279 1280 /* 1281 * Encode the header 1282 */ 1283 *loghdr = (caddr_t)kmem_alloc(*freesize, KM_SLEEP); 1284 xdrmem_create(&xdrs, *loghdr, *freesize, XDR_ENCODE); 1285 1286 (void) xdr_nfslog_buffer_header(&xdrs, &lh); 1287 1288 /* 1289 * Reset with final size of the encoded data 1290 */ 1291 final_size = xdr_getpos(&xdrs); 1292 xdr_setpos(&xdrs, 0); 1293 (void) xdr_u_int(&xdrs, &final_size); 1294 1295 *reclen = (size_t)final_size; 1296 } 1297 1298 /* 1299 * **************************************************************** 1300 * RPC dispatch table for logging 1301 * Indexed by program, version, proc 1302 * Based on NFS dispatch table. 1303 */ 1304 struct nfslog_proc_disp { 1305 bool_t (*xdrargs)(); 1306 bool_t (*xdrres)(); 1307 bool_t affects_transactions; /* Operation affects transaction */ 1308 /* processing */ 1309 }; 1310 1311 struct nfslog_vers_disp { 1312 int nfslog_dis_nprocs; /* number of procs */ 1313 struct nfslog_proc_disp *nfslog_dis_proc_table; /* proc array */ 1314 }; 1315 1316 struct nfslog_prog_disp { 1317 int nfslog_dis_prog; /* program number */ 1318 int nfslog_dis_versmin; /* Minimum version value */ 1319 int nfslog_dis_nvers; /* Number of version values */ 1320 struct nfslog_vers_disp *nfslog_dis_vers_table; /* versions array */ 1321 }; 1322 1323 static int rfs_log_bad = 0; /* incremented on bad log attempts */ 1324 static int rfs_log_good = 0; /* incremented on successful log attempts */ 1325 1326 /* 1327 * Define the actions taken per prog/vers/proc: 1328 * 1329 * In some cases, the nl types are the same as the nfs types and a simple 1330 * bcopy should suffice. Rather that define tens of identical procedures, 1331 * simply define these to bcopy. Similarly this takes care of different 1332 * procs that use same parameter struct. 1333 */ 1334 1335 static struct nfslog_proc_disp nfslog_proc_v2[] = { 1336 /* 1337 * NFS VERSION 2 1338 */ 1339 1340 /* RFS_NULL = 0 */ 1341 {xdr_void, xdr_void, FALSE}, 1342 1343 /* RFS_GETATTR = 1 */ 1344 {xdr_fhandle, xdr_nfslog_getattrres, FALSE}, 1345 1346 /* RFS_SETATTR = 2 */ 1347 {xdr_nfslog_setattrargs, xdr_nfsstat, TRUE}, 1348 1349 /* RFS_ROOT = 3 *** NO LONGER SUPPORTED *** */ 1350 {xdr_void, xdr_void, FALSE}, 1351 1352 /* RFS_LOOKUP = 4 */ 1353 {xdr_nfslog_diropargs, xdr_nfslog_diropres, TRUE}, 1354 1355 /* RFS_READLINK = 5 */ 1356 {xdr_fhandle, xdr_nfslog_rdlnres, FALSE}, 1357 1358 /* RFS_READ = 6 */ 1359 {xdr_nfslog_nfsreadargs, xdr_nfslog_rdresult, TRUE}, 1360 1361 /* RFS_WRITECACHE = 7 *** NO LONGER SUPPORTED *** */ 1362 {xdr_void, xdr_void, FALSE}, 1363 1364 /* RFS_WRITE = 8 */ 1365 {xdr_nfslog_writeargs, xdr_nfslog_writeresult, TRUE}, 1366 1367 /* RFS_CREATE = 9 */ 1368 {xdr_nfslog_createargs, xdr_nfslog_diropres, TRUE}, 1369 1370 /* RFS_REMOVE = 10 */ 1371 {xdr_nfslog_diropargs, xdr_nfsstat, TRUE}, 1372 1373 /* RFS_RENAME = 11 */ 1374 {xdr_nfslog_rnmargs, xdr_nfsstat, TRUE}, 1375 1376 /* RFS_LINK = 12 */ 1377 {xdr_nfslog_linkargs, xdr_nfsstat, TRUE}, 1378 1379 /* RFS_SYMLINK = 13 */ 1380 {xdr_nfslog_symlinkargs, xdr_nfsstat, TRUE}, 1381 1382 /* RFS_MKDIR = 14 */ 1383 {xdr_nfslog_createargs, xdr_nfslog_diropres, TRUE}, 1384 1385 /* RFS_RMDIR = 15 */ 1386 {xdr_nfslog_diropargs, xdr_nfsstat, TRUE}, 1387 1388 /* RFS_READDIR = 16 */ 1389 {xdr_nfslog_rddirargs, xdr_nfslog_rddirres, TRUE}, 1390 1391 /* RFS_STATFS = 17 */ 1392 {xdr_fhandle, xdr_nfslog_statfs, FALSE}, 1393 }; 1394 1395 1396 /* 1397 * NFS VERSION 3 1398 */ 1399 1400 static struct nfslog_proc_disp nfslog_proc_v3[] = { 1401 1402 /* NFSPROC3_NULL = 0 */ 1403 {xdr_void, xdr_void, FALSE}, 1404 1405 /* NFSPROC3_GETATTR = 1 */ 1406 {xdr_nfs_fh3, xdr_nfslog_GETATTR3res, FALSE}, 1407 1408 /* NFSPROC3_SETATTR = 2 */ 1409 {xdr_nfslog_SETATTR3args, xdr_nfslog_SETATTR3res, TRUE}, 1410 1411 /* NFSPROC3_LOOKUP = 3 */ 1412 {xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res, TRUE}, 1413 1414 /* NFSPROC3_ACCESS = 4 */ 1415 {xdr_nfslog_ACCESS3args, xdr_nfslog_ACCESS3res, FALSE}, 1416 1417 /* NFSPROC3_READLINK = 5 */ 1418 {xdr_nfs_fh3, xdr_nfslog_READLINK3res, FALSE}, 1419 1420 /* NFSPROC3_READ = 6 */ 1421 {xdr_nfslog_READ3args, xdr_nfslog_READ3res, TRUE}, 1422 1423 /* NFSPROC3_WRITE = 7 */ 1424 {xdr_nfslog_WRITE3args, xdr_nfslog_WRITE3res, TRUE}, 1425 1426 /* NFSPROC3_CREATE = 8 */ 1427 {xdr_nfslog_CREATE3args, xdr_nfslog_CREATE3res, TRUE}, 1428 1429 /* NFSPROC3_MKDIR = 9 */ 1430 {xdr_nfslog_MKDIR3args, xdr_nfslog_MKDIR3res, TRUE}, 1431 1432 /* NFSPROC3_SYMLINK = 10 */ 1433 {xdr_nfslog_SYMLINK3args, xdr_nfslog_SYMLINK3res, TRUE}, 1434 1435 /* NFSPROC3_MKNOD = 11 */ 1436 {xdr_nfslog_MKNOD3args, xdr_nfslog_MKNOD3res, TRUE}, 1437 1438 /* NFSPROC3_REMOVE = 12 */ 1439 {xdr_nfslog_REMOVE3args, xdr_nfslog_REMOVE3res, TRUE}, 1440 1441 /* NFSPROC3_RMDIR = 13 */ 1442 {xdr_nfslog_RMDIR3args, xdr_nfslog_RMDIR3res, TRUE}, 1443 1444 /* NFSPROC3_RENAME = 14 */ 1445 {xdr_nfslog_RENAME3args, xdr_nfslog_RENAME3res, TRUE}, 1446 1447 /* NFSPROC3_LINK = 15 */ 1448 {xdr_nfslog_LINK3args, xdr_nfslog_LINK3res, TRUE}, 1449 1450 /* NFSPROC3_READDIR = 16 */ 1451 {xdr_nfslog_READDIR3args, xdr_nfslog_READDIR3res, TRUE}, 1452 1453 /* NFSPROC3_READDIRPLUS = 17 */ 1454 {xdr_nfslog_READDIRPLUS3args, xdr_nfslog_READDIRPLUS3res, TRUE}, 1455 1456 /* NFSPROC3_FSSTAT = 18 */ 1457 {xdr_nfslog_FSSTAT3args, xdr_nfslog_FSSTAT3res, FALSE}, 1458 1459 /* NFSPROC3_FSINFO = 19 */ 1460 {xdr_nfslog_FSINFO3args, xdr_nfslog_FSINFO3res, FALSE}, 1461 1462 /* NFSPROC3_PATHCONF = 20 */ 1463 {xdr_nfslog_PATHCONF3args, xdr_nfslog_PATHCONF3res, FALSE}, 1464 1465 /* NFSPROC3_COMMIT = 21 */ 1466 {xdr_nfslog_COMMIT3args, xdr_nfslog_COMMIT3res, FALSE}, 1467 }; 1468 1469 static struct nfslog_proc_disp nfslog_proc_v1[] = { 1470 /* 1471 * NFSLOG VERSION 1 1472 */ 1473 1474 /* NFSLOG_NULL = 0 */ 1475 {xdr_void, xdr_void, TRUE}, 1476 1477 /* NFSLOG_SHARE = 1 */ 1478 {xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres, TRUE}, 1479 1480 /* NFSLOG_UNSHARE = 2 */ 1481 {xdr_nfslog_sharefsargs, xdr_nfslog_sharefsres, TRUE}, 1482 1483 /* NFSLOG_LOOKUP = 3 */ 1484 {xdr_nfslog_diropargs3, xdr_nfslog_LOOKUP3res, TRUE}, 1485 1486 /* NFSLOG_GETFH = 4 */ 1487 {xdr_nfslog_getfhargs, xdr_nfsstat, TRUE}, 1488 }; 1489 1490 static struct nfslog_vers_disp nfslog_vers_disptable[] = { 1491 {sizeof (nfslog_proc_v2) / sizeof (nfslog_proc_v2[0]), 1492 nfslog_proc_v2}, 1493 {sizeof (nfslog_proc_v3) / sizeof (nfslog_proc_v3[0]), 1494 nfslog_proc_v3}, 1495 }; 1496 1497 static struct nfslog_vers_disp nfslog_nfslog_vers_disptable[] = { 1498 {sizeof (nfslog_proc_v1) / sizeof (nfslog_proc_v1[0]), 1499 nfslog_proc_v1}, 1500 }; 1501 1502 static struct nfslog_prog_disp nfslog_dispatch_table[] = { 1503 {NFS_PROGRAM, NFS_VERSMIN, 1504 (sizeof (nfslog_vers_disptable) / 1505 sizeof (nfslog_vers_disptable[0])), 1506 nfslog_vers_disptable}, 1507 1508 {NFSLOG_PROGRAM, NFSLOG_VERSMIN, 1509 (sizeof (nfslog_nfslog_vers_disptable) / 1510 sizeof (nfslog_nfslog_vers_disptable[0])), 1511 nfslog_nfslog_vers_disptable}, 1512 }; 1513 1514 static int nfslog_dispatch_table_arglen = sizeof (nfslog_dispatch_table) / 1515 sizeof (nfslog_dispatch_table[0]); 1516 1517 /* 1518 * This function will determine the appropriate export info struct to use 1519 * and allocate a record id to be used in the written log buffer. 1520 * Usually this is a straightforward operation but the existence of the 1521 * multicomponent lookup and its semantics of crossing file system 1522 * boundaries add to the complexity. See the comments below... 1523 */ 1524 struct exportinfo * 1525 nfslog_get_exi( 1526 struct exportinfo *exi, 1527 struct svc_req *req, 1528 caddr_t res, 1529 unsigned int *nfslog_rec_id) 1530 { 1531 struct log_buffer *lb; 1532 struct exportinfo *exi_ret = NULL; 1533 fhandle_t *fh = NULL; 1534 nfs_fh3 *fh3; 1535 1536 if (exi == NULL) 1537 return (NULL); 1538 1539 /* 1540 * If the exi is marked for logging, allocate a record id and return 1541 */ 1542 if (exi->exi_export.ex_flags & EX_LOG) { 1543 lb = exi->exi_logbuffer; 1544 1545 /* obtain the unique record id for the caller */ 1546 *nfslog_rec_id = atomic_add_32_nv(&lb->lb_rec_id, (int32_t)1); 1547 1548 /* 1549 * The caller will expect to be able to exi_rele() it, 1550 * so exi->exi_count must be incremented before it can 1551 * be returned, to make it uniform with exi_ret->exi_count 1552 */ 1553 mutex_enter(&exi->exi_lock); 1554 exi->exi_count++; 1555 mutex_exit(&exi->exi_lock); 1556 1557 return (exi); 1558 } 1559 1560 if (exi != exi_public) 1561 return (NULL); 1562 1563 /* 1564 * Here we have an exi that is not marked for logging. 1565 * It is possible that this request is a multicomponent lookup 1566 * that was done from the public file handle (not logged) and 1567 * the resulting file handle being returned to the client exists 1568 * in a file system that is being logged. If this is the case 1569 * we need to log this multicomponent lookup to the appropriate 1570 * log buffer. This will allow for the appropriate path name 1571 * mapping to occur at user level. 1572 */ 1573 if (req->rq_prog == NFS_PROGRAM) { 1574 switch (req->rq_vers) { 1575 case NFS_V3: 1576 if ((req->rq_proc == NFSPROC3_LOOKUP) && 1577 (((LOOKUP3res *)res)->status == NFS3_OK)) { 1578 fh3 = &((LOOKUP3res *)res)->res_u.ok.object; 1579 if (fh3->fh3_length == sizeof (fhandle_t)) 1580 fh = &fh3->fh3_u.nfs_fh3_i.fh3_i; 1581 } 1582 break; 1583 1584 case NFS_VERSION: 1585 if ((req->rq_proc == RFS_LOOKUP) && 1586 (((struct nfsdiropres *) 1587 res)->dr_status == NFS_OK)) { 1588 fh = 1589 &((struct nfsdiropres *)res)->dr_u.dr_drok_u.drok_fhandle; 1590 } 1591 break; 1592 default: 1593 break; 1594 } 1595 if (fh != NULL) { 1596 exi_ret = checkexport(&fh->fh_fsid, 1597 (fid_t *)&fh->fh_xlen); 1598 } 1599 } 1600 1601 if (exi_ret != NULL && exi_ret->exi_export.ex_flags & EX_LOG) { 1602 lb = exi_ret->exi_logbuffer; 1603 /* obtain the unique record id for the caller */ 1604 *nfslog_rec_id = atomic_add_32_nv(&lb->lb_rec_id, (int32_t)1); 1605 1606 return (exi_ret); 1607 } 1608 return (NULL); 1609 } 1610 1611 #ifdef DEBUG 1612 static long long rfslog_records_ignored = 0; 1613 #endif 1614 1615 /* 1616 * nfslog_write_record - Fill in the record buffer for writing out. 1617 * If logrecp is null, log it, otherwise, malloc the record and return it. 1618 * 1619 * It is the responsibility of the caller to check whether this exportinfo 1620 * has logging enabled. 1621 * Note that nfslog_share_public_record() only needs to check for the 1622 * existence of at least one logbuffer to which the public filehandle record 1623 * needs to be logged. 1624 */ 1625 void 1626 nfslog_write_record(struct exportinfo *exi, struct svc_req *req, 1627 caddr_t args, caddr_t res, cred_t *cr, struct netbuf *pnb, 1628 unsigned int record_id, unsigned int which_buffers) 1629 { 1630 struct nfslog_prog_disp *progtable; /* prog struct */ 1631 struct nfslog_vers_disp *verstable; /* version struct */ 1632 struct nfslog_proc_disp *disp = NULL; /* proc struct */ 1633 int i, vers; 1634 void *log_cookie; /* for logrecord if */ 1635 caddr_t buffer; 1636 XDR xdrs; 1637 unsigned int final_size; 1638 int encode_ok; 1639 int alloc_indx; 1640 1641 ASSERT(exi != NULL); ASSERT(req != NULL); ASSERT(args != NULL); 1642 ASSERT(res != NULL); ASSERT(cr != NULL); 1643 1644 /* 1645 * Find program element 1646 * Search the list since program can not be used as index 1647 */ 1648 for (i = 0; (i < nfslog_dispatch_table_arglen); i++) { 1649 if (req->rq_prog == nfslog_dispatch_table[i].nfslog_dis_prog) 1650 break; 1651 } 1652 if (i >= nfslog_dispatch_table_arglen) { /* program not logged */ 1653 /* not an error */ 1654 return; 1655 } 1656 1657 /* 1658 * Extract the dispatch functions based on program/version 1659 */ 1660 progtable = &nfslog_dispatch_table[i]; 1661 vers = req->rq_vers - progtable->nfslog_dis_versmin; 1662 verstable = &progtable->nfslog_dis_vers_table[vers]; 1663 disp = &verstable->nfslog_dis_proc_table[req->rq_proc]; 1664 1665 if (!(exi->exi_export.ex_flags & EX_LOG_ALLOPS) && 1666 !disp->affects_transactions) { 1667 /* 1668 * Only interested in logging operations affecting 1669 * transaction generation. This is not one of them. 1670 */ 1671 #ifdef DEBUG 1672 rfslog_records_ignored++; 1673 #endif 1674 return; 1675 } 1676 1677 switch (req->rq_prog) { 1678 case NFS_PROGRAM: 1679 switch (req->rq_vers) { 1680 case NFS_V3: 1681 switch (req->rq_proc) { 1682 case NFSPROC3_READDIRPLUS: 1683 alloc_indx = MEDIUM_INDX; 1684 break; 1685 default: 1686 alloc_indx = SMALL_INDX; 1687 break; 1688 } 1689 break; 1690 default: 1691 alloc_indx = SMALL_INDX; 1692 break; 1693 } 1694 break; 1695 case NFSLOG_PROGRAM: 1696 alloc_indx = MEDIUM_INDX; 1697 break; 1698 default: 1699 alloc_indx = SMALL_INDX; 1700 break; 1701 } 1702 1703 do { 1704 encode_ok = FALSE; 1705 1706 /* Pick the size to alloc; end of the road - return */ 1707 if (nfslog_mem_alloc[alloc_indx].size == (-1)) { 1708 cmn_err(CE_WARN, 1709 "NFSLOG: unable to encode record - prog=%d " 1710 "proc = %d", req->rq_prog, req->rq_proc); 1711 return; 1712 } 1713 1714 buffer = nfslog_record_alloc(exi, alloc_indx, &log_cookie, 0); 1715 if (buffer == NULL) { 1716 /* Error processing - no space alloced */ 1717 rfs_log_bad++; 1718 cmn_err(CE_WARN, "NFSLOG: can't get record"); 1719 return; 1720 } 1721 1722 xdrmem_create(&xdrs, buffer, 1723 nfslog_mem_alloc[alloc_indx].size, XDR_ENCODE); 1724 1725 /* 1726 * Encode the header, args and results of the record 1727 */ 1728 if (xdr_nfslog_request_record(&xdrs, exi, req, cr, pnb, 1729 nfslog_mem_alloc[alloc_indx].size, record_id) && 1730 (*disp->xdrargs)(&xdrs, args) && 1731 (*disp->xdrres)(&xdrs, res)) { 1732 encode_ok = TRUE; 1733 1734 rfs_log_good++; 1735 /* 1736 * Get the final size of the encoded 1737 * data and insert that length at the 1738 * beginning. 1739 */ 1740 final_size = xdr_getpos(&xdrs); 1741 xdr_setpos(&xdrs, 0); 1742 (void) xdr_u_int(&xdrs, &final_size); 1743 } else { 1744 /* Oops, the encode failed so we need to free memory */ 1745 nfslog_record_put(log_cookie, 0, FALSE, which_buffers); 1746 alloc_indx++; 1747 } 1748 1749 } while (encode_ok == FALSE); 1750 1751 1752 /* 1753 * Take the final log record and put it in the log file. 1754 * This may be queued to the file internally and written 1755 * later unless the last parameter is TRUE. 1756 * If the record_id is 0 then this is most likely a share/unshare 1757 * request and it should be written synchronously to the log file. 1758 */ 1759 nfslog_record_put(log_cookie, final_size, 1760 (record_id == 0), which_buffers); 1761 } 1762 1763 static char * 1764 get_publicfh_path(int *alloc_length) 1765 { 1766 extern struct exportinfo *exi_public; 1767 char *pubpath; 1768 1769 rw_enter(&exported_lock, RW_READER); 1770 1771 *alloc_length = exi_public->exi_export.ex_pathlen + 1; 1772 pubpath = kmem_alloc(*alloc_length, KM_SLEEP); 1773 1774 (void) strcpy(pubpath, exi_public->exi_export.ex_path); 1775 1776 rw_exit(&exported_lock); 1777 1778 return (pubpath); 1779 } 1780 1781 static void 1782 log_public_record(struct exportinfo *exi, cred_t *cr) 1783 { 1784 struct svc_req req; 1785 struct netbuf nb = {0, 0, NULL}; 1786 int free_length = 0; 1787 diropargs3 args; 1788 LOOKUP3res res; 1789 1790 bzero(&req, sizeof (req)); 1791 req.rq_prog = NFSLOG_PROGRAM; 1792 req.rq_vers = NFSLOG_VERSION; 1793 req.rq_proc = NFSLOG_LOOKUP; 1794 req.rq_cred.oa_flavor = AUTH_NONE; 1795 1796 bzero(&args, sizeof (diropargs3)); 1797 bzero(&res, sizeof (LOOKUP3res)); 1798 1799 args.dir.fh3_length = 0; 1800 if ((args.name = get_publicfh_path(&free_length)) == NULL) 1801 return; 1802 args.dirp = &args.dir; 1803 1804 res.status = NFS3_OK; 1805 res.res_u.ok.object.fh3_length = 0; 1806 1807 /* 1808 * Calling this function with the exi_public 1809 * will have the effect of appending the record 1810 * to each of the open log buffers 1811 */ 1812 nfslog_write_record(exi, &req, 1813 (caddr_t)&args, (caddr_t)&res, cr, &nb, 0, NFSLOG_ALL_BUFFERS); 1814 1815 kmem_free(args.name, free_length); 1816 } 1817 1818 /* 1819 * nfslog_share_record - logs a share request. 1820 * This is not an NFS request, but we pretend here... 1821 */ 1822 void 1823 nfslog_share_record(struct exportinfo *exi, cred_t *cr) 1824 { 1825 struct svc_req req; 1826 int res = 0; 1827 struct netbuf nb = {0, 0, NULL}; 1828 1829 ASSERT(exi != NULL); 1830 1831 if (nfslog_buffer_list == NULL) 1832 return; 1833 1834 if (exi->exi_export.ex_flags & EX_LOG) { 1835 bzero(&req, sizeof (req)); 1836 req.rq_prog = NFSLOG_PROGRAM; 1837 req.rq_vers = NFSLOG_VERSION; 1838 req.rq_proc = NFSLOG_SHARE; 1839 req.rq_cred.oa_flavor = AUTH_NONE; 1840 nfslog_write_record(exi, &req, 1841 (caddr_t)exi, (caddr_t)&res, cr, 1842 &nb, 0, NFSLOG_ONE_BUFFER); 1843 } 1844 1845 log_public_record(exi, cr); 1846 } 1847 1848 /* 1849 * nfslog_unshare_record - logs an unshare request. 1850 * This is not an NFS request, but we pretend here... 1851 */ 1852 void 1853 nfslog_unshare_record(struct exportinfo *exi, cred_t *cr) 1854 { 1855 struct svc_req req; 1856 int res = 0; 1857 struct netbuf nb = {0, 0, NULL}; 1858 1859 ASSERT(exi != NULL); 1860 ASSERT(exi->exi_export.ex_flags & EX_LOG); 1861 1862 bzero(&req, sizeof (req)); 1863 req.rq_prog = NFSLOG_PROGRAM; 1864 req.rq_vers = NFSLOG_VERSION; 1865 req.rq_proc = NFSLOG_UNSHARE; 1866 req.rq_cred.oa_flavor = AUTH_NONE; 1867 nfslog_write_record(exi, &req, 1868 (caddr_t)exi, (caddr_t)&res, cr, &nb, 0, NFSLOG_ONE_BUFFER); 1869 } 1870 1871 1872 void 1873 nfslog_getfh(struct exportinfo *exi, 1874 fhandle *fh, 1875 char *fname, 1876 enum uio_seg seg, 1877 cred_t *cr) 1878 { 1879 struct svc_req req; 1880 int res = 0; 1881 struct netbuf nb = {0, 0, NULL}; 1882 int error = 0; 1883 char *namebuf; 1884 size_t len; 1885 nfslog_getfhargs gfh; 1886 1887 ASSERT(exi != NULL); 1888 ASSERT(exi->exi_export.ex_flags & EX_LOG); 1889 1890 bzero(&req, sizeof (req)); 1891 req.rq_prog = NFSLOG_PROGRAM; 1892 req.rq_vers = NFSLOG_VERSION; 1893 req.rq_proc = NFSLOG_GETFH; 1894 req.rq_cred.oa_flavor = AUTH_NONE; 1895 1896 namebuf = kmem_alloc(MAXPATHLEN + 4, KM_SLEEP); 1897 if (seg == UIO_USERSPACE) { 1898 error = copyinstr(fname, namebuf, MAXPATHLEN, &len); 1899 } else { 1900 error = copystr(fname, namebuf, MAXPATHLEN, &len); 1901 } 1902 1903 if (!error) { 1904 gfh.gfh_fh_buf = *fh; 1905 gfh.gfh_path = namebuf; 1906 1907 nfslog_write_record(exi, &req, 1908 (caddr_t)&gfh, (caddr_t)&res, cr, &nb, 0, 1909 NFSLOG_ONE_BUFFER); 1910 } 1911 kmem_free(namebuf, MAXPATHLEN + 4); 1912 } 1913