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