1 /*- 2 * Copyright (c) 1982, 1986, 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * (c) UNIX System Laboratories, Inc. 5 * All or some portions of this file are derived from material licensed 6 * to the University of California by American Telephone and Telegraph 7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 8 * the permission of UNIX System Laboratories, Inc. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 * 38 * @(#)kern_resource.c 8.5 (Berkeley) 1/21/94 39 * $FreeBSD: src/sys/kern/kern_resource.c,v 1.55.2.5 2001/11/03 01:41:08 ps Exp $ 40 * $DragonFly: src/sys/kern/kern_resource.c,v 1.35 2008/05/27 05:25:34 dillon Exp $ 41 */ 42 43 #include "opt_compat.h" 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/sysproto.h> 48 #include <sys/file.h> 49 #include <sys/kern_syscall.h> 50 #include <sys/kernel.h> 51 #include <sys/resourcevar.h> 52 #include <sys/malloc.h> 53 #include <sys/proc.h> 54 #include <sys/priv.h> 55 #include <sys/time.h> 56 #include <sys/lockf.h> 57 58 #include <vm/vm.h> 59 #include <vm/vm_param.h> 60 #include <sys/lock.h> 61 #include <vm/pmap.h> 62 #include <vm/vm_map.h> 63 64 #include <sys/thread2.h> 65 #include <sys/spinlock2.h> 66 67 static int donice (struct proc *chgp, int n); 68 69 static MALLOC_DEFINE(M_UIDINFO, "uidinfo", "uidinfo structures"); 70 #define UIHASH(uid) (&uihashtbl[(uid) & uihash]) 71 static struct spinlock uihash_lock; 72 static LIST_HEAD(uihashhead, uidinfo) *uihashtbl; 73 static u_long uihash; /* size of hash table - 1 */ 74 75 static struct uidinfo *uicreate (uid_t uid); 76 static struct uidinfo *uilookup (uid_t uid); 77 78 /* 79 * Resource controls and accounting. 80 */ 81 82 struct getpriority_info { 83 int low; 84 int who; 85 }; 86 87 static int getpriority_callback(struct proc *p, void *data); 88 89 int 90 sys_getpriority(struct getpriority_args *uap) 91 { 92 struct getpriority_info info; 93 struct proc *curp = curproc; 94 struct proc *p; 95 int low = PRIO_MAX + 1; 96 97 switch (uap->which) { 98 case PRIO_PROCESS: 99 if (uap->who == 0) 100 p = curp; 101 else 102 p = pfind(uap->who); 103 if (p == 0) 104 break; 105 if (!PRISON_CHECK(curp->p_ucred, p->p_ucred)) 106 break; 107 low = p->p_nice; 108 break; 109 110 case PRIO_PGRP: 111 { 112 struct pgrp *pg; 113 114 if (uap->who == 0) 115 pg = curp->p_pgrp; 116 else if ((pg = pgfind(uap->who)) == NULL) 117 break; 118 LIST_FOREACH(p, &pg->pg_members, p_pglist) { 119 if ((PRISON_CHECK(curp->p_ucred, p->p_ucred) && p->p_nice < low)) 120 low = p->p_nice; 121 } 122 break; 123 } 124 case PRIO_USER: 125 if (uap->who == 0) 126 uap->who = curp->p_ucred->cr_uid; 127 info.low = low; 128 info.who = uap->who; 129 allproc_scan(getpriority_callback, &info); 130 low = info.low; 131 break; 132 133 default: 134 return (EINVAL); 135 } 136 if (low == PRIO_MAX + 1) 137 return (ESRCH); 138 uap->sysmsg_result = low; 139 return (0); 140 } 141 142 /* 143 * Figure out the current lowest nice priority for processes owned 144 * by the specified user. 145 */ 146 static 147 int 148 getpriority_callback(struct proc *p, void *data) 149 { 150 struct getpriority_info *info = data; 151 152 if (PRISON_CHECK(curproc->p_ucred, p->p_ucred) && 153 p->p_ucred->cr_uid == info->who && 154 p->p_nice < info->low) { 155 info->low = p->p_nice; 156 } 157 return(0); 158 } 159 160 struct setpriority_info { 161 int prio; 162 int who; 163 int error; 164 int found; 165 }; 166 167 static int setpriority_callback(struct proc *p, void *data); 168 169 int 170 sys_setpriority(struct setpriority_args *uap) 171 { 172 struct setpriority_info info; 173 struct proc *curp = curproc; 174 struct proc *p; 175 int found = 0, error = 0; 176 177 switch (uap->which) { 178 case PRIO_PROCESS: 179 if (uap->who == 0) 180 p = curp; 181 else 182 p = pfind(uap->who); 183 if (p == 0) 184 break; 185 if (!PRISON_CHECK(curp->p_ucred, p->p_ucred)) 186 break; 187 error = donice(p, uap->prio); 188 found++; 189 break; 190 191 case PRIO_PGRP: 192 { 193 struct pgrp *pg; 194 195 if (uap->who == 0) 196 pg = curp->p_pgrp; 197 else if ((pg = pgfind(uap->who)) == NULL) 198 break; 199 LIST_FOREACH(p, &pg->pg_members, p_pglist) { 200 if (PRISON_CHECK(curp->p_ucred, p->p_ucred)) { 201 error = donice(p, uap->prio); 202 found++; 203 } 204 } 205 break; 206 } 207 case PRIO_USER: 208 if (uap->who == 0) 209 uap->who = curp->p_ucred->cr_uid; 210 info.prio = uap->prio; 211 info.who = uap->who; 212 info.error = 0; 213 info.found = 0; 214 allproc_scan(setpriority_callback, &info); 215 error = info.error; 216 found = info.found; 217 break; 218 219 default: 220 return (EINVAL); 221 } 222 if (found == 0) 223 return (ESRCH); 224 return (error); 225 } 226 227 static 228 int 229 setpriority_callback(struct proc *p, void *data) 230 { 231 struct setpriority_info *info = data; 232 int error; 233 234 if (p->p_ucred->cr_uid == info->who && 235 PRISON_CHECK(curproc->p_ucred, p->p_ucred)) { 236 error = donice(p, info->prio); 237 if (error) 238 info->error = error; 239 ++info->found; 240 } 241 return(0); 242 } 243 244 static int 245 donice(struct proc *chgp, int n) 246 { 247 struct proc *curp = curproc; 248 struct ucred *cr = curp->p_ucred; 249 struct lwp *lp; 250 251 if (cr->cr_uid && cr->cr_ruid && 252 cr->cr_uid != chgp->p_ucred->cr_uid && 253 cr->cr_ruid != chgp->p_ucred->cr_uid) 254 return (EPERM); 255 if (n > PRIO_MAX) 256 n = PRIO_MAX; 257 if (n < PRIO_MIN) 258 n = PRIO_MIN; 259 if (n < chgp->p_nice && priv_check_cred(cr, PRIV_SCHED_SETPRIORITY, 0)) 260 return (EACCES); 261 chgp->p_nice = n; 262 FOREACH_LWP_IN_PROC(lp, chgp) 263 chgp->p_usched->resetpriority(lp); 264 return (0); 265 } 266 267 int 268 sys_lwp_rtprio(struct lwp_rtprio_args *uap) 269 { 270 struct proc *p = curproc; 271 struct lwp *lp; 272 struct rtprio rtp; 273 struct ucred *cr = p->p_ucred; 274 int error; 275 276 error = copyin(uap->rtp, &rtp, sizeof(struct rtprio)); 277 if (error) 278 return error; 279 280 if (uap->pid < 0) { 281 return EINVAL; 282 } else if (uap->pid == 0) { 283 /* curproc already loaded on p */ 284 } else { 285 p = pfind(uap->pid); 286 } 287 288 if (p == 0) { 289 return ESRCH; 290 } 291 292 if (uap->tid < -1) { 293 return EINVAL; 294 } else if (uap->tid == -1) { 295 /* 296 * sadly, tid can be 0 so we can't use 0 here 297 * like sys_rtprio() 298 */ 299 lp = curthread->td_lwp; 300 } else { 301 lp = lwp_rb_tree_RB_LOOKUP(&p->p_lwp_tree, uap->tid); 302 if (lp == NULL) 303 return ESRCH; 304 } 305 306 switch (uap->function) { 307 case RTP_LOOKUP: 308 return (copyout(&lp->lwp_rtprio, uap->rtp, 309 sizeof(struct rtprio))); 310 case RTP_SET: 311 if (cr->cr_uid && cr->cr_ruid && 312 cr->cr_uid != p->p_ucred->cr_uid && 313 cr->cr_ruid != p->p_ucred->cr_uid) { 314 return EPERM; 315 } 316 /* disallow setting rtprio in most cases if not superuser */ 317 if (priv_check_cred(cr, PRIV_SCHED_RTPRIO, 0)) { 318 /* can't set someone else's */ 319 if (uap->pid) { /* XXX */ 320 return EPERM; 321 } 322 /* can't set realtime priority */ 323 /* 324 * Realtime priority has to be restricted for reasons which should be 325 * obvious. However, for idle priority, there is a potential for 326 * system deadlock if an idleprio process gains a lock on a resource 327 * that other processes need (and the idleprio process can't run 328 * due to a CPU-bound normal process). Fix me! XXX 329 */ 330 if (RTP_PRIO_IS_REALTIME(rtp.type)) { 331 return EPERM; 332 } 333 } 334 switch (rtp.type) { 335 #ifdef RTP_PRIO_FIFO 336 case RTP_PRIO_FIFO: 337 #endif 338 case RTP_PRIO_REALTIME: 339 case RTP_PRIO_NORMAL: 340 case RTP_PRIO_IDLE: 341 if (rtp.prio > RTP_PRIO_MAX) 342 return EINVAL; 343 lp->lwp_rtprio = rtp; 344 return 0; 345 default: 346 return EINVAL; 347 } 348 default: 349 return EINVAL; 350 } 351 panic("can't get here"); 352 } 353 354 /* 355 * Set realtime priority 356 */ 357 /* ARGSUSED */ 358 int 359 sys_rtprio(struct rtprio_args *uap) 360 { 361 struct proc *curp = curproc; 362 struct proc *p; 363 struct lwp *lp; 364 struct ucred *cr = curp->p_ucred; 365 struct rtprio rtp; 366 int error; 367 368 error = copyin(uap->rtp, &rtp, sizeof(struct rtprio)); 369 if (error) 370 return (error); 371 372 if (uap->pid == 0) 373 p = curp; 374 else 375 p = pfind(uap->pid); 376 377 if (p == 0) 378 return (ESRCH); 379 380 /* XXX lwp */ 381 lp = FIRST_LWP_IN_PROC(p); 382 switch (uap->function) { 383 case RTP_LOOKUP: 384 return (copyout(&lp->lwp_rtprio, uap->rtp, sizeof(struct rtprio))); 385 case RTP_SET: 386 if (cr->cr_uid && cr->cr_ruid && 387 cr->cr_uid != p->p_ucred->cr_uid && 388 cr->cr_ruid != p->p_ucred->cr_uid) 389 return (EPERM); 390 /* disallow setting rtprio in most cases if not superuser */ 391 if (priv_check_cred(cr, PRIV_SCHED_RTPRIO, 0)) { 392 /* can't set someone else's */ 393 if (uap->pid) 394 return (EPERM); 395 /* can't set realtime priority */ 396 /* 397 * Realtime priority has to be restricted for reasons which should be 398 * obvious. However, for idle priority, there is a potential for 399 * system deadlock if an idleprio process gains a lock on a resource 400 * that other processes need (and the idleprio process can't run 401 * due to a CPU-bound normal process). Fix me! XXX 402 */ 403 if (RTP_PRIO_IS_REALTIME(rtp.type)) 404 return (EPERM); 405 } 406 switch (rtp.type) { 407 #ifdef RTP_PRIO_FIFO 408 case RTP_PRIO_FIFO: 409 #endif 410 case RTP_PRIO_REALTIME: 411 case RTP_PRIO_NORMAL: 412 case RTP_PRIO_IDLE: 413 if (rtp.prio > RTP_PRIO_MAX) 414 return (EINVAL); 415 lp->lwp_rtprio = rtp; 416 return (0); 417 default: 418 return (EINVAL); 419 } 420 421 default: 422 return (EINVAL); 423 } 424 } 425 426 int 427 sys_setrlimit(struct __setrlimit_args *uap) 428 { 429 struct rlimit alim; 430 int error; 431 432 error = copyin(uap->rlp, &alim, sizeof(alim)); 433 if (error) 434 return (error); 435 436 error = kern_setrlimit(uap->which, &alim); 437 438 return (error); 439 } 440 441 int 442 sys_getrlimit(struct __getrlimit_args *uap) 443 { 444 struct rlimit lim; 445 int error; 446 447 error = kern_getrlimit(uap->which, &lim); 448 449 if (error == 0) 450 error = copyout(&lim, uap->rlp, sizeof(*uap->rlp)); 451 return error; 452 } 453 454 /* 455 * Transform the running time and tick information in lwp lp's thread into user, 456 * system, and interrupt time usage. 457 * 458 * Since we are limited to statclock tick granularity this is a statisical 459 * calculation which will be correct over the long haul, but should not be 460 * expected to measure fine grained deltas. 461 * 462 * It is possible to catch a lwp in the midst of being created, so 463 * check whether lwp_thread is NULL or not. 464 */ 465 void 466 calcru(struct lwp *lp, struct timeval *up, struct timeval *sp) 467 { 468 struct thread *td; 469 470 /* 471 * Calculate at the statclock level. YYY if the thread is owned by 472 * another cpu we need to forward the request to the other cpu, or 473 * have a token to interlock the information in order to avoid racing 474 * thread destruction. 475 */ 476 if ((td = lp->lwp_thread) != NULL) { 477 crit_enter(); 478 up->tv_sec = td->td_uticks / 1000000; 479 up->tv_usec = td->td_uticks % 1000000; 480 sp->tv_sec = td->td_sticks / 1000000; 481 sp->tv_usec = td->td_sticks % 1000000; 482 crit_exit(); 483 } 484 } 485 486 /* 487 * Aggregate resource statistics of all lwps of a process. 488 * 489 * proc.p_ru keeps track of all statistics directly related to a proc. This 490 * consists of RSS usage and nswap information and aggregate numbers for all 491 * former lwps of this proc. 492 * 493 * proc.p_cru is the sum of all stats of reaped children. 494 * 495 * lwp.lwp_ru contains the stats directly related to one specific lwp, meaning 496 * packet, scheduler switch or page fault counts, etc. This information gets 497 * added to lwp.lwp_proc.p_ru when the lwp exits. 498 */ 499 void 500 calcru_proc(struct proc *p, struct rusage *ru) 501 { 502 struct timeval upt, spt; 503 long *rip1, *rip2; 504 struct lwp *lp; 505 506 *ru = p->p_ru; 507 508 FOREACH_LWP_IN_PROC(lp, p) { 509 calcru(lp, &upt, &spt); 510 timevaladd(&ru->ru_utime, &upt); 511 timevaladd(&ru->ru_stime, &spt); 512 for (rip1 = &ru->ru_first, rip2 = &lp->lwp_ru.ru_first; 513 rip1 <= &ru->ru_last; 514 rip1++, rip2++) 515 *rip1 += *rip2; 516 } 517 } 518 519 520 /* ARGSUSED */ 521 int 522 sys_getrusage(struct getrusage_args *uap) 523 { 524 struct rusage ru; 525 struct rusage *rup; 526 527 switch (uap->who) { 528 529 case RUSAGE_SELF: 530 rup = &ru; 531 calcru_proc(curproc, rup); 532 break; 533 534 case RUSAGE_CHILDREN: 535 rup = &curproc->p_cru; 536 break; 537 538 default: 539 return (EINVAL); 540 } 541 return (copyout((caddr_t)rup, (caddr_t)uap->rusage, 542 sizeof (struct rusage))); 543 } 544 545 void 546 ruadd(struct rusage *ru, struct rusage *ru2) 547 { 548 long *ip, *ip2; 549 int i; 550 551 timevaladd(&ru->ru_utime, &ru2->ru_utime); 552 timevaladd(&ru->ru_stime, &ru2->ru_stime); 553 if (ru->ru_maxrss < ru2->ru_maxrss) 554 ru->ru_maxrss = ru2->ru_maxrss; 555 ip = &ru->ru_first; ip2 = &ru2->ru_first; 556 for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--) 557 *ip++ += *ip2++; 558 } 559 560 /* 561 * Find the uidinfo structure for a uid. This structure is used to 562 * track the total resource consumption (process count, socket buffer 563 * size, etc.) for the uid and impose limits. 564 */ 565 void 566 uihashinit(void) 567 { 568 spin_init(&uihash_lock); 569 uihashtbl = hashinit(maxproc / 16, M_UIDINFO, &uihash); 570 } 571 572 static struct uidinfo * 573 uilookup(uid_t uid) 574 { 575 struct uihashhead *uipp; 576 struct uidinfo *uip; 577 578 uipp = UIHASH(uid); 579 LIST_FOREACH(uip, uipp, ui_hash) { 580 if (uip->ui_uid == uid) 581 break; 582 } 583 return (uip); 584 } 585 586 static struct uidinfo * 587 uicreate(uid_t uid) 588 { 589 struct uidinfo *uip, *tmp; 590 /* 591 * Allocate space and check for a race 592 */ 593 MALLOC(uip, struct uidinfo *, sizeof(*uip), M_UIDINFO, M_WAITOK); 594 /* 595 * Initialize structure and enter it into the hash table 596 */ 597 spin_init(&uip->ui_lock); 598 uip->ui_uid = uid; 599 uip->ui_proccnt = 0; 600 uip->ui_sbsize = 0; 601 uip->ui_ref = 1; /* we're returning a ref */ 602 uip->ui_posixlocks = 0; 603 varsymset_init(&uip->ui_varsymset, NULL); 604 605 /* 606 * Somebody may have already created the uidinfo for this 607 * uid. If so, return that instead. 608 */ 609 spin_lock_wr(&uihash_lock); 610 tmp = uilookup(uid); 611 if (tmp != NULL) { 612 varsymset_clean(&uip->ui_varsymset); 613 spin_uninit(&uip->ui_lock); 614 FREE(uip, M_UIDINFO); 615 uip = tmp; 616 } else { 617 LIST_INSERT_HEAD(UIHASH(uid), uip, ui_hash); 618 } 619 spin_unlock_wr(&uihash_lock); 620 621 return (uip); 622 } 623 624 struct uidinfo * 625 uifind(uid_t uid) 626 { 627 struct uidinfo *uip; 628 629 spin_lock_rd(&uihash_lock); 630 uip = uilookup(uid); 631 if (uip == NULL) { 632 spin_unlock_rd(&uihash_lock); 633 uip = uicreate(uid); 634 } else { 635 uihold(uip); 636 spin_unlock_rd(&uihash_lock); 637 } 638 return (uip); 639 } 640 641 static __inline void 642 uifree(struct uidinfo *uip) 643 { 644 spin_lock_wr(&uihash_lock); 645 646 /* 647 * Note that we're taking a read lock even though we 648 * modify the structure because we know nobody can find 649 * it now that we've locked uihash_lock. If somebody 650 * can get to it through a stored pointer, the reference 651 * count will not be 0 and in that case we don't modify 652 * the struct. 653 */ 654 spin_lock_rd(&uip->ui_lock); 655 if (uip->ui_ref != 0) { 656 /* 657 * Someone found the uid and got a ref when we 658 * unlocked. No need to free any more. 659 */ 660 spin_unlock_rd(&uip->ui_lock); 661 return; 662 } 663 if (uip->ui_sbsize != 0) 664 /* XXX no %qd in kernel. Truncate. */ 665 kprintf("freeing uidinfo: uid = %d, sbsize = %ld\n", 666 uip->ui_uid, (long)uip->ui_sbsize); 667 if (uip->ui_proccnt != 0) 668 kprintf("freeing uidinfo: uid = %d, proccnt = %ld\n", 669 uip->ui_uid, uip->ui_proccnt); 670 671 LIST_REMOVE(uip, ui_hash); 672 spin_unlock_wr(&uihash_lock); 673 varsymset_clean(&uip->ui_varsymset); 674 lockuninit(&uip->ui_varsymset.vx_lock); 675 spin_unlock_rd(&uip->ui_lock); 676 spin_uninit(&uip->ui_lock); 677 FREE(uip, M_UIDINFO); 678 } 679 680 void 681 uihold(struct uidinfo *uip) 682 { 683 atomic_add_int(&uip->ui_ref, 1); 684 KKASSERT(uip->ui_ref > 0); 685 } 686 687 void 688 uidrop(struct uidinfo *uip) 689 { 690 if (atomic_fetchadd_int(&uip->ui_ref, -1) == 1) { 691 uifree(uip); 692 } else { 693 KKASSERT(uip->ui_ref > 0); 694 } 695 } 696 697 void 698 uireplace(struct uidinfo **puip, struct uidinfo *nuip) 699 { 700 uidrop(*puip); 701 *puip = nuip; 702 } 703 704 /* 705 * Change the count associated with number of processes 706 * a given user is using. When 'max' is 0, don't enforce a limit 707 */ 708 int 709 chgproccnt(struct uidinfo *uip, int diff, int max) 710 { 711 int ret; 712 spin_lock_wr(&uip->ui_lock); 713 /* don't allow them to exceed max, but allow subtraction */ 714 if (diff > 0 && uip->ui_proccnt + diff > max && max != 0) { 715 ret = 0; 716 } else { 717 uip->ui_proccnt += diff; 718 if (uip->ui_proccnt < 0) 719 kprintf("negative proccnt for uid = %d\n", uip->ui_uid); 720 ret = 1; 721 } 722 spin_unlock_wr(&uip->ui_lock); 723 return ret; 724 } 725 726 /* 727 * Change the total socket buffer size a user has used. 728 */ 729 int 730 chgsbsize(struct uidinfo *uip, u_long *hiwat, u_long to, rlim_t max) 731 { 732 rlim_t new; 733 734 spin_lock_wr(&uip->ui_lock); 735 new = uip->ui_sbsize + to - *hiwat; 736 KKASSERT(new >= 0); 737 738 /* 739 * If we are trying to increase the socket buffer size 740 * Scale down the hi water mark when we exceed the user's 741 * allowed socket buffer space. 742 * 743 * We can't scale down too much or we will blow up atomic packet 744 * operations. 745 */ 746 if (to > *hiwat && to > MCLBYTES && new > max) { 747 to = to * max / new; 748 if (to < MCLBYTES) 749 to = MCLBYTES; 750 } 751 uip->ui_sbsize = new; 752 *hiwat = to; 753 spin_unlock_wr(&uip->ui_lock); 754 return (1); 755 } 756 757