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/CDDL.txt 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/CDDL.txt. 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 // 23 // Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 // Use is subject to license terms. 25 // 26 27 #pragma ident "@(#)copy.cc 1.54 08/05/20 SMI" 28 29 // 30 // This file handles the interface between the kernel's copyin/out and 31 // the MC copyin/out. 32 // 33 34 #include <sys/os.h> 35 #include <orb/infrastructure/orb_conf.h> 36 #include <orb/infrastructure/orb.h> 37 #include <orb/infrastructure/copy.h> 38 #include <h/addrspc.h> 39 #include <nslib/ns.h> 40 #include <stdio.h> 41 #include <sys/cl_comm_support.h> 42 43 #include <sys/thread.h> 44 #include <sys/copyops.h> 45 46 #include <sys/sol_version.h> 47 48 // 49 // This file and client/addrspace_impl.cc implement a mechanism to handle 50 // cross-node ioctls. The problem is that the device driver can do random 51 // copyin/outs. To handle this, we modify the low-level copyin/out/str 52 // routines to call mc_copyin. For cross-node operations, we set thread- 53 // specific data with information on the originating node and pid. 54 // We assume that the process won't move or exit during the ioctl system 55 // call, so we can communicate with the home node's addrspace object. 56 // This object actually does the copyin/out to the originating process. 57 // 58 // In the new approach, there is a single addrspace object on each node, 59 // created when the module is loaded. This object takes (pid, address, 60 // length) and does a copyin/copyout/copyinstr. To support this, instead of 61 // passing around an addrspace object, ioctls now pass around the nodeid and 62 // pid. In addition, if the ioctl is local, the new copyin/out routines are 63 // bypassed entirely. The new code speeds up local ioctls by about a factor 64 // of 4. 65 // 66 // There is a special-case scenario to support the HA-ness of ioctls. The 67 // problem is that if there is an ioctl that needs to be replayed after a 68 // failover/switchover (for e.g. the UFS '_FIOLFS' ioctl), then during the 69 // replay, there is no user thread that makes the call - it is done by the 70 // appropriate component as part of the failover/switchover, and the arguments 71 // are already in kernel space. Hence, when the ioctl handler calls 72 // copyin/copyout, we need to do a simple bcopy. To indicate to 73 // mc_copyin/mc_copyout that the caller of the ioctl is the kernel, we set 74 // the copy_args of the thread to (orb_conf::node_number, 0) before issuing 75 // the ioctl. 76 // 77 78 os::tsd copy::context; // Thread-specific context storage 79 80 // 81 // We originally had a transport problem with sending too-big packets. 82 // So, we fairly arbitrarily set a length so the data will fit in a 83 // 100K packet, and use xfersize to break the transfer into units no 84 // larger than this. This may no longer be necessary. 85 // 86 87 #define XXX_ARBITRARY_LENGTH (100 * 1024) 88 89 static inline size_t 90 xfersize(size_t n) 91 { 92 return (MIN(n, XXX_ARBITRARY_LENGTH)); 93 } 94 95 // 96 // On each node, we have a cache that points to the addrspace objects on 97 // each of the other nodes. These entries are loaded from the name server 98 // on demand. 99 // 100 static addrspc::addrspace_var addrspaceCache_v[NODEID_MAX+1]; 101 static os::mutex_t addrspace_mutex; 102 103 copy::copy() 104 { 105 for (int i = 0; i <= NODEID_MAX; i++) { 106 addrspaceCache_v[i] = addrspc::addrspace::_nil(); 107 } 108 } 109 110 // 111 // getAddrspc does the lookup of an addrspace object in the cache. 112 // getAddrspc returns nil if it can't find the addrspace object in the 113 // name server. This shouldn't happen normally. 114 // 115 static addrspc::addrspace_ptr 116 getAddrspc(sol::nodeid_t nodeid) 117 { 118 addrspc::addrspace_ptr addrspc_p; 119 ASSERT(nodeid <= NODEID_MAX); 120 121 addrspace_mutex.lock(); 122 if (CORBA::is_nil(addrspaceCache_v[nodeid])) { 123 // get an object reference of the mount name server 124 naming::naming_context_var ctx_v = root_nameserver_funcp(); 125 126 Environment e; 127 char name[20]; 128 (void) sprintf(name, "addrspace.%d", nodeid); 129 CORBA::Object_var obj_v = ctx_v->resolve(name, e); 130 if (e.exception()) { 131 addrspace_mutex.unlock(); 132 #ifdef DEBUG 133 e.exception()->print_exception("getAddrspc: Exception " 134 "getting\n"); 135 #endif 136 return (addrspc::addrspace::_nil()); 137 } 138 addrspaceCache_v[nodeid] = addrspc::addrspace::_narrow(obj_v); 139 ASSERT(is_not_nil(addrspaceCache_v[nodeid])); 140 } 141 addrspc_p = addrspc::addrspace::_duplicate(addrspaceCache_v[nodeid]); 142 addrspace_mutex.unlock(); 143 return (addrspc_p); 144 } 145 146 // 147 // checkAddrspc - 148 // Releases the cached addrspace reference, and gets a new one from the 149 // name server, caching it again. Returns the new reference if it differs 150 // from the reference passed in, otherwise returns nil. Called by 151 // mc_xcopyin, mc_xcopyout, and mc_copyinstr when an invocation on the 152 // addrspace object raises a system exception. Calling checkAddrspc with 153 // the old reference used for the invocation will refresh the cache and 154 // determine if the old reference was stale. If it was stale (i.e., there 155 // is a valid new reference that differs from the old one), we want to 156 // retry the invocation with the new reference; if not, we give up and 157 // return an error. 158 // 159 static addrspc::addrspace_ptr 160 checkAddrspc(sol::nodeid_t nodeid, addrspc::addrspace_ptr old_obj_p) 161 { 162 // Clear the addrspace object cache entry for the specified node. 163 addrspace_mutex.lock(); 164 addrspaceCache_v[nodeid] = addrspc::addrspace::_nil(); 165 addrspace_mutex.unlock(); 166 167 // Get new reference from name server and cache it. 168 addrspc::addrspace_ptr new_obj_p = getAddrspc(nodeid); 169 170 // Compare old and new references. 171 if (!CORBA::is_nil(new_obj_p) && new_obj_p->_equiv(old_obj_p)) { 172 // 173 // Old and new references are the same. 174 // Drop new reference and return nil. 175 // 176 CORBA::release(new_obj_p); 177 return (addrspc::addrspace::_nil()); 178 } else { 179 // 180 // Either we got a different reference or a nil one. Return it. 181 // getAddrspc has already duplicated it if it's non-nil. 182 // 183 return (new_obj_p); 184 } 185 } 186 187 static int mc_copyin(const void *, void *, size_t); 188 static int mc_xcopyin(const void *, void *, size_t); 189 static int mc_copyout(const void *, void *, size_t); 190 static int mc_xcopyout(const void *, void *, size_t); 191 static int mc_copyinstr(const char *, char *, size_t, size_t *); 192 static int mc_copyoutstr(const char *, char *, size_t, size_t *); 193 static int mc_fuword8(const void *, uint8_t *); 194 static int mc_fuiword8(const void *, uint8_t *); 195 static int mc_fuiword32(const void *, uint32_t *); 196 static int mc_suiword8(void *, uint8_t); 197 static int mc_suiword32(void *, uint32_t); 198 static int mc_fuword16(const void *, uint16_t *); 199 static int mc_fuword32(const void *, uint32_t *); 200 static int mc_fuword64(const void *, uint64_t *); 201 static int mc_suword8(void *, uint8_t); 202 static int mc_suword16(void *, uint16_t); 203 static int mc_suword32(void *, uint32_t); 204 static int mc_suword64(void *, uint64_t); 205 206 static struct copyops mc_copyops = { 207 mc_copyin, 208 mc_xcopyin, 209 mc_copyout, 210 mc_xcopyout, 211 mc_copyinstr, 212 mc_copyoutstr, 213 mc_fuword8, 214 #if SOL_VERSION < __s10 215 mc_fuiword8, 216 #endif 217 mc_fuword16, 218 mc_fuword32, 219 #if SOL_VERSION < __s10 220 mc_fuiword32, 221 #endif 222 mc_fuword64, 223 mc_suword8, 224 #if SOL_VERSION < __s10 225 mc_suiword8, 226 #endif 227 mc_suword16, 228 mc_suword32, 229 #if SOL_VERSION < __s10 230 mc_suiword32, 231 #endif 232 mc_suword64, 233 default_physio 234 }; 235 236 // 237 // Copy data in from user address to kernel address. 238 // 'cn' is the count of the number of bytes to copy. 239 // 240 static int 241 mc_xcopyin(const void *uaddr, void *kaddr, size_t cn) 242 { 243 caddr_t userbuf = (caddr_t)uaddr; 244 caddr_t driverbuf = (caddr_t)kaddr; 245 246 // We have been called by the xcopyin code to perform the 247 // network op. 248 249 copy_args *args = copy::getcontext(); 250 ASSERT(args != (copy_args *)-1); 251 ASSERT(args != (copy_args *)-2); 252 ASSERT(args != NULL); 253 254 if (args->pid == 0) { 255 if (args->nodeid == orb_conf::node_number()) { 256 // 257 // A pid of zero is used to replay ioctls from the 258 // local kernel during a switchover/failover. At this 259 // point, the data is already in the kernel and a 260 // bcopy is in order. 261 // 262 bcopy(uaddr, kaddr, cn); 263 return (0); 264 } else { 265 // This path (kernel making an ioctl call on a remote 266 // node) has no callers yet, and it is unclear whether 267 // we need to implement it. 268 // For now, we have a panic in here to catch any 269 // callers. 270 // 271 272 // 273 // SCMSGS 274 // @explanation 275 // The system does not support copy operations between 276 // the kernel and a user process when the specified 277 // node is not the local node. 278 // @user_action 279 // Contact your authorized Sun service provider to 280 // determine whether a workaround or patch is 281 // available. 282 // 283 (void) orb_syslog_msgp->log(SC_SYSLOG_PANIC, MESSAGE, 284 "clcomm: Invalid copyargs: node %d pid %d", 285 args->nodeid, args->pid); 286 } 287 } 288 289 addrspc::addrspace_var addrspace_obj = getAddrspc(args->nodeid); 290 if (CORBA::is_nil(addrspace_obj)) { 291 return (ENODEV); 292 } 293 Environment e; 294 295 if (cn == 0) { 296 return (0); 297 } 298 addrspc::ioctldata_t *data_ptr; 299 // 300 // We invalidate the context to make sure we don't end up 301 // in here multiple times somehow. 302 // 303 #ifdef DEBUG 304 copy::setcontext((copy_args *)-2); 305 #endif 306 while (cn != 0) { 307 sol::error_t err; 308 309 // Get the data from the remote process into data_ptr. 310 err = addrspace_obj->copyin(args->pid, 311 (sol::uintptr_t)(long)userbuf, 312 (sol::size_t)xfersize(cn), data_ptr, e); 313 if (e.exception()) { 314 // 315 // There are no user exceptions defined for 316 // this interface, so this is a system 317 // exception (e.g., the node is down or has 318 // rebooted). Since we're caching the 319 // addrspace reference, we may have a stale 320 // reference and need a new one. 321 // 322 ASSERT(e.sys_exception() != NULL); 323 e.clear(); 324 325 // releases old reference in addrspace_obj 326 addrspace_obj = checkAddrspc(args->nodeid, 327 addrspace_obj); 328 if (CORBA::is_nil(addrspace_obj)) { 329 // No new reference. 330 #ifdef DEBUG 331 copy::setcontext(args); 332 #endif 333 return (ENODEV); 334 } else { 335 // 336 // Got a new reference different from 337 // the old one. 338 // 339 continue; 340 } 341 } 342 if (err != 0) { 343 delete data_ptr; 344 #ifdef DEBUG 345 copy::setcontext(args); 346 #endif 347 return (EFAULT); // difference xcopy/copy 348 } 349 ASSERT(data_ptr->length() > 0); 350 ASSERT(data_ptr->length() <= cn); 351 // Copy the data from data_ptr to the kernel buffer. 352 bcopy(data_ptr->buffer(), driverbuf, 353 (size_t)data_ptr->length()); 354 cn -= data_ptr->length(); 355 driverbuf += data_ptr->length(); 356 userbuf += data_ptr->length(); 357 // 358 // Copyin created data_ptr; we no longer want it 359 // since the data has been copied to driverbuf. 360 // 361 delete data_ptr; 362 } 363 #ifdef DEBUG 364 copy::setcontext(args); 365 #endif 366 return (0); 367 } 368 369 // 370 // Copy data out from kernel address to user address. 371 // 'cn' is the count of the number of bytes to copy. 372 // 373 static int 374 mc_xcopyout(const void *kaddr, void *uaddr, size_t cn) 375 { 376 caddr_t driverbuf = (caddr_t)kaddr; 377 caddr_t userbuf = (caddr_t)uaddr; 378 379 // We have been called by the xcopyout code to perform the 380 // network op. 381 382 copy_args *args = copy::getcontext(); 383 ASSERT(args != (copy_args *)-1); 384 ASSERT(args != (copy_args *)-2); 385 ASSERT(args != NULL); 386 387 if (args->pid == 0) { 388 if (args->nodeid == orb_conf::node_number()) { 389 // 390 // A pid of zero is used to replay ioctls from the 391 // local kernel during a switchover/failover. At this 392 // point, the data is already in the kernel and a 393 // bcopy is in order. 394 // 395 bcopy(kaddr, uaddr, cn); 396 return (0); 397 } else { 398 // This path (kernel making an ioctl call on a remote 399 // node) has no callers yet, and it is unclear whether 400 // we need to implement it. 401 // For now, we have a panic in here to catch any 402 // callers. 403 // 404 (void) orb_syslog_msgp->log(SC_SYSLOG_PANIC, MESSAGE, 405 "clcomm: Invalid copyargs: node %d pid %d", 406 args->nodeid, args->pid); 407 } 408 } 409 410 if (cn == 0) { 411 return (0); // check before we create data object 412 // 413 // Note, in sun4m Solaris, copyout(..,..,0) fails 414 // while copyout(..,..,-1) succeeds, while 415 // copyin(bad addr,...,0) gets EFAULT. This is all 416 // basically random behavior based on the details 417 // of the assembly code, so let's just succeed. 418 // If we want to be bug-for-bug compatible, it 419 // will be very messy. 420 // 421 } 422 addrspc::addrspace_var addrspace_obj = getAddrspc(args->nodeid); 423 if (CORBA::is_nil(addrspace_obj)) { 424 return (ENODEV); 425 } 426 Environment e; 427 428 ASSERT(xfersize(cn) <= UINT_MAX); // for casts below 429 addrspc::ioctldata_t *data_ptr = new addrspc::ioctldata_t( 430 (uint_t)xfersize(cn), (uint_t)xfersize(cn)); 431 432 while (cn != 0) { 433 sol::error_t err; 434 size_t xsize = xfersize(cn); 435 data_ptr->length((uint_t)xsize); 436 // Copy the data into the data_ptr buffer. 437 bcopy(driverbuf, data_ptr->buffer(), xsize); 438 ASSERT(data_ptr->length() <= data_ptr->maximum()); 439 // Send the data out to the remote process. 440 err = addrspace_obj->copyout(args->pid, 441 (sol::uintptr_t)(long)userbuf, 442 (sol::size_t)xsize, *data_ptr, e); 443 if (e.exception()) { 444 // 445 // There are no user exceptions defined for 446 // this interface, so this is a system 447 // exception (e.g., the node is down or has 448 // rebooted). Since we're caching the 449 // addrspace reference, we may have a stale 450 // reference and need a new one. 451 // 452 ASSERT(e.sys_exception() != NULL); 453 e.clear(); 454 455 // Releases old reference in addrspace_obj. 456 addrspace_obj = checkAddrspc(args->nodeid, 457 addrspace_obj); 458 if (CORBA::is_nil(addrspace_obj)) { 459 // No new reference. 460 delete data_ptr; 461 return (ENODEV); 462 } else { 463 // 464 // Got a new reference different from 465 // the old one. 466 // 467 continue; 468 } 469 } 470 if (err != 0) { 471 delete data_ptr; 472 return (EFAULT); 473 } 474 ASSERT(data_ptr->length() > 0); 475 ASSERT(data_ptr->length() <= cn); 476 cn -= data_ptr->length(); 477 driverbuf += data_ptr->length(); 478 userbuf += data_ptr->length(); 479 } 480 // Done with data_ptr. 481 delete data_ptr; 482 return (0); 483 } 484 485 // 486 // mc_copyin is like mc_xcopyin but it return -1 instead of an errno. 487 // 488 static int 489 mc_copyin(const void *uaddr, void *kaddr, size_t cn) 490 { 491 return (mc_xcopyin(uaddr, kaddr, cn) ? -1 : 0); 492 } 493 494 static int 495 mc_copyout(const void *kaddr, void *uaddr, size_t cn) 496 { 497 return (mc_xcopyout(kaddr, uaddr, cn) ? -1 : 0); 498 } 499 500 // 501 // Copy string in from user address to kernel address driverbuf. 'maxlength' 502 // is the maximum number of bytes to copy. 'lencopied' returns the number 503 // of bytes actually copied, including the terminating 0. If the string 504 // doesn't fit in maxlength bytes, return ENAMETOOLONG. If we can't find an 505 // addrspace object, things are bad, but we'll return ENODEV. 506 // 507 static int 508 mc_copyinstr(const char *uaddr, char *kaddr, size_t maxlength, 509 size_t *lencopied) 510 { 511 // We have been called by the copyinstr code to perform the 512 // network op. 513 514 int status; 515 copy_args *args = copy::getcontext(); 516 ASSERT(args != (copy_args *)-1); 517 ASSERT(args != (copy_args *)-2); 518 ASSERT(args != NULL); 519 520 Environment e; 521 CORBA::Exception *ex; 522 if (maxlength == 0) { 523 return (EFAULT); 524 } 525 526 if (args->pid == 0) { 527 if (args->nodeid == orb_conf::node_number()) { 528 // 529 // A pid of zero is used to replay ioctls from the 530 // local kernel during a switchover/failover. At this 531 // point, the data is already in the kernel and a 532 // bcopy is in order. 533 // 534 size_t len = strlen(uaddr) + 1; 535 if (maxlength < len) { 536 return (ENAMETOOLONG); 537 } 538 bcopy(uaddr, kaddr, len); 539 return (0); 540 } else { 541 // 542 // This path (kernel making an ioctl call on a remote 543 // node) has no callers yet, and it is unclear whether 544 // we need to implement it. 545 // For now, we have a panic in here to catch any 546 // callers. 547 // 548 (void) orb_syslog_msgp->log(SC_SYSLOG_PANIC, MESSAGE, 549 "clcomm: Invalid copyargs: node %d pid %d", 550 args->nodeid, args->pid); 551 } 552 } 553 554 addrspc::addrspace_var addrspace_obj = getAddrspc(args->nodeid); 555 if (CORBA::is_nil(addrspace_obj)) { 556 return (ENODEV); 557 } 558 addrspc::ioctldata_t *data_ptr; 559 if (maxlength > xfersize(maxlength)) { 560 // XXX it should handle long strings. 561 562 // 563 // SCMSGS 564 // @explanation 565 // The system attempted to copy a string from user space to 566 // the kernel. The maximum string length exceeds length limit. 567 // @user_action 568 // Contact your authorized Sun service provider to determine 569 // whether a workaround or patch is available. 570 // 571 (void) orb_syslog_msgp->log(SC_SYSLOG_PANIC, MESSAGE, 572 "clcomm: copyinstr: max string length %d too long", 573 maxlength); 574 // maxlength = xfersize(maxlength); 575 } 576 do { 577 status = addrspace_obj->copyinstr(args->pid, 578 (sol::uintptr_t)(long)uaddr, (sol::size_t)maxlength, 579 data_ptr, e); 580 if ((ex = e.exception()) != NULL) { 581 // 582 // There are no user exceptions defined for 583 // this interface, so this is a system 584 // exception (e.g., the node is down or has 585 // rebooted). Since we're caching the 586 // addrspace reference, we may have a stale 587 // reference and need a new one. 588 // 589 ASSERT(e.sys_exception() != NULL); 590 e.clear(); 591 592 // Releases old reference in addrspace_obj. 593 addrspace_obj = checkAddrspc(args->nodeid, 594 addrspace_obj); 595 if (CORBA::is_nil(addrspace_obj)) { 596 // No new reference. 597 return (ENODEV); 598 } 599 // 600 // Got a new reference different from 601 // the old one. Fall through and repeat 602 // the loop. 603 // 604 } 605 } while (ex != NULL); 606 607 if (status == EFAULT || status == ENAMETOOLONG) { 608 delete data_ptr; 609 return (status); 610 } 611 if (lencopied != NULL) { 612 *lencopied = data_ptr->length(); 613 } 614 615 bcopy(data_ptr->buffer(), kaddr, (size_t)data_ptr->length()); 616 delete data_ptr; 617 return (status); 618 } 619 620 static int 621 mc_copyoutstr(const char *kaddr, char *uaddr, 622 size_t maxlength, size_t *lencopied) 623 { 624 // We have been called by the copyoutstr code to perform the 625 // network op. 626 627 int status = 0, status2; 628 629 if (maxlength == 0) { 630 return (EFAULT); 631 } 632 size_t len; 633 for (len = 1; kaddr[len-1] != '\0'; len++) { 634 if (len >= maxlength) { 635 status = ENAMETOOLONG; 636 break; 637 } 638 } 639 // 640 // Invariant: len <= maxlength and kaddr[len - 1] == '\0' 641 // or len == maxlength, status = ENAMETOOLONG 642 // To preserve Solaris semantics, we copy out maxlength 643 // bytes even if we have ENAMETOOLONG 644 // 645 if (lencopied != 0) { 646 *lencopied = len; 647 } 648 status2 = mc_copyout(kaddr, uaddr, len); 649 if (status2 != 0) { 650 return (EFAULT); 651 } else { 652 return (status); 653 } 654 } 655 656 static int 657 mc_fuword8(const void *uaddr, uint8_t *kaddr) 658 { 659 return (mc_xcopyin(uaddr, kaddr, (size_t)1) ? -1 : 0); 660 } 661 662 static int 663 mc_fuiword8(const void *uaddr, uint8_t *kaddr) 664 { 665 return (mc_xcopyin(uaddr, kaddr, (size_t)1) ? -1 : 0); 666 } 667 668 static int 669 mc_fuword16(const void *uaddr, uint16_t *kaddr) 670 { 671 return (mc_xcopyin(uaddr, kaddr, (size_t)2) ? -1 : 0); 672 } 673 674 static int 675 mc_fuword32(const void *uaddr, uint32_t *kaddr) 676 { 677 return (mc_xcopyin(uaddr, kaddr, (size_t)4) ? -1 : 0); 678 } 679 680 static int 681 mc_fuiword32(const void *uaddr, uint32_t *kaddr) 682 { 683 return (mc_xcopyin(uaddr, kaddr, (size_t)4) ? -1 : 0); 684 } 685 686 static int 687 mc_fuword64(const void *uaddr, uint64_t *kaddr) 688 { 689 return (mc_xcopyin(uaddr, kaddr, (size_t)8) ? -1 : 0); 690 } 691 692 static int 693 mc_suword8(void *uaddr, uint8_t val) 694 { 695 return (mc_xcopyout(&val, uaddr, sizeof (val)) ? -1 : 0); 696 } 697 698 static int 699 mc_suiword8(void *uaddr, uint8_t val) 700 { 701 return (mc_xcopyout(&val, uaddr, sizeof (val)) ? -1 : 0); 702 } 703 704 static int 705 mc_suword16(void *uaddr, uint16_t val) 706 { 707 return (mc_xcopyout(&val, uaddr, sizeof (val)) ? -1 : 0); 708 } 709 710 static int 711 mc_suword32(void *uaddr, uint32_t val) 712 { 713 return (mc_xcopyout(&val, uaddr, sizeof (val)) ? -1 : 0); 714 } 715 716 static int 717 mc_suiword32(void *uaddr, uint32_t val) 718 { 719 return (mc_xcopyout(&val, uaddr, sizeof (val)) ? -1 : 0); 720 } 721 722 static int 723 mc_suword64(void *uaddr, uint64_t val) 724 { 725 return (mc_xcopyout(&val, uaddr, sizeof (val)) ? -1 : 0); 726 } 727 728 // 729 // Initialize a transport server thread to use the mc functions for 730 // copyin/out. 731 // 732 extern "C" void 733 set_server_copyops() 734 { 735 install_copyops(curthread, &mc_copyops); 736 } 737