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