thr_attr.c revision 214335
1/* 2 * Copyright (c) 2003 Craig Rodrigues <rodrigc@attbi.com>. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Craig Rodrigues. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY CRAIG RODRIGUES AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 */ 33 34/* 35 * Copyright (c) 1998 Daniel Eischen <eischen@vigrid.com>. 36 * Copyright (C) 2001 Jason Evans <jasone@freebsd.org>. 37 * Copyright (c) 2002,2003 Alexey Zelkin <phantom@FreeBSD.org> 38 * All rights reserved. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice(s), this list of conditions and the following disclaimer 45 * unmodified other than the allowable addition of one or more 46 * copyright notices. 47 * 2. Redistributions in binary form must reproduce the above copyright 48 * notice(s), this list of conditions and the following disclaimer in 49 * the documentation and/or other materials provided with the 50 * distribution. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 53 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 55 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE 56 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 57 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 58 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 59 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 60 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 61 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 62 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 63 */ 64 65/* 66 * Copyright (c) 1996 John Birrell <jb@cimlogic.com.au>. 67 * All rights reserved. 68 * 69 * Redistribution and use in source and binary forms, with or without 70 * modification, are permitted provided that the following conditions 71 * are met: 72 * 1. Redistributions of source code must retain the above copyright 73 * notice, this list of conditions and the following disclaimer. 74 * 2. Redistributions in binary form must reproduce the above copyright 75 * notice, this list of conditions and the following disclaimer in the 76 * documentation and/or other materials provided with the distribution. 77 * 3. Neither the name of the author nor the names of any co-contributors 78 * may be used to endorse or promote products derived from this software 79 * without specific prior written permission. 80 * 81 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND 82 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 83 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 84 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 85 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 86 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 87 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 88 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 89 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 90 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 91 * SUCH DAMAGE. 92 * 93 * $FreeBSD: head/lib/libthr/thread/thr_attr.c 214335 2010-10-25 11:16:50Z davidxu $ 94 */ 95 96#include "namespace.h" 97#include <errno.h> 98#include <pthread.h> 99#include <stdlib.h> 100#include <string.h> 101#include <pthread_np.h> 102#include <sys/sysctl.h> 103#include "un-namespace.h" 104 105#include "thr_private.h" 106 107static size_t _get_kern_cpuset_size(void); 108 109__weak_reference(_pthread_attr_destroy, pthread_attr_destroy); 110 111int 112_pthread_attr_destroy(pthread_attr_t *attr) 113{ 114 int ret; 115 116 /* Check for invalid arguments: */ 117 if (attr == NULL || *attr == NULL) 118 /* Invalid argument: */ 119 ret = EINVAL; 120 else { 121 if ((*attr)->cpuset != NULL) 122 free((*attr)->cpuset); 123 /* Free the memory allocated to the attribute object: */ 124 free(*attr); 125 126 /* 127 * Leave the attribute pointer NULL now that the memory 128 * has been freed: 129 */ 130 *attr = NULL; 131 ret = 0; 132 } 133 return(ret); 134} 135 136__weak_reference(_pthread_attr_get_np, pthread_attr_get_np); 137 138int 139_pthread_attr_get_np(pthread_t pthread, pthread_attr_t *dstattr) 140{ 141 struct pthread *curthread; 142 struct pthread_attr attr, *dst; 143 int ret; 144 size_t cpusetsize; 145 146 if (pthread == NULL || dstattr == NULL || (dst = *dstattr) == NULL) 147 return (EINVAL); 148 cpusetsize = _get_kern_cpuset_size(); 149 if (dst->cpusetsize < cpusetsize) { 150 char *newset = realloc(dst->cpuset, cpusetsize); 151 if (newset == NULL) 152 return (errno); 153 memset(newset + dst->cpusetsize, 0, cpusetsize - 154 dst->cpusetsize); 155 dst->cpuset = (cpuset_t *)newset; 156 dst->cpusetsize = cpusetsize; 157 } 158 curthread = _get_curthread(); 159 if ((ret = _thr_find_thread(curthread, pthread, /*include dead*/0)) != 0) 160 return (ret); 161 attr = pthread->attr; 162 if (pthread->flags & THR_FLAGS_DETACHED) 163 attr.flags |= PTHREAD_DETACHED; 164 ret = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, TID(pthread), 165 dst->cpusetsize, dst->cpuset); 166 if (ret == -1) 167 ret = errno; 168 THR_THREAD_UNLOCK(curthread, pthread); 169 if (ret == 0) { 170 memcpy(&dst->pthread_attr_start_copy, 171 &attr.pthread_attr_start_copy, 172 offsetof(struct pthread_attr, pthread_attr_end_copy) - 173 offsetof(struct pthread_attr, pthread_attr_start_copy)); 174 } 175 return (ret); 176} 177 178__weak_reference(_pthread_attr_getdetachstate, pthread_attr_getdetachstate); 179 180int 181_pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) 182{ 183 int ret; 184 185 /* Check for invalid arguments: */ 186 if (attr == NULL || *attr == NULL || detachstate == NULL) 187 ret = EINVAL; 188 else { 189 /* Check if the detached flag is set: */ 190 if ((*attr)->flags & PTHREAD_DETACHED) 191 /* Return detached: */ 192 *detachstate = PTHREAD_CREATE_DETACHED; 193 else 194 /* Return joinable: */ 195 *detachstate = PTHREAD_CREATE_JOINABLE; 196 ret = 0; 197 } 198 return(ret); 199} 200 201__weak_reference(_pthread_attr_getguardsize, pthread_attr_getguardsize); 202 203int 204_pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) 205{ 206 int ret; 207 208 /* Check for invalid arguments: */ 209 if (attr == NULL || *attr == NULL || guardsize == NULL) 210 ret = EINVAL; 211 else { 212 /* Return the guard size: */ 213 *guardsize = (*attr)->guardsize_attr; 214 ret = 0; 215 } 216 return(ret); 217} 218 219__weak_reference(_pthread_attr_getinheritsched, pthread_attr_getinheritsched); 220 221int 222_pthread_attr_getinheritsched(const pthread_attr_t *attr, int *sched_inherit) 223{ 224 int ret = 0; 225 226 if ((attr == NULL) || (*attr == NULL)) 227 ret = EINVAL; 228 else 229 *sched_inherit = (*attr)->sched_inherit; 230 231 return(ret); 232} 233 234__weak_reference(_pthread_attr_getschedparam, pthread_attr_getschedparam); 235 236int 237_pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param) 238{ 239 int ret = 0; 240 241 if ((attr == NULL) || (*attr == NULL) || (param == NULL)) 242 ret = EINVAL; 243 else 244 param->sched_priority = (*attr)->prio; 245 246 return(ret); 247} 248 249__weak_reference(_pthread_attr_getschedpolicy, pthread_attr_getschedpolicy); 250 251int 252_pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) 253{ 254 int ret = 0; 255 256 if ((attr == NULL) || (*attr == NULL) || (policy == NULL)) 257 ret = EINVAL; 258 else 259 *policy = (*attr)->sched_policy; 260 261 return(ret); 262} 263 264__weak_reference(_pthread_attr_getscope, pthread_attr_getscope); 265 266int 267_pthread_attr_getscope(const pthread_attr_t *attr, int *contentionscope) 268{ 269 int ret = 0; 270 271 if ((attr == NULL) || (*attr == NULL) || (contentionscope == NULL)) 272 /* Return an invalid argument: */ 273 ret = EINVAL; 274 275 else 276 *contentionscope = (*attr)->flags & PTHREAD_SCOPE_SYSTEM ? 277 PTHREAD_SCOPE_SYSTEM : PTHREAD_SCOPE_PROCESS; 278 279 return(ret); 280} 281 282__weak_reference(_pthread_attr_getstack, pthread_attr_getstack); 283 284int 285_pthread_attr_getstack(const pthread_attr_t * __restrict attr, 286 void ** __restrict stackaddr, 287 size_t * __restrict stacksize) 288{ 289 int ret; 290 291 /* Check for invalid arguments: */ 292 if (attr == NULL || *attr == NULL || stackaddr == NULL 293 || stacksize == NULL ) 294 ret = EINVAL; 295 else { 296 /* Return the stack address and size */ 297 *stackaddr = (*attr)->stackaddr_attr; 298 *stacksize = (*attr)->stacksize_attr; 299 ret = 0; 300 } 301 return(ret); 302} 303 304__weak_reference(_pthread_attr_getstackaddr, pthread_attr_getstackaddr); 305 306int 307_pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr) 308{ 309 int ret; 310 311 /* Check for invalid arguments: */ 312 if (attr == NULL || *attr == NULL || stackaddr == NULL) 313 ret = EINVAL; 314 else { 315 /* Return the stack address: */ 316 *stackaddr = (*attr)->stackaddr_attr; 317 ret = 0; 318 } 319 return(ret); 320} 321 322__weak_reference(_pthread_attr_getstacksize, pthread_attr_getstacksize); 323 324int 325_pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize) 326{ 327 int ret; 328 329 /* Check for invalid arguments: */ 330 if (attr == NULL || *attr == NULL || stacksize == NULL) 331 ret = EINVAL; 332 else { 333 /* Return the stack size: */ 334 *stacksize = (*attr)->stacksize_attr; 335 ret = 0; 336 } 337 return(ret); 338} 339 340__weak_reference(_pthread_attr_init, pthread_attr_init); 341 342int 343_pthread_attr_init(pthread_attr_t *attr) 344{ 345 int ret; 346 pthread_attr_t pattr; 347 348 _thr_check_init(); 349 350 /* Allocate memory for the attribute object: */ 351 if ((pattr = (pthread_attr_t) malloc(sizeof(struct pthread_attr))) == NULL) 352 /* Insufficient memory: */ 353 ret = ENOMEM; 354 else { 355 /* Initialise the attribute object with the defaults: */ 356 memcpy(pattr, &_pthread_attr_default, sizeof(struct pthread_attr)); 357 358 /* Return a pointer to the attribute object: */ 359 *attr = pattr; 360 ret = 0; 361 } 362 return(ret); 363} 364 365__weak_reference(_pthread_attr_setcreatesuspend_np, pthread_attr_setcreatesuspend_np); 366 367int 368_pthread_attr_setcreatesuspend_np(pthread_attr_t *attr) 369{ 370 int ret; 371 372 if (attr == NULL || *attr == NULL) { 373 ret = EINVAL; 374 } else { 375 (*attr)->suspend = THR_CREATE_SUSPENDED; 376 ret = 0; 377 } 378 return(ret); 379} 380 381__weak_reference(_pthread_attr_setdetachstate, pthread_attr_setdetachstate); 382 383int 384_pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) 385{ 386 int ret; 387 388 /* Check for invalid arguments: */ 389 if (attr == NULL || *attr == NULL || 390 (detachstate != PTHREAD_CREATE_DETACHED && 391 detachstate != PTHREAD_CREATE_JOINABLE)) 392 ret = EINVAL; 393 else { 394 /* Check if detached state: */ 395 if (detachstate == PTHREAD_CREATE_DETACHED) 396 /* Set the detached flag: */ 397 (*attr)->flags |= PTHREAD_DETACHED; 398 else 399 /* Reset the detached flag: */ 400 (*attr)->flags &= ~PTHREAD_DETACHED; 401 ret = 0; 402 } 403 return(ret); 404} 405 406__weak_reference(_pthread_attr_setguardsize, pthread_attr_setguardsize); 407 408int 409_pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) 410{ 411 int ret; 412 413 /* Check for invalid arguments. */ 414 if (attr == NULL || *attr == NULL) 415 ret = EINVAL; 416 else { 417 /* Save the stack size. */ 418 (*attr)->guardsize_attr = guardsize; 419 ret = 0; 420 } 421 return(ret); 422} 423 424__weak_reference(_pthread_attr_setinheritsched, pthread_attr_setinheritsched); 425 426int 427_pthread_attr_setinheritsched(pthread_attr_t *attr, int sched_inherit) 428{ 429 int ret = 0; 430 431 if ((attr == NULL) || (*attr == NULL)) 432 ret = EINVAL; 433 else if (sched_inherit != PTHREAD_INHERIT_SCHED && 434 sched_inherit != PTHREAD_EXPLICIT_SCHED) 435 ret = ENOTSUP; 436 else 437 (*attr)->sched_inherit = sched_inherit; 438 439 return(ret); 440} 441 442__weak_reference(_pthread_attr_setschedparam, pthread_attr_setschedparam); 443 444int 445_pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param) 446{ 447 int policy; 448 449 if ((attr == NULL) || (*attr == NULL)) 450 return (EINVAL); 451 452 if (param == NULL) 453 return (ENOTSUP); 454 455 policy = (*attr)->sched_policy; 456 457 if (policy == SCHED_FIFO || policy == SCHED_RR) { 458 if (param->sched_priority < _thr_priorities[policy-1].pri_min || 459 param->sched_priority > _thr_priorities[policy-1].pri_max) 460 return (ENOTSUP); 461 } else { 462 /* 463 * Ignore it for SCHED_OTHER now, patches for glib ports 464 * are wrongly using M:N thread library's internal macro 465 * THR_MIN_PRIORITY and THR_MAX_PRIORITY. 466 */ 467 } 468 469 (*attr)->prio = param->sched_priority; 470 471 return (0); 472} 473 474__weak_reference(_pthread_attr_setschedpolicy, pthread_attr_setschedpolicy); 475 476int 477_pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) 478{ 479 int ret = 0; 480 481 if ((attr == NULL) || (*attr == NULL)) 482 ret = EINVAL; 483 else if ((policy < SCHED_FIFO) || (policy > SCHED_RR)) { 484 ret = ENOTSUP; 485 } else { 486 (*attr)->sched_policy = policy; 487 (*attr)->prio = _thr_priorities[policy-1].pri_default; 488 } 489 return(ret); 490} 491 492__weak_reference(_pthread_attr_setscope, pthread_attr_setscope); 493 494int 495_pthread_attr_setscope(pthread_attr_t *attr, int contentionscope) 496{ 497 int ret = 0; 498 499 if ((attr == NULL) || (*attr == NULL)) { 500 /* Return an invalid argument: */ 501 ret = EINVAL; 502 } else if ((contentionscope != PTHREAD_SCOPE_PROCESS) && 503 (contentionscope != PTHREAD_SCOPE_SYSTEM)) { 504 ret = EINVAL; 505 } else if (contentionscope == PTHREAD_SCOPE_SYSTEM) { 506 (*attr)->flags |= contentionscope; 507 } else { 508 (*attr)->flags &= ~PTHREAD_SCOPE_SYSTEM; 509 } 510 return (ret); 511} 512 513__weak_reference(_pthread_attr_setstack, pthread_attr_setstack); 514 515int 516_pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, 517 size_t stacksize) 518{ 519 int ret; 520 521 /* Check for invalid arguments: */ 522 if (attr == NULL || *attr == NULL || stackaddr == NULL 523 || stacksize < PTHREAD_STACK_MIN) 524 ret = EINVAL; 525 else { 526 /* Save the stack address and stack size */ 527 (*attr)->stackaddr_attr = stackaddr; 528 (*attr)->stacksize_attr = stacksize; 529 ret = 0; 530 } 531 return(ret); 532} 533 534__weak_reference(_pthread_attr_setstackaddr, pthread_attr_setstackaddr); 535 536int 537_pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr) 538{ 539 int ret; 540 541 /* Check for invalid arguments: */ 542 if (attr == NULL || *attr == NULL || stackaddr == NULL) 543 ret = EINVAL; 544 else { 545 /* Save the stack address: */ 546 (*attr)->stackaddr_attr = stackaddr; 547 ret = 0; 548 } 549 return(ret); 550} 551 552__weak_reference(_pthread_attr_setstacksize, pthread_attr_setstacksize); 553 554int 555_pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) 556{ 557 int ret; 558 559 /* Check for invalid arguments: */ 560 if (attr == NULL || *attr == NULL || stacksize < PTHREAD_STACK_MIN) 561 ret = EINVAL; 562 else { 563 /* Save the stack size: */ 564 (*attr)->stacksize_attr = stacksize; 565 ret = 0; 566 } 567 return(ret); 568} 569 570static size_t 571_get_kern_cpuset_size(void) 572{ 573 static int kern_cpuset_size = 0; 574 575 if (kern_cpuset_size == 0) { 576 size_t len; 577 578 len = sizeof(kern_cpuset_size); 579 if (sysctlbyname("kern.smp.maxcpus", &kern_cpuset_size, 580 &len, NULL, 0)) 581 PANIC("failed to get sysctl kern.smp.maxcpus"); 582 583 kern_cpuset_size = (kern_cpuset_size + 7) / 8; 584 } 585 586 return (kern_cpuset_size); 587} 588 589__weak_reference(_pthread_attr_setaffinity_np, pthread_attr_setaffinity_np); 590int 591_pthread_attr_setaffinity_np(pthread_attr_t *pattr, size_t cpusetsize, 592 const cpuset_t *cpusetp) 593{ 594 pthread_attr_t attr; 595 int ret; 596 597 if (pattr == NULL || (attr = (*pattr)) == NULL) 598 ret = EINVAL; 599 else { 600 if (cpusetsize == 0 || cpusetp == NULL) { 601 if (attr->cpuset != NULL) { 602 free(attr->cpuset); 603 attr->cpuset = NULL; 604 attr->cpusetsize = 0; 605 } 606 return (0); 607 } 608 609 if (cpusetsize > attr->cpusetsize) { 610 size_t kern_size = _get_kern_cpuset_size(); 611 if (cpusetsize > kern_size) { 612 size_t i; 613 for (i = kern_size; i < cpusetsize; ++i) { 614 if (((char *)cpusetp)[i]) 615 return (EINVAL); 616 } 617 } 618 void *newset = realloc(attr->cpuset, cpusetsize); 619 if (newset == NULL) 620 return (ENOMEM); 621 attr->cpuset = newset; 622 attr->cpusetsize = cpusetsize; 623 } else { 624 memset(((char *)attr->cpuset) + cpusetsize, 0, 625 attr->cpusetsize - cpusetsize); 626 attr->cpusetsize = cpusetsize; 627 } 628 memcpy(attr->cpuset, cpusetp, cpusetsize); 629 ret = 0; 630 } 631 return (ret); 632} 633 634__weak_reference(_pthread_attr_getaffinity_np, pthread_attr_getaffinity_np); 635int 636_pthread_attr_getaffinity_np(const pthread_attr_t *pattr, size_t cpusetsize, 637 cpuset_t *cpusetp) 638{ 639 pthread_attr_t attr; 640 int ret = 0; 641 642 if (pattr == NULL || (attr = (*pattr)) == NULL) 643 ret = EINVAL; 644 else if (attr->cpuset != NULL) { 645 memcpy(cpusetp, attr->cpuset, MIN(cpusetsize, attr->cpusetsize)); 646 if (cpusetsize > attr->cpusetsize) 647 memset(((char *)cpusetp) + attr->cpusetsize, 0, 648 cpusetsize - attr->cpusetsize); 649 } else { 650 size_t kern_size = _get_kern_cpuset_size(); 651 memset(cpusetp, -1, MIN(cpusetsize, kern_size)); 652 if (cpusetsize > kern_size) 653 memset(((char *)cpusetp) + kern_size, 0, 654 cpusetsize - kern_size); 655 } 656 return (ret); 657} 658