1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2016 Nexenta Systems, Inc. All rights reserved. 14 */ 15 16 17 #include <smbsrv/smb2_kproto.h> 18 #include <smbsrv/smb_kstat.h> 19 #include <smbsrv/smb2.h> 20 21 /* 22 * Saved state for a command that "goes async". When a compound request 23 * contains a command that may block indefinitely, the compound reply is 24 * composed with an "interim response" for that command, and information 25 * needed to actually dispatch that command is saved on a list of "async" 26 * commands for this compound request. After the compound reply is sent, 27 * the list of async commands is processed, and those may block as long 28 * as they need to without affecting the initial compound request. 29 * 30 * Now interestingly, this "async" mechanism is not used with the full 31 * range of asynchrony that one might imagine. The design of async 32 * request processing can be drastically simplified if we can assume 33 * that there's no need to run more than one async command at a time. 34 * With that simplifying assumption, we can continue using the current 35 * "one worker thread per request message" model, which has very simple 36 * locking rules etc. The same worker thread that handles the initial 37 * compound request can handle the list of async requests. 38 * 39 * As it turns out, SMB2 clients do not try to use more than one "async" 40 * command in a compound. If they were to do so, the [MS-SMB2] spec. 41 * allows us to decline additional async requests with an error. 42 * 43 * smb_async_req_t is the struct used to save an "async" request on 44 * the list of requests that had an interim reply in the initial 45 * compound reply. This includes everything needed to restart 46 * processing at the async command. 47 */ 48 49 typedef struct smb2_async_req { 50 51 smb_sdrc_t (*ar_func)(smb_request_t *); 52 53 int ar_cmd_hdr; /* smb2_cmd_hdr offset */ 54 int ar_cmd_len; /* length from hdr */ 55 56 /* 57 * SMB2 header fields. 58 */ 59 uint16_t ar_cmd_code; 60 uint16_t ar_uid; 61 uint16_t ar_tid; 62 uint32_t ar_pid; 63 uint32_t ar_hdr_flags; 64 uint64_t ar_messageid; 65 } smb2_async_req_t; 66 67 void smb2sr_do_async(smb_request_t *); 68 smb_sdrc_t smb2_invalid_cmd(smb_request_t *); 69 static void smb2_tq_work(void *); 70 71 static const smb_disp_entry_t 72 smb2_disp_table[SMB2__NCMDS] = { 73 74 /* text-name, pre, func, post, cmd-code, dialect, flags */ 75 76 { "smb2_negotiate", NULL, 77 smb2_negotiate, NULL, 0, 0, 78 SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID }, 79 80 { "smb2_session_setup", NULL, 81 smb2_session_setup, NULL, 0, 0, 82 SDDF_SUPPRESS_TID | SDDF_SUPPRESS_UID }, 83 84 { "smb2_logoff", NULL, 85 smb2_logoff, NULL, 0, 0, 86 SDDF_SUPPRESS_TID }, 87 88 { "smb2_tree_connect", NULL, 89 smb2_tree_connect, NULL, 0, 0, 90 SDDF_SUPPRESS_TID }, 91 92 { "smb2_tree_disconn", NULL, 93 smb2_tree_disconn, NULL, 0, 0 }, 94 95 { "smb2_create", NULL, 96 smb2_create, NULL, 0, 0 }, 97 98 { "smb2_close", NULL, 99 smb2_close, NULL, 0, 0 }, 100 101 { "smb2_flush", NULL, 102 smb2_flush, NULL, 0, 0 }, 103 104 { "smb2_read", NULL, 105 smb2_read, NULL, 0, 0 }, 106 107 { "smb2_write", NULL, 108 smb2_write, NULL, 0, 0 }, 109 110 { "smb2_lock", NULL, 111 smb2_lock, NULL, 0, 0 }, 112 113 { "smb2_ioctl", NULL, 114 smb2_ioctl, NULL, 0, 0 }, 115 116 { "smb2_cancel", NULL, 117 smb2_cancel, NULL, 0, 0, 118 SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID }, 119 120 { "smb2_echo", NULL, 121 smb2_echo, NULL, 0, 0, 122 SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID }, 123 124 { "smb2_query_dir", NULL, 125 smb2_query_dir, NULL, 0, 0 }, 126 127 { "smb2_change_notify", NULL, 128 smb2_change_notify, NULL, 0, 0 }, 129 130 { "smb2_query_info", NULL, 131 smb2_query_info, NULL, 0, 0 }, 132 133 { "smb2_set_info", NULL, 134 smb2_set_info, NULL, 0, 0 }, 135 136 { "smb2_oplock_break_ack", NULL, 137 smb2_oplock_break_ack, NULL, 0, 0 }, 138 139 { "smb2_invalid_cmd", NULL, 140 smb2_invalid_cmd, NULL, 0, 0, 141 SDDF_SUPPRESS_UID | SDDF_SUPPRESS_TID }, 142 }; 143 144 smb_sdrc_t 145 smb2_invalid_cmd(smb_request_t *sr) 146 { 147 #ifdef DEBUG 148 cmn_err(CE_NOTE, "clnt %s bad SMB2 cmd code", 149 sr->session->ip_addr_str); 150 #endif 151 sr->smb2_status = NT_STATUS_INVALID_PARAMETER; 152 return (SDRC_DROP_VC); 153 } 154 155 /* 156 * This is the SMB2 handler for new smb requests, called from 157 * smb_session_reader after SMB negotiate is done. For most SMB2 158 * requests, we just enqueue them for the smb_session_worker to 159 * execute via the task queue, so they can block for resources 160 * without stopping the reader thread. A few protocol messages 161 * are special cases and are handled directly here in the reader 162 * thread so they don't wait for taskq scheduling. 163 * 164 * This function must either enqueue the new request for 165 * execution via the task queue, or execute it directly 166 * and then free it. If this returns non-zero, the caller 167 * will drop the session. 168 */ 169 int 170 smb2sr_newrq(smb_request_t *sr) 171 { 172 struct mbuf_chain *mbc = &sr->command; 173 uint32_t magic; 174 int rc, skip; 175 176 if (smb_mbc_peek(mbc, 0, "l", &magic) != 0) 177 goto drop; 178 179 if (magic != SMB2_PROTOCOL_MAGIC) 180 goto drop; 181 182 /* 183 * Walk the SMB2 commands in this compound message and 184 * keep track of the range of message IDs it uses. 185 */ 186 for (;;) { 187 if (smb2_decode_header(sr) != 0) 188 goto drop; 189 190 /* 191 * Cancel requests are special: They refer to 192 * an earlier message ID (or an async. ID), 193 * never a new ID, and are never compounded. 194 * This is intentionally not "goto drop" 195 * because rc may be zero (success). 196 */ 197 if (sr->smb2_cmd_code == SMB2_CANCEL) { 198 rc = smb2_newrq_cancel(sr); 199 smb_request_free(sr); 200 return (rc); 201 } 202 203 /* 204 * Keep track of the total credits in this compound 205 * and the first (real) message ID (not: 0, -1) 206 * While we're looking, verify that all (real) IDs 207 * are (first <= ID < (first + msg_credits)) 208 */ 209 if (sr->smb2_credit_charge == 0) 210 sr->smb2_credit_charge = 1; 211 sr->smb2_total_credits += sr->smb2_credit_charge; 212 213 if (sr->smb2_messageid != 0 && 214 sr->smb2_messageid != UINT64_MAX) { 215 216 if (sr->smb2_first_msgid == 0) 217 sr->smb2_first_msgid = sr->smb2_messageid; 218 219 if (sr->smb2_messageid < sr->smb2_first_msgid || 220 sr->smb2_messageid >= (sr->smb2_first_msgid + 221 sr->smb2_total_credits)) { 222 long long id = (long long) sr->smb2_messageid; 223 cmn_err(CE_WARN, "clnt %s msg ID 0x%llx " 224 "out of sequence in compound", 225 sr->session->ip_addr_str, id); 226 } 227 } 228 229 /* Normal loop exit on next == zero */ 230 if (sr->smb2_next_command == 0) 231 break; 232 233 /* Abundance of caution... */ 234 if (sr->smb2_next_command < SMB2_HDR_SIZE) 235 goto drop; 236 237 /* Advance to the next header. */ 238 skip = sr->smb2_next_command - SMB2_HDR_SIZE; 239 if (MBC_ROOM_FOR(mbc, skip) == 0) 240 goto drop; 241 mbc->chain_offset += skip; 242 } 243 /* Rewind back to the top. */ 244 mbc->chain_offset = 0; 245 246 /* 247 * Submit the request to the task queue, which calls 248 * smb2_tq_work when the workload permits. 249 */ 250 sr->sr_time_submitted = gethrtime(); 251 sr->sr_state = SMB_REQ_STATE_SUBMITTED; 252 smb_srqueue_waitq_enter(sr->session->s_srqueue); 253 (void) taskq_dispatch(sr->sr_server->sv_worker_pool, 254 smb2_tq_work, sr, TQ_SLEEP); 255 return (0); 256 257 drop: 258 smb_request_free(sr); 259 return (-1); 260 } 261 262 static void 263 smb2_tq_work(void *arg) 264 { 265 smb_request_t *sr; 266 smb_srqueue_t *srq; 267 268 sr = (smb_request_t *)arg; 269 SMB_REQ_VALID(sr); 270 271 srq = sr->session->s_srqueue; 272 smb_srqueue_waitq_to_runq(srq); 273 sr->sr_worker = curthread; 274 sr->sr_time_active = gethrtime(); 275 276 /* 277 * Always dispatch to the work function, because cancelled 278 * requests need an error reply (NT_STATUS_CANCELLED). 279 */ 280 mutex_enter(&sr->sr_mutex); 281 if (sr->sr_state == SMB_REQ_STATE_SUBMITTED) 282 sr->sr_state = SMB_REQ_STATE_ACTIVE; 283 mutex_exit(&sr->sr_mutex); 284 285 smb2sr_work(sr); 286 287 smb_srqueue_runq_exit(srq); 288 } 289 290 /* 291 * smb2sr_work 292 * 293 * This function processes each SMB command in the current request 294 * (which may be a compound request) building a reply containing 295 * SMB reply messages, one-to-one with the SMB commands. Some SMB 296 * commands (change notify, blocking locks) may require both an 297 * "interim response" and a later "async response" at completion. 298 * In such cases, we'll encode the interim response in the reply 299 * compound we're building, and put the (now async) command on a 300 * list of commands that need further processing. After we've 301 * finished processing the commands in this compound and building 302 * the compound reply, we'll send the compound reply, and finally 303 * process the list of async commands. 304 * 305 * As we work our way through the compound request and reply, 306 * we need to keep track of the bounds of the current request 307 * and reply. For the request, this uses an MBC_SHADOW_CHAIN 308 * that begins at smb2_cmd_hdr. The reply is appended to the 309 * sr->reply chain starting at smb2_reply_hdr. 310 * 311 * This function must always free the smb request, or arrange 312 * for it to be completed and free'd later (if SDRC_SR_KEPT). 313 */ 314 void 315 smb2sr_work(struct smb_request *sr) 316 { 317 const smb_disp_entry_t *sdd; 318 smb_disp_stats_t *sds; 319 smb_session_t *session; 320 uint32_t msg_len; 321 uint16_t cmd_idx; 322 int rc = 0; 323 boolean_t disconnect = B_FALSE; 324 boolean_t related; 325 326 session = sr->session; 327 328 ASSERT(sr->tid_tree == 0); 329 ASSERT(sr->uid_user == 0); 330 ASSERT(sr->fid_ofile == 0); 331 sr->smb_fid = (uint16_t)-1; 332 sr->smb2_status = 0; 333 334 /* temporary until we identify a user */ 335 sr->user_cr = zone_kcred(); 336 337 cmd_start: 338 /* 339 * Note that we don't check sr_state here and abort the 340 * compound if cancelled (etc.) because some SMB2 command 341 * handlers need to do work even when cancelled. 342 * 343 * We treat some status codes as if "sticky", meaning 344 * once they're set after some command handler returns, 345 * all remaining commands get this status without even 346 * calling the command-specific handler. 347 */ 348 if (sr->smb2_status != NT_STATUS_CANCELLED && 349 sr->smb2_status != NT_STATUS_INSUFFICIENT_RESOURCES) 350 sr->smb2_status = 0; 351 352 /* 353 * Decode the request header 354 * 355 * Most problems with decoding will result in the error 356 * STATUS_INVALID_PARAMETER. If the decoding problem 357 * prevents continuing, we'll close the connection. 358 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted... 359 */ 360 sr->smb2_cmd_hdr = sr->command.chain_offset; 361 if ((rc = smb2_decode_header(sr)) != 0) { 362 cmn_err(CE_WARN, "clnt %s bad SMB2 header", 363 session->ip_addr_str); 364 disconnect = B_TRUE; 365 goto cleanup; 366 } 367 368 /* 369 * The SMB2_FLAGS_SERVER_TO_REDIR should only appear 370 * in messages from the server back to the client. 371 */ 372 if ((sr->smb2_hdr_flags & SMB2_FLAGS_SERVER_TO_REDIR) != 0) { 373 cmn_err(CE_WARN, "clnt %s bad SMB2 flags", 374 session->ip_addr_str); 375 disconnect = B_TRUE; 376 goto cleanup; 377 } 378 related = (sr->smb2_hdr_flags & SMB2_FLAGS_RELATED_OPERATIONS); 379 380 /* 381 * In case we bail out with an error before we get to the 382 * section that computes the credit grant, initialize the 383 * response header fields so that credits won't change. 384 * Note: SMB 2.02 clients may send credit charge zero. 385 */ 386 if (sr->smb2_credit_charge == 0) 387 sr->smb2_credit_charge = 1; 388 sr->smb2_credit_response = sr->smb2_credit_charge; 389 390 /* 391 * Reserve space for the reply header, and save the offset. 392 * The reply header will be overwritten later. If we have 393 * already exhausted the output space, then this client is 394 * trying something funny. Log it and kill 'em. 395 */ 396 sr->smb2_reply_hdr = sr->reply.chain_offset; 397 if ((rc = smb2_encode_header(sr, B_FALSE)) != 0) { 398 cmn_err(CE_WARN, "clnt %s excessive reply", 399 session->ip_addr_str); 400 disconnect = B_TRUE; 401 goto cleanup; 402 } 403 404 /* 405 * Figure out the length of data following the SMB2 header. 406 * It ends at either the next SMB2 header if there is one 407 * (smb2_next_command != 0) or at the end of the message. 408 */ 409 if (sr->smb2_next_command != 0) { 410 /* [MS-SMB2] says this is 8-byte aligned */ 411 msg_len = sr->smb2_next_command; 412 if ((msg_len & 7) != 0 || (msg_len < SMB2_HDR_SIZE) || 413 ((sr->smb2_cmd_hdr + msg_len) > sr->command.max_bytes)) { 414 cmn_err(CE_WARN, "clnt %s bad SMB2 next cmd", 415 session->ip_addr_str); 416 disconnect = B_TRUE; 417 goto cleanup; 418 } 419 } else { 420 msg_len = sr->command.max_bytes - sr->smb2_cmd_hdr; 421 } 422 423 /* 424 * Setup a shadow chain for this SMB2 command, starting 425 * with the header and ending at either the next command 426 * or the end of the message. The signing check below 427 * needs the entire SMB2 command. After that's done, we 428 * advance chain_offset to the end of the header where 429 * the command specific handlers continue decoding. 430 */ 431 (void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command, 432 sr->smb2_cmd_hdr, msg_len); 433 434 /* 435 * We will consume the data for this request from smb_data. 436 * That effectively consumes msg_len bytes from sr->command 437 * but doesn't update its chain_offset, so we need to update 438 * that here to make later received bytes accounting work. 439 */ 440 sr->command.chain_offset = sr->smb2_cmd_hdr + msg_len; 441 ASSERT(sr->command.chain_offset <= sr->command.max_bytes); 442 443 /* 444 * Validate the commmand code, get dispatch table entries. 445 * [MS-SMB2] 3.3.5.2.6 Handling Incorrectly Formatted... 446 * 447 * The last slot in the dispatch table is used to handle 448 * invalid commands. Same for statistics. 449 */ 450 if (sr->smb2_cmd_code < SMB2_INVALID_CMD) 451 cmd_idx = sr->smb2_cmd_code; 452 else 453 cmd_idx = SMB2_INVALID_CMD; 454 sdd = &smb2_disp_table[cmd_idx]; 455 sds = &session->s_server->sv_disp_stats2[cmd_idx]; 456 457 /* 458 * If this command is NOT "related" to the previous, 459 * clear out the UID, TID, FID state that might be 460 * left over from the previous command. 461 * 462 * If the command IS related, any new IDs are ignored, 463 * and we simply continue with the previous user, tree, 464 * and open file. 465 */ 466 if (!related) { 467 /* 468 * Drop user, tree, file; carefully ordered to 469 * avoid dangling references: file, tree, user 470 */ 471 if (sr->fid_ofile != NULL) { 472 smb_ofile_request_complete(sr->fid_ofile); 473 smb_ofile_release(sr->fid_ofile); 474 sr->fid_ofile = NULL; 475 } 476 if (sr->tid_tree != NULL) { 477 smb_tree_release(sr->tid_tree); 478 sr->tid_tree = NULL; 479 } 480 if (sr->uid_user != NULL) { 481 smb_user_release(sr->uid_user); 482 sr->uid_user = NULL; 483 sr->user_cr = zone_kcred(); 484 } 485 } 486 487 /* 488 * Make sure we have a user and tree as needed 489 * according to the flags for the this command. 490 * Note that we may have inherited these. 491 */ 492 if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0) { 493 /* 494 * This command requires a user session. 495 */ 496 if (related) { 497 /* 498 * Previous command should have given us a user. 499 * [MS-SMB2] 3.3.5.2 Handling Related Requests 500 */ 501 if (sr->uid_user == NULL) { 502 smb2sr_put_error(sr, 503 NT_STATUS_INVALID_PARAMETER); 504 goto cmd_done; 505 } 506 sr->smb_uid = sr->uid_user->u_uid; 507 } else { 508 /* 509 * Lookup the UID 510 * [MS-SMB2] 3.3.5.2 Verifying the Session 511 */ 512 ASSERT(sr->uid_user == NULL); 513 sr->uid_user = smb_session_lookup_uid(session, 514 sr->smb_uid); 515 if (sr->uid_user == NULL) { 516 smb2sr_put_error(sr, 517 NT_STATUS_USER_SESSION_DELETED); 518 goto cmd_done; 519 } 520 sr->user_cr = smb_user_getcred(sr->uid_user); 521 } 522 ASSERT(sr->uid_user != NULL); 523 } 524 525 if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0) { 526 /* 527 * This command requires a tree connection. 528 */ 529 if (related) { 530 /* 531 * Previous command should have given us a tree. 532 * [MS-SMB2] 3.3.5.2 Handling Related Requests 533 */ 534 if (sr->tid_tree == NULL) { 535 smb2sr_put_error(sr, 536 NT_STATUS_INVALID_PARAMETER); 537 goto cmd_done; 538 } 539 sr->smb_tid = sr->tid_tree->t_tid; 540 } else { 541 /* 542 * Lookup the TID 543 * [MS-SMB2] 3.3.5.2 Verifying the Tree Connect 544 */ 545 ASSERT(sr->tid_tree == NULL); 546 sr->tid_tree = smb_session_lookup_tree(session, 547 sr->smb_tid); 548 if (sr->tid_tree == NULL) { 549 smb2sr_put_error(sr, 550 NT_STATUS_NETWORK_NAME_DELETED); 551 goto cmd_done; 552 } 553 } 554 ASSERT(sr->tid_tree != NULL); 555 } 556 557 /* 558 * SMB2 signature verification, two parts: 559 * (a) Require SMB2_FLAGS_SIGNED (for most request types) 560 * (b) If SMB2_FLAGS_SIGNED is set, check the signature. 561 * [MS-SMB2] 3.3.5.2.4 Verifying the Signature 562 */ 563 564 /* 565 * No user session means no signature check. That's OK, 566 * i.e. for commands marked SDDF_SUPPRESS_UID above. 567 * Note, this also means we won't sign the reply. 568 */ 569 if (sr->uid_user == NULL) 570 sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED; 571 572 /* 573 * The SDDF_SUPPRESS_UID dispatch is set for requests that 574 * don't need a UID (user). These also don't require a 575 * signature check here. 576 */ 577 if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 && 578 sr->uid_user != NULL && 579 (sr->uid_user->u_sign_flags & SMB_SIGNING_CHECK) != 0) { 580 /* 581 * This request type should be signed, and 582 * we're configured to require signatures. 583 */ 584 if ((sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) == 0) { 585 smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED); 586 goto cmd_done; 587 } 588 rc = smb2_sign_check_request(sr); 589 if (rc != 0) { 590 DTRACE_PROBE1(smb2__sign__check, smb_request_t, sr); 591 smb2sr_put_error(sr, NT_STATUS_ACCESS_DENIED); 592 goto cmd_done; 593 } 594 } 595 596 /* 597 * Now that the signing check is done with smb_data, 598 * advance past the SMB2 header we decoded earlier. 599 * This leaves sr->smb_data correctly positioned 600 * for command-specific decoding in the dispatch 601 * function called next. 602 */ 603 sr->smb_data.chain_offset = sr->smb2_cmd_hdr + SMB2_HDR_SIZE; 604 605 /* 606 * SMB2 credits determine how many simultaneous commands the 607 * client may issue, and bounds the range of message IDs those 608 * commands may use. With multi-credit support, commands may 609 * use ranges of message IDs, where the credits used by each 610 * command are proportional to their data transfer size. 611 * 612 * Every command may request an increase or decrease of 613 * the currently granted credits, based on the difference 614 * between the credit request and the credit charge. 615 * [MS-SMB2] 3.3.1.2 Algorithm for the Granting of Credits 616 * 617 * Most commands have credit_request=1, credit_charge=1, 618 * which keeps the credit grant unchanged. 619 * 620 * All we're really doing here (for now) is reducing the 621 * credit_response if the client requests a credit increase 622 * that would take their credit over the maximum, and 623 * limiting the decrease so they don't run out of credits. 624 * 625 * Later, this could do something dynamic based on load. 626 * 627 * One other non-obvious bit about credits: We keep the 628 * session s_max_credits low until the 1st authentication, 629 * at which point we'll set the normal maximum_credits. 630 * Some clients ask for more credits with session setup, 631 * and we need to handle that requested increase _after_ 632 * the command-specific handler returns so it won't be 633 * restricted to the lower (pre-auth) limit. 634 */ 635 sr->smb2_credit_response = sr->smb2_credit_request; 636 if (sr->smb2_credit_request < sr->smb2_credit_charge) { 637 uint16_t cur, d; 638 639 mutex_enter(&session->s_credits_mutex); 640 cur = session->s_cur_credits; 641 642 /* Handle credit decrease. */ 643 d = sr->smb2_credit_charge - sr->smb2_credit_request; 644 cur -= d; 645 if (cur & 0x8000) { 646 /* 647 * underflow (bad credit charge or request) 648 * leave credits unchanged (response=charge) 649 */ 650 cur = session->s_cur_credits; 651 sr->smb2_credit_response = sr->smb2_credit_charge; 652 DTRACE_PROBE1(smb2__credit__neg, smb_request_t, sr); 653 } 654 655 /* 656 * The server MUST ensure that the number of credits 657 * held by the client is never reduced to zero. 658 * [MS-SMB2] 3.3.1.2 659 */ 660 if (cur == 0) { 661 cur = 1; 662 sr->smb2_credit_response += 1; 663 DTRACE_PROBE1(smb2__credit__min, smb_request_t, sr); 664 } 665 666 DTRACE_PROBE3(smb2__credit__decrease, 667 smb_request_t, sr, int, (int)cur, 668 int, (int)session->s_cur_credits); 669 670 session->s_cur_credits = cur; 671 mutex_exit(&session->s_credits_mutex); 672 } 673 674 /* 675 * The real work: call the SMB2 command handler 676 * (except for "sticky" smb2_status - see above) 677 */ 678 sr->sr_time_start = gethrtime(); 679 rc = SDRC_SUCCESS; 680 if (sr->smb2_status == 0) { 681 /* NB: not using pre_op */ 682 rc = (*sdd->sdt_function)(sr); 683 /* NB: not using post_op */ 684 } else { 685 smb2sr_put_error(sr, sr->smb2_status); 686 } 687 688 MBC_FLUSH(&sr->raw_data); 689 690 /* 691 * Second half of SMB2 credit handling (increases) 692 */ 693 if (sr->smb2_credit_request > sr->smb2_credit_charge) { 694 uint16_t cur, d; 695 696 mutex_enter(&session->s_credits_mutex); 697 cur = session->s_cur_credits; 698 699 /* Handle credit increase. */ 700 d = sr->smb2_credit_request - sr->smb2_credit_charge; 701 cur += d; 702 703 /* 704 * If new credits would be above max, 705 * reduce the credit grant. 706 */ 707 if (cur > session->s_max_credits) { 708 d = cur - session->s_max_credits; 709 cur = session->s_max_credits; 710 sr->smb2_credit_response -= d; 711 DTRACE_PROBE1(smb2__credit__max, smb_request_t, sr); 712 } 713 714 DTRACE_PROBE3(smb2__credit__increase, 715 smb_request_t, sr, int, (int)cur, 716 int, (int)session->s_cur_credits); 717 718 session->s_cur_credits = cur; 719 mutex_exit(&session->s_credits_mutex); 720 } 721 722 cmd_done: 723 /* 724 * Pad the reply to align(8) if necessary. 725 */ 726 if (sr->reply.chain_offset & 7) { 727 int padsz = 8 - (sr->reply.chain_offset & 7); 728 (void) smb_mbc_encodef(&sr->reply, "#.", padsz); 729 } 730 ASSERT((sr->reply.chain_offset & 7) == 0); 731 732 /* 733 * Record some statistics: latency, rx bytes, tx bytes. 734 */ 735 smb_server_inc_req(sr->sr_server); 736 smb_latency_add_sample(&sds->sdt_lat, 737 gethrtime() - sr->sr_time_start); 738 atomic_add_64(&sds->sdt_rxb, 739 (int64_t)(sr->command.chain_offset - sr->smb2_cmd_hdr)); 740 atomic_add_64(&sds->sdt_txb, 741 (int64_t)(sr->reply.chain_offset - sr->smb2_reply_hdr)); 742 743 switch (rc) { 744 case SDRC_SUCCESS: 745 break; 746 default: 747 /* 748 * SMB2 does not use the other dispatch return codes. 749 * If we see something else, log an event so we'll 750 * know something is returning bogus status codes. 751 * If you see these in the log, use dtrace to find 752 * the code returning something else. 753 */ 754 #ifdef DEBUG 755 cmn_err(CE_NOTE, "handler for %u returned 0x%x", 756 sr->smb2_cmd_code, rc); 757 #endif 758 sr->smb2_status = NT_STATUS_INTERNAL_ERROR; 759 break; 760 case SDRC_ERROR: 761 /* 762 * Many command handlers return SDRC_ERROR for any 763 * problems decoding the request, and don't bother 764 * setting smb2_status. For those cases, the best 765 * status return would be "invalid parameter". 766 */ 767 if (sr->smb2_status == 0) 768 sr->smb2_status = NT_STATUS_INVALID_PARAMETER; 769 break; 770 case SDRC_DROP_VC: 771 disconnect = B_TRUE; 772 goto cleanup; 773 774 case SDRC_NO_REPLY: 775 /* will free sr */ 776 goto cleanup; 777 } 778 779 /* 780 * If there's a next command, figure out where it starts, 781 * and fill in the next command offset for the reply. 782 * Note: We sanity checked smb2_next_command above 783 * (the offset to the next command). Similarly set 784 * smb2_next_reply as the offset to the next reply. 785 */ 786 if (sr->smb2_next_command != 0) { 787 sr->command.chain_offset = 788 sr->smb2_cmd_hdr + sr->smb2_next_command; 789 sr->smb2_next_reply = 790 sr->reply.chain_offset - sr->smb2_reply_hdr; 791 } else { 792 sr->smb2_next_reply = 0; 793 } 794 795 /* 796 * Overwrite the SMB2 header for the response of 797 * this command (possibly part of a compound). 798 * encode_header adds: SMB2_FLAGS_SERVER_TO_REDIR 799 */ 800 (void) smb2_encode_header(sr, B_TRUE); 801 802 if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) 803 smb2_sign_reply(sr); 804 805 if (sr->smb2_next_command != 0) 806 goto cmd_start; 807 808 /* 809 * We've done all the commands in this compound. 810 * Send it out. 811 */ 812 smb2_send_reply(sr); 813 814 /* 815 * If any of the requests "went async", process those now. 816 * The async. function "keeps" this sr, changing its state 817 * to completed and calling smb_request_free(). 818 */ 819 if (sr->sr_async_req != NULL) { 820 smb2sr_do_async(sr); 821 return; 822 } 823 824 cleanup: 825 if (disconnect) { 826 smb_rwx_rwenter(&session->s_lock, RW_WRITER); 827 switch (session->s_state) { 828 case SMB_SESSION_STATE_DISCONNECTED: 829 case SMB_SESSION_STATE_TERMINATED: 830 break; 831 default: 832 smb_soshutdown(session->sock); 833 session->s_state = SMB_SESSION_STATE_DISCONNECTED; 834 break; 835 } 836 smb_rwx_rwexit(&session->s_lock); 837 } 838 839 mutex_enter(&sr->sr_mutex); 840 sr->sr_state = SMB_REQ_STATE_COMPLETED; 841 mutex_exit(&sr->sr_mutex); 842 843 smb_request_free(sr); 844 } 845 846 /* 847 * Dispatch an async request using saved information. 848 * See smb2sr_save_async and [MS-SMB2] 3.3.4.2 849 * 850 * This is sort of a "lite" version of smb2sr_work. Initialize the 851 * command and reply areas as they were when the command-speicific 852 * handler started (in case it needs to decode anything again). 853 * Call the async function, which builds the command-specific part 854 * of the response. Finally, send the response and free the sr. 855 */ 856 void 857 smb2sr_do_async(smb_request_t *sr) 858 { 859 const smb_disp_entry_t *sdd; 860 smb2_async_req_t *ar; 861 smb_sdrc_t (*ar_func)(smb_request_t *); 862 int sdrc; 863 864 /* 865 * Restore what smb2_decode_header found. 866 * (In lieu of decoding it again.) 867 */ 868 ar = sr->sr_async_req; 869 sr->smb2_cmd_hdr = ar->ar_cmd_hdr; 870 sr->smb2_cmd_code = ar->ar_cmd_code; 871 sr->smb2_hdr_flags = ar->ar_hdr_flags; 872 sr->smb2_async_id = (uintptr_t)ar; 873 sr->smb2_messageid = ar->ar_messageid; 874 sr->smb_pid = ar->ar_pid; 875 sr->smb_tid = ar->ar_tid; 876 sr->smb_uid = ar->ar_uid; 877 sr->smb2_status = 0; 878 879 /* 880 * Async requests don't grant credits, because any credits 881 * should have gone out with the interim reply. 882 * An async reply goes alone (no next reply). 883 */ 884 sr->smb2_credit_response = 0; 885 sr->smb2_next_reply = 0; 886 887 /* 888 * Setup input mbuf_chain 889 */ 890 ASSERT(ar->ar_cmd_len >= SMB2_HDR_SIZE); 891 (void) MBC_SHADOW_CHAIN(&sr->smb_data, &sr->command, 892 sr->smb2_cmd_hdr + SMB2_HDR_SIZE, 893 ar->ar_cmd_len - SMB2_HDR_SIZE); 894 895 /* 896 * Done with sr_async_req 897 */ 898 ar_func = ar->ar_func; 899 kmem_free(ar, sizeof (*ar)); 900 sr->sr_async_req = ar = NULL; 901 902 /* 903 * Setup output mbuf_chain 904 */ 905 MBC_FLUSH(&sr->reply); 906 sr->smb2_reply_hdr = sr->reply.chain_offset; 907 (void) smb2_encode_header(sr, B_FALSE); 908 909 VERIFY3U(sr->smb2_cmd_code, <, SMB2_INVALID_CMD); 910 sdd = &smb2_disp_table[sr->smb2_cmd_code]; 911 912 /* 913 * Keep the UID, TID, ofile we have. 914 */ 915 if ((sdd->sdt_flags & SDDF_SUPPRESS_UID) == 0 && 916 sr->uid_user == NULL) { 917 smb2sr_put_error(sr, NT_STATUS_USER_SESSION_DELETED); 918 goto cmd_done; 919 } 920 if ((sdd->sdt_flags & SDDF_SUPPRESS_TID) == 0 && 921 sr->tid_tree == NULL) { 922 smb2sr_put_error(sr, NT_STATUS_NETWORK_NAME_DELETED); 923 goto cmd_done; 924 } 925 926 /* 927 * Signature already verified 928 * Credits handled... 929 * 930 * Just call the async handler function. 931 */ 932 sdrc = ar_func(sr); 933 switch (sdrc) { 934 case SDRC_SUCCESS: 935 break; 936 case SDRC_ERROR: 937 if (sr->smb2_status == 0) 938 sr->smb2_status = NT_STATUS_INTERNAL_ERROR; 939 break; 940 case SDRC_SR_KEPT: 941 /* This SR will be completed later. */ 942 return; 943 } 944 945 cmd_done: 946 smb2sr_finish_async(sr); 947 } 948 949 void 950 smb2sr_finish_async(smb_request_t *sr) 951 { 952 smb_disp_stats_t *sds; 953 954 /* 955 * Pad the reply to align(8) if necessary. 956 */ 957 if (sr->reply.chain_offset & 7) { 958 int padsz = 8 - (sr->reply.chain_offset & 7); 959 (void) smb_mbc_encodef(&sr->reply, "#.", padsz); 960 } 961 ASSERT((sr->reply.chain_offset & 7) == 0); 962 963 /* 964 * Record some statistics: (just tx bytes here) 965 */ 966 sds = &sr->session->s_server->sv_disp_stats2[sr->smb2_cmd_code]; 967 atomic_add_64(&sds->sdt_txb, (int64_t)(sr->reply.chain_offset)); 968 969 /* 970 * Put (overwrite) the final SMB2 header. 971 * The call adds: SMB2_FLAGS_SERVER_TO_REDIR 972 */ 973 (void) smb2_encode_header(sr, B_TRUE); 974 975 if (sr->smb2_hdr_flags & SMB2_FLAGS_SIGNED) 976 smb2_sign_reply(sr); 977 978 smb2_send_reply(sr); 979 980 /* 981 * Done. Unlink and free. 982 */ 983 984 mutex_enter(&sr->sr_mutex); 985 sr->sr_state = SMB_REQ_STATE_COMPLETED; 986 mutex_exit(&sr->sr_mutex); 987 988 smb_request_free(sr); 989 } 990 991 /* 992 * In preparation for sending an "interim response", save 993 * all the state we'll need to run an async command later, 994 * and assign an "async id" for this (now async) command. 995 * See [MS-SMB2] 3.3.4.2 996 * 997 * If more than one request in a compound request tries to 998 * "go async", we can "say no". See [MS-SMB2] 3.3.4.2 999 * If an operation would require asynchronous processing 1000 * but resources are constrained, the server MAY choose to 1001 * fail that operation with STATUS_INSUFFICIENT_RESOURCES. 1002 * 1003 * For simplicity, we further restrict the cases where we're 1004 * willing to "go async", and only allow the last command in a 1005 * compound to "go async". It happens that this is the only 1006 * case where we're actually asked to go async anyway. This 1007 * simplification also means there can be at most one command 1008 * in a compound that "goes async" (the last one). 1009 * 1010 * If we agree to "go async", this should return STATUS_PENDING. 1011 * Otherwise return STATUS_INSUFFICIENT_RESOURCES for this and 1012 * all requests following this request. (See the comments re. 1013 * "sticky" smb2_status values in smb2sr_work). 1014 * 1015 * Note: the Async ID we assign here is arbitrary, and need only 1016 * be unique among pending async responses on this connection, so 1017 * this just uses an object address as the Async ID. 1018 * 1019 * Also, the assigned worker is the ONLY thread using this 1020 * async request object (sr_async_req) so no locking. 1021 */ 1022 uint32_t 1023 smb2sr_go_async(smb_request_t *sr, 1024 smb_sdrc_t (*async_func)(smb_request_t *)) 1025 { 1026 smb2_async_req_t *ar; 1027 1028 if (sr->smb2_next_command != 0) 1029 return (NT_STATUS_INSUFFICIENT_RESOURCES); 1030 1031 ASSERT(sr->sr_async_req == NULL); 1032 ar = kmem_zalloc(sizeof (*ar), KM_SLEEP); 1033 1034 /* 1035 * Place an interim response in the compound reply. 1036 * 1037 * Turn on the "async" flag for both the (synchronous) 1038 * interim response and the (later) async response, 1039 * by storing that in flags before coping into ar. 1040 */ 1041 sr->smb2_hdr_flags |= SMB2_FLAGS_ASYNC_COMMAND; 1042 sr->smb2_async_id = (uintptr_t)ar; 1043 1044 ar->ar_func = async_func; 1045 ar->ar_cmd_hdr = sr->smb2_cmd_hdr; 1046 ar->ar_cmd_len = sr->smb_data.max_bytes - sr->smb2_cmd_hdr; 1047 1048 ar->ar_cmd_code = sr->smb2_cmd_code; 1049 ar->ar_hdr_flags = sr->smb2_hdr_flags; 1050 ar->ar_messageid = sr->smb2_messageid; 1051 ar->ar_pid = sr->smb_pid; 1052 ar->ar_tid = sr->smb_tid; 1053 ar->ar_uid = sr->smb_uid; 1054 1055 sr->sr_async_req = ar; 1056 1057 /* Interim responses are NOT signed. */ 1058 sr->smb2_hdr_flags &= ~SMB2_FLAGS_SIGNED; 1059 1060 return (NT_STATUS_PENDING); 1061 } 1062 1063 int 1064 smb2_decode_header(smb_request_t *sr) 1065 { 1066 uint64_t ssnid; 1067 uint32_t pid, tid; 1068 uint16_t hdr_len; 1069 int rc; 1070 1071 rc = smb_mbc_decodef( 1072 &sr->command, "Nwww..wwllqllq16c", 1073 &hdr_len, /* w */ 1074 &sr->smb2_credit_charge, /* w */ 1075 &sr->smb2_chan_seq, /* w */ 1076 /* reserved .. */ 1077 &sr->smb2_cmd_code, /* w */ 1078 &sr->smb2_credit_request, /* w */ 1079 &sr->smb2_hdr_flags, /* l */ 1080 &sr->smb2_next_command, /* l */ 1081 &sr->smb2_messageid, /* q */ 1082 &pid, /* l */ 1083 &tid, /* l */ 1084 &ssnid, /* q */ 1085 sr->smb2_sig); /* 16c */ 1086 if (rc) 1087 return (rc); 1088 1089 if (hdr_len != SMB2_HDR_SIZE) 1090 return (-1); 1091 1092 sr->smb_uid = (uint16_t)ssnid; /* XXX wide UIDs */ 1093 1094 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { 1095 sr->smb2_async_id = pid | 1096 ((uint64_t)tid) << 32; 1097 } else { 1098 sr->smb_pid = pid; 1099 sr->smb_tid = (uint16_t)tid; /* XXX wide TIDs */ 1100 } 1101 1102 return (rc); 1103 } 1104 1105 int 1106 smb2_encode_header(smb_request_t *sr, boolean_t overwrite) 1107 { 1108 uint64_t ssnid = sr->smb_uid; 1109 uint64_t pid_tid_aid; /* pid+tid, or async id */ 1110 uint32_t reply_hdr_flags; 1111 int rc; 1112 1113 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { 1114 pid_tid_aid = sr->smb2_async_id; 1115 } else { 1116 pid_tid_aid = sr->smb_pid | 1117 ((uint64_t)sr->smb_tid) << 32; 1118 } 1119 reply_hdr_flags = sr->smb2_hdr_flags | SMB2_FLAGS_SERVER_TO_REDIR; 1120 1121 if (overwrite) { 1122 rc = smb_mbc_poke(&sr->reply, 1123 sr->smb2_reply_hdr, 1124 "Nwwlwwllqqq16c", 1125 SMB2_HDR_SIZE, /* w */ 1126 sr->smb2_credit_charge, /* w */ 1127 sr->smb2_status, /* l */ 1128 sr->smb2_cmd_code, /* w */ 1129 sr->smb2_credit_response, /* w */ 1130 reply_hdr_flags, /* l */ 1131 sr->smb2_next_reply, /* l */ 1132 sr->smb2_messageid, /* q */ 1133 pid_tid_aid, /* q */ 1134 ssnid, /* q */ 1135 sr->smb2_sig); /* 16c */ 1136 } else { 1137 rc = smb_mbc_encodef(&sr->reply, 1138 "Nwwlwwllqqq16c", 1139 SMB2_HDR_SIZE, /* w */ 1140 sr->smb2_credit_charge, /* w */ 1141 sr->smb2_status, /* l */ 1142 sr->smb2_cmd_code, /* w */ 1143 sr->smb2_credit_response, /* w */ 1144 reply_hdr_flags, /* l */ 1145 sr->smb2_next_reply, /* l */ 1146 sr->smb2_messageid, /* q */ 1147 pid_tid_aid, /* q */ 1148 ssnid, /* q */ 1149 sr->smb2_sig); /* 16c */ 1150 } 1151 1152 return (rc); 1153 } 1154 1155 void 1156 smb2_send_reply(smb_request_t *sr) 1157 { 1158 1159 if (smb_session_send(sr->session, 0, &sr->reply) == 0) 1160 sr->reply.chain = 0; 1161 } 1162 1163 /* 1164 * This wrapper function exists to help catch calls to smbsr_status() 1165 * (which is SMB1-specific) in common code. See smbsr_status(). 1166 * If the log message below is seen, put a dtrace probe on this 1167 * function with a stack() action to see who is calling the SMB1 1168 * "put error" from common code, and fix it. 1169 */ 1170 void 1171 smbsr_status_smb2(smb_request_t *sr, DWORD status) 1172 { 1173 const char *name; 1174 1175 if (sr->smb2_cmd_code < SMB2__NCMDS) 1176 name = smb2_disp_table[sr->smb2_cmd_code].sdt_name; 1177 else 1178 name = "<unknown>"; 1179 #ifdef DEBUG 1180 cmn_err(CE_NOTE, "smbsr_status called for %s", name); 1181 #endif 1182 1183 smb2sr_put_error_data(sr, status, NULL); 1184 } 1185 1186 void 1187 smb2sr_put_errno(struct smb_request *sr, int errnum) 1188 { 1189 uint32_t status = smb_errno2status(errnum); 1190 smb2sr_put_error_data(sr, status, NULL); 1191 } 1192 1193 void 1194 smb2sr_put_error(smb_request_t *sr, uint32_t status) 1195 { 1196 smb2sr_put_error_data(sr, status, NULL); 1197 } 1198 1199 /* 1200 * Build an SMB2 error response. [MS-SMB2] 2.2.2 1201 */ 1202 void 1203 smb2sr_put_error_data(smb_request_t *sr, uint32_t status, mbuf_chain_t *mbc) 1204 { 1205 DWORD len; 1206 1207 /* 1208 * The common dispatch code writes this when it 1209 * updates the SMB2 header before sending. 1210 */ 1211 sr->smb2_status = status; 1212 1213 /* Rewind to the end of the SMB header. */ 1214 sr->reply.chain_offset = sr->smb2_reply_hdr + SMB2_HDR_SIZE; 1215 1216 /* 1217 * NB: Must provide at least one byte of error data, 1218 * per [MS-SMB2] 2.2.2 1219 */ 1220 if (mbc != NULL && (len = MBC_LENGTH(mbc)) != 0) { 1221 (void) smb_mbc_encodef( 1222 &sr->reply, 1223 "wwlC", 1224 9, /* StructSize */ /* w */ 1225 0, /* reserved */ /* w */ 1226 len, /* l */ 1227 mbc); /* C */ 1228 } else { 1229 (void) smb_mbc_encodef( 1230 &sr->reply, 1231 "wwl.", 1232 9, /* StructSize */ /* w */ 1233 0, /* reserved */ /* w */ 1234 0); /* l. */ 1235 } 1236 } 1237 1238 /* 1239 * smb2sr_lookup_fid 1240 * 1241 * Setup sr->fid_ofile, either inherited from a related command, 1242 * or obtained via FID lookup. Similar inheritance logic as in 1243 * smb2sr_work. 1244 */ 1245 uint32_t 1246 smb2sr_lookup_fid(smb_request_t *sr, smb2fid_t *fid) 1247 { 1248 boolean_t related = sr->smb2_hdr_flags & 1249 SMB2_FLAGS_RELATED_OPERATIONS; 1250 1251 if (related) { 1252 if (sr->fid_ofile == NULL) 1253 return (NT_STATUS_INVALID_PARAMETER); 1254 sr->smb_fid = sr->fid_ofile->f_fid; 1255 return (0); 1256 } 1257 1258 /* 1259 * If we could be sure this is called only once per cmd, 1260 * we could simply ASSERT(sr->fid_ofile == NULL) here. 1261 * However, there are cases where it can be called again 1262 * handling the same command, so let's tolerate that. 1263 */ 1264 if (sr->fid_ofile == NULL) { 1265 sr->smb_fid = (uint16_t)fid->temporal; 1266 sr->fid_ofile = smb_ofile_lookup_by_fid(sr, sr->smb_fid); 1267 } 1268 if (sr->fid_ofile == NULL) 1269 return (NT_STATUS_FILE_CLOSED); 1270 1271 return (0); 1272 } 1273 1274 /* 1275 * smb2_dispatch_stats_init 1276 * 1277 * Initializes dispatch statistics for SMB2. 1278 * See also smb_dispatch_stats_init(), which fills in 1279 * the lower part of the statistics array, from zero 1280 * through SMB_COM_NUM; 1281 */ 1282 void 1283 smb2_dispatch_stats_init(smb_server_t *sv) 1284 { 1285 smb_disp_stats_t *sds = sv->sv_disp_stats2; 1286 smb_kstat_req_t *ksr; 1287 int i; 1288 1289 ksr = ((smbsrv_kstats_t *)sv->sv_ksp->ks_data)->ks_reqs2; 1290 1291 for (i = 0; i < SMB2__NCMDS; i++, ksr++) { 1292 smb_latency_init(&sds[i].sdt_lat); 1293 (void) strlcpy(ksr->kr_name, smb2_disp_table[i].sdt_name, 1294 sizeof (ksr->kr_name)); 1295 } 1296 } 1297 1298 /* 1299 * smb2_dispatch_stats_fini 1300 * 1301 * Frees and destroyes the resources used for statistics. 1302 */ 1303 void 1304 smb2_dispatch_stats_fini(smb_server_t *sv) 1305 { 1306 smb_disp_stats_t *sds = sv->sv_disp_stats2; 1307 int i; 1308 1309 for (i = 0; i < SMB2__NCMDS; i++) 1310 smb_latency_destroy(&sds[i].sdt_lat); 1311 } 1312 1313 void 1314 smb2_dispatch_stats_update(smb_server_t *sv, 1315 smb_kstat_req_t *ksr, int first, int nreq) 1316 { 1317 smb_disp_stats_t *sds = sv->sv_disp_stats2; 1318 int i; 1319 int last; 1320 1321 last = first + nreq - 1; 1322 1323 if ((first < SMB2__NCMDS) && (last < SMB2__NCMDS)) { 1324 for (i = first; i <= last; i++, ksr++) { 1325 ksr->kr_rxb = sds[i].sdt_rxb; 1326 ksr->kr_txb = sds[i].sdt_txb; 1327 mutex_enter(&sds[i].sdt_lat.ly_mutex); 1328 ksr->kr_nreq = sds[i].sdt_lat.ly_a_nreq; 1329 ksr->kr_sum = sds[i].sdt_lat.ly_a_sum; 1330 ksr->kr_a_mean = sds[i].sdt_lat.ly_a_mean; 1331 ksr->kr_a_stddev = 1332 sds[i].sdt_lat.ly_a_stddev; 1333 ksr->kr_d_mean = sds[i].sdt_lat.ly_d_mean; 1334 ksr->kr_d_stddev = 1335 sds[i].sdt_lat.ly_d_stddev; 1336 sds[i].sdt_lat.ly_d_mean = 0; 1337 sds[i].sdt_lat.ly_d_nreq = 0; 1338 sds[i].sdt_lat.ly_d_stddev = 0; 1339 sds[i].sdt_lat.ly_d_sum = 0; 1340 mutex_exit(&sds[i].sdt_lat.ly_mutex); 1341 } 1342 } 1343 } 1344