1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 #include <stdio.h>
  29 #include <stdio_ext.h>
  30 #include <stdlib.h>
  31 #include <unistd.h>
  32 #include <sys/types.h>
  33 #include <sys/time.h>
  34 #include <fcntl.h>
  35 #include <sys/fcntl.h>
  36 #include <errno.h>
  37 #include <string.h>
  38 #include <limits.h>
  39 #include <sys/socket.h>
  40 #include <net/if.h>
  41 #include <netinet/in.h>
  42 #include <arpa/inet.h>
  43 #include <thread.h>
  44 #include <tnf/probe.h>
  45 
  46 #include <netinet/dhcp.h>
  47 #include <locale.h>
  48 #include <signal.h>
  49 #include <tnf/probe.h>
  50 
  51 #include <dhcp_svc_confopt.h>
  52 #include <dhcp_svc_private.h>
  53 #include <dhcp_impl.h>
  54 
  55 #ifdef  DEBUG
  56 #include <mtmalloc.h>
  57 #endif                          /* DEBUG */
  58 
  59 /*
  60  * Global variables.
  61  */
  62 int             verbose = 0;
  63 thread_t        *tp;
  64 cond_t          never;
  65 volatile time_t *timp;
  66 volatile int    tms;
  67 char            *fl;
  68 mutex_t         mtx;
  69 mutex_t         thread_mtx;
  70 volatile ulong_t ops_outstanding;
  71 
  72 static volatile ulong_t tops, otops;
  73 static volatile ulong_t minops[6];
  74 static volatile time_t mintim[6];
  75 static volatile int minind;
  76 long            sample_time = 10L;
  77 long            nsamples = 2;
  78 
  79 static volatile time_t start, ostart;
  80 volatile time_t ustart;
  81 volatile int    time_to_go;
  82 volatile int    spawn_helper;
  83 char            b[1024 * 1024];
  84 volatile double slp;
  85 volatile int    worktype;
  86 thread_t        sigthread;
  87 volatile int    old, new, unstarted;
  88 volatile uint_t threads;
  89 volatile unsigned int douwork = 0;
  90 volatile int    dofsync = 0;
  91 volatile int    domalloc = 0;
  92 volatile int    dofork = 0;
  93 thread_t        opnthread;
  94 volatile int    doopen = 0;
  95 
  96 dsvc_datastore_t datastore;     /* Datastore for container access */
  97 
  98 #define MAXTABLE        1024
  99 int             ntable;
 100 
 101 dn_rec_list_t  *thread_dncp[MAXTABLE];
 102 dsvc_handle_t   dh[MAXTABLE];   /* data handle */
 103 struct in_addr  net[MAXTABLE];
 104 uint_t          nrecords[MAXTABLE];
 105 char            *network;
 106 
 107 typedef struct work {
 108         boolean_t       isthreaded;
 109         int             thread;
 110         cond_t          cv;
 111         mutex_t         mtx;
 112         dn_rec_t        *dnp;
 113 }work_t;
 114 
 115 void
 116 free_work_t(work_t *wptr) {
 117         free(wptr->dnp);
 118         free(wptr);
 119 }
 120 /*
 121  * Simulated binary datastore work
 122  */
 123 /* ARGSUSED */
 124 static void    *
 125 uwork(void *argp)
 126 {
 127         int             i;
 128         int             err;
 129         int             fd;
 130         long            block;
 131         work_t          *wptr = argp;
 132         char            *ptr;
 133         size_t          size = ((random() & (domalloc - 1)) + 0x200) &
 134                                 ~(0x200 - 1);
 135         int             wtype;
 136 
 137         if (domalloc)
 138                 ptr = malloc(size);
 139         else
 140                 ptr = b;
 141 
 142         if (wptr->isthreaded) {
 143                 (void) mutex_lock(&wptr->mtx);
 144         }
 145         i = wptr->thread;
 146 
 147         TNF_PROBE_1(uwork, "work", "uwork%debug 'in function work'",
 148                     tnf_long, size, size);
 149 
 150         wtype = worktype == 0 ? random() & 0x7 : worktype;
 151         block = (random() & (douwork - 1)) + (tms / 0x200) + 1;
 152 
 153         /* prewrite legal records */
 154         if (timp[i] == NULL && ustart == 0) {
 155                 ustart = time(NULL);
 156                 wtype = 4;
 157                 block = (tms / 0x200) + 1;
 158                 size = sizeof (b);
 159                 ptr = b;
 160         }
 161         timp[i] = time(NULL);
 162         fd = open(fl, O_RDWR);
 163         (void) write(fd, (char *)timp, tms);
 164         (void) close(fd);
 165 
 166         if (wtype == 4) {
 167 
 168                 TNF_PROBE_2(uwork_write, "work",
 169                             "uwork_write%debug 'in function work'",
 170                             tnf_long, block, block,
 171                             tnf_long, size, size);
 172 
 173                 fd = open(fl, O_RDWR);
 174                 (void) lseek(fd, block * 0x200, 0L);
 175                 err = write(fd, ptr, size);
 176                 (void) close(fd);
 177 
 178                 TNF_PROBE_1(uwork_write_end, "work",
 179                             "uwork_write_end%debug 'in function work'",
 180                             tnf_long, err, err);
 181 
 182         } else if (wtype == 3 && dofsync) {
 183 
 184                 TNF_PROBE_0(uwork_fsync, "work",
 185                             "uwork_fsync%debug 'in function work'");
 186 
 187                 fd = open(fl, O_RDWR);
 188                 err = fsync(fd);
 189                 (void) close(fd);
 190 
 191                 TNF_PROBE_1(uwork_fsync_end, "work",
 192                             "uwork_fsync_end%debug 'in function work'",
 193                             tnf_long, err, err);
 194         } else {
 195                 TNF_PROBE_2(uwork_read, "work",
 196                             "uwork_read%debug 'in function work'",
 197                             tnf_long, block, block,
 198                             tnf_long, size, size);
 199 
 200                 fd = open(fl, O_RDWR);
 201                 (void) lseek(fd, block * 0x200, 0L);
 202                 err = read(fd, ptr, size);
 203                 (void) close(fd);
 204 
 205                 TNF_PROBE_1(uwork_read_end, "work",
 206                             "uwork_read_end%debug 'in function work'",
 207                             tnf_long, err, err);
 208 
 209         }
 210         if (domalloc && ptr != b)
 211                 free(ptr);
 212 
 213         if (wptr->isthreaded) {
 214                 (void) mutex_unlock(&wptr->mtx);
 215                 cond_signal(&wptr->cv);
 216                 TNF_PROBE_0(work_end, "work", "");
 217                 thr_exit(NULL);
 218         }
 219         TNF_PROBE_0(uwork_end, "work", "");
 220 
 221         return ((void *) NULL);
 222 }
 223 
 224 /*
 225  * Simulated datastore work
 226  */
 227 static void    *
 228 work(void *argp)
 229 {
 230         int             i, j;
 231         dn_rec_t        *dnp;
 232         int             err;
 233         work_t          *wptr = argp;
 234         uchar_t         cid_len;
 235         char            *ptr;
 236         uint32_t        query;
 237         dn_rec_t        dn, ndn;
 238         dn_rec_list_t   *dncp = NULL;
 239         uint_t          crecords, irecords;
 240         int             wtype;
 241         int             firsttime = 0;
 242         int             op;
 243         size_t          size = ((random() & (domalloc - 1)) + 0x100) &
 244                         ~(0x1000 - 1);
 245         int             table;
 246 
 247         if (domalloc)
 248                 ptr = malloc(size);
 249         else
 250                 ptr = b;
 251 
 252         irecords = (random() & 0xff) + 1;
 253         if (irecords == 12) {
 254                 irecords = (uint_t)-1;
 255         }
 256         if (wptr->isthreaded) {
 257                 (void) mutex_lock(&wptr->mtx);
 258         }
 259         i = wptr->thread;
 260         dnp = wptr->dnp;
 261 
 262         table = i % ntable;
 263         dn = *dnp;
 264 
 265         cid_len = 7;
 266 
 267         if (worktype == 0) {
 268                 wtype = random() & 0x7;
 269                 if (wtype == 4)
 270                         wtype--;
 271         } else
 272                 wtype = worktype;
 273 
 274         /* preload a legal record */
 275         if (timp[i] == NULL) {
 276                 wtype = 3;
 277                 firsttime = 1;
 278                 irecords = threads * 2;
 279                 (void) mutex_lock(&thread_mtx);
 280                 if ((dncp = thread_dncp[table]) != NULL) {
 281                         thread_dncp[table] = dncp->dnl_next;
 282                         *dnp = *(dncp->dnl_rec);
 283                         dncp->dnl_next = NULL;
 284                         wtype = -1;
 285                         (void) mutex_unlock(&thread_mtx);
 286                 }
 287         }
 288         TNF_PROBE_2(work, "work", "work%debug 'in function work'",
 289                     tnf_ulong, worktype, wtype,
 290                     tnf_ulong, irecords, irecords);
 291 
 292         timp[i] = time(NULL);
 293         crecords = 0;
 294         DSVC_QINIT(query);
 295         switch (wtype) {
 296         case -1:
 297                 break;
 298         case 1:
 299                 switch (random() & 0x7) {
 300                 case 1:
 301                         for (j = 0; j < cid_len; j++)
 302                                 dn.dn_cid[j] = random() & 0xff;
 303                         break;
 304                 case 2:
 305                         for (j = 0; j < cid_len; j++)
 306                                 dn.dn_cid[j] = '\0';
 307                         dn.dn_cid_len = 1;
 308                         break;
 309                 }
 310                 DSVC_QEQ(query, DN_QCID);
 311 
 312                 /* LINTED */
 313                 TNF_PROBE_2(work_cid, "work work_cid",
 314                             "work_cid%debug 'in function work'",
 315                             tnf_ulong, cid, *(ulong_t *)&dn.dn_cid,
 316                             tnf_ulong, cid_len, dn.dn_cid_len);
 317 
 318                 err = lookup_dd(dh[table], B_TRUE, query, -1,
 319                             (const void *)&dn, (void **)&dncp, &crecords);
 320 
 321                 TNF_PROBE_2(work_cid_end, "work work_cid",
 322                             "work_cid_end%debug 'in function work'",
 323                             tnf_ulong, err, err,
 324                             tnf_ulong, crecords, crecords);
 325 
 326                 if (crecords > 0 && dncp)
 327                         *dnp = *(dncp->dnl_rec);
 328                 break;
 329 
 330         case 2:
 331                 switch (random() & 0x7) {
 332                 case 1:
 333                         dn.dn_cip.s_addr = random();
 334                         break;
 335                 case 2:
 336                         dn.dn_cip.s_addr = net[table].s_addr |
 337                                 (random() & (nrecords[table] - 1));
 338                         break;
 339                 }
 340 
 341                 DSVC_QEQ(query, DN_QCIP);
 342 
 343                 TNF_PROBE_1(work_cip, "work work_cip",
 344                             "work_cip%debug 'in function work'",
 345                             tnf_ulong, cip, dn.dn_cip.s_addr);
 346 
 347                 err = lookup_dd(dh[table], B_TRUE, query, -1,
 348                             (const void *)&dn, (void **)&dncp, &crecords);
 349 
 350                 TNF_PROBE_2(work_cip_end, "work work_cip",
 351                             "work_cip_end%debug 'in function work'",
 352                             tnf_ulong, err, err,
 353                             tnf_ulong, crecords, crecords);
 354 
 355                 if (crecords > 0 && dncp)
 356                         *dnp = *(dncp->dnl_rec);
 357                 break;
 358         case 3:
 359                 op = random() & 0x7;
 360                 if (firsttime)
 361                         op = 2;
 362 
 363                 switch (op) {
 364                 case 1:
 365                         DSVC_QNEQ(query, DN_QLEASE);
 366                         dn.dn_lease = 0;
 367                         break;
 368                 case 2:
 369                         DSVC_QEQ(query, DN_QCID);
 370                         for (j = 0; j < cid_len; j++)
 371                                 dn.dn_cid[j] = '\0';
 372                         dn.dn_cid_len = 1;
 373                         break;
 374                 }
 375 
 376                 TNF_PROBE_2(work_read, "work work_read",
 377                             "work_read%debug 'in function work'",
 378                             tnf_ulong, query, query,
 379                             tnf_ulong, cid_len, dn.dn_cid_len);
 380 
 381                 err = lookup_dd(dh[table], B_TRUE, query, irecords,
 382                             (const void *)&dn, (void **)&dncp, &crecords);
 383 
 384                 TNF_PROBE_2(work_read_end, "work work_read",
 385                             "work_read_end%debug 'in function work'",
 386                             tnf_ulong, err, err,
 387                             tnf_ulong, crecords, crecords);
 388 
 389                 if (crecords > 0 && dncp) {
 390                         *dnp = *(dncp->dnl_rec);
 391                         if (firsttime) {
 392                                 thread_dncp[table] = dncp->dnl_next;
 393                                 dncp->dnl_next = NULL;
 394                                 mutex_unlock(&thread_mtx);
 395                         }
 396                 }
 397                 break;
 398         case 4:
 399                 op = dnp->dn_lease & 0x3;
 400                 switch (op) {
 401                 case 0:
 402                         /* write record w/ cid */
 403                         ndn = *dnp;
 404                         ndn.dn_lease = (htonl(time(NULL)) & ~0x3) + 1;
 405                         ndn.dn_cid_len = 14;
 406                         for (j = 0; j < ndn.dn_cid_len; j++)
 407                                 ndn.dn_cid[j] = random() & 0xff;
 408 
 409                         /* LINTED */
 410                         TNF_PROBE_2(work1_modify, "work work1_modify",
 411                                     "work1_modify%debug 'in function work'",
 412                                     tnf_ulong, cid, *(ulong_t *)&ndn.dn_cid,
 413                                     tnf_ulong, cid_len, ndn.dn_cid_len);
 414 
 415                         err = modify_dd_entry(dh[table], dnp, &ndn);
 416                         if (err != DSVC_SUCCESS && verbose) {
 417                                 fprintf(stderr, "work: %d %d error %d\n",
 418                                         wtype, op, err);
 419                         }
 420 
 421                         TNF_PROBE_1(work1_modify_end, "work work1_modify_end",
 422                                     "work1_modify_end%debug 'in function work'",
 423                                     tnf_ulong, err, err);
 424                         *dnp = ndn;
 425                         break;
 426                 case 1:
 427                         /* re-read record w/ cid */
 428                         DSVC_QEQ(query, DN_QCID);
 429                         TNF_PROBE_2(work_read1, "work work_read1",
 430                                     "work_read1%debug 'in function work'",
 431                                     tnf_ulong, query, query,
 432                                     tnf_ulong, cid_len, dn.dn_cid_len);
 433 
 434                         err = lookup_dd(dh[table], B_TRUE, query, - 1,
 435                                         (const void *)dnp, (void **)&dncp,
 436                                         &crecords);
 437                         TNF_PROBE_2(work_read1_end, "work work_read1",
 438                                     "work_read1_end%debug 'in function work'",
 439                                     tnf_ulong, err, err,
 440                                     tnf_ulong, crecords, crecords);
 441 
 442                         if ((err != DSVC_SUCCESS || crecords < 1) && verbose) {
 443                                 fprintf(stderr, "work: %d %d error %d %d\n",
 444                                         wtype, op, err, crecords);
 445                         }
 446                         dnp->dn_lease++;
 447                         break;
 448                 case 2:
 449                         /* write free record */
 450                         dnp->dn_lease--;
 451                         ndn = *dnp;
 452                         DSVC_QEQ(query, DN_QCID);
 453                         for (j = 0; j < cid_len; j++)
 454                                 ndn.dn_cid[j] = '\0';
 455                         ndn.dn_cid_len = 1;
 456                         ndn.dn_lease = 0;
 457 
 458                         TNF_PROBE_2(work_modify2, "work work_modify2",
 459                                     "work_modify2%debug 'in function work'",
 460                                     tnf_ulong, cid, *(ulong_t *)&ndn.dn_cid,
 461                                     tnf_ulong, cid_len, ndn.dn_cid_len);
 462 
 463                         err = modify_dd_entry(dh[table], dnp, &ndn);
 464 
 465                         TNF_PROBE_1(work_modify2_end, "work work_modify2_end",
 466                             "work_modify2_end%debug 'in function work'",
 467                                     tnf_ulong, err, err);
 468 
 469                         if (err != DSVC_SUCCESS && verbose) {
 470                                 fprintf(stderr, "work: %d %d error %d\n",
 471                                         wtype, op, err);
 472                         }
 473                         *dnp = ndn;
 474                         break;
 475                 }
 476                 break;
 477 
 478 
 479         default:
 480                 ndn = *dnp;
 481                 ndn.dn_cid_len = cid_len;
 482                 switch (random() & 0x1) {
 483                 case 0:
 484                         for (j = 0; j < cid_len; j++)
 485                                 ndn.dn_cid[j] = random() & 0xff;
 486                         break;
 487                 case 1:
 488                         for (j = 0; j < cid_len; j++)
 489                                 ndn.dn_cid[j] = '\0';
 490                         ndn.dn_cid_len = 1;
 491                         break;
 492                 }
 493                 ndn.dn_lease = htonl(time(NULL));
 494 
 495                 /* LINTED */
 496                 TNF_PROBE_2(work_modify, "work work_modify",
 497                             "work_modify%debug 'in function work'",
 498                             tnf_ulong, cid, *(ulong_t *)&ndn.dn_cid,
 499                             tnf_ulong, cid_len, ndn.dn_cid_len);
 500 
 501                 err = modify_dd_entry(dh[table], dnp, &ndn);
 502                 if (err != DSVC_SUCCESS && err != DSVC_COLLISION) {
 503                         if (verbose)
 504                                 fprintf(stderr, "modify: error %d\n", err);
 505                 }
 506 
 507                 TNF_PROBE_1(work_modify_end, "work work_modify_end",
 508                             "work_modify_end%debug 'in function work'",
 509                             tnf_ulong, err, err);
 510 
 511                 *dnp = ndn;
 512                 break;
 513         }
 514 
 515         if (domalloc)
 516                 free(ptr);
 517 
 518         if (wptr->isthreaded) {
 519                 (void) mutex_unlock(&wptr->mtx);
 520                 cond_signal(&wptr->cv);
 521                 TNF_PROBE_2(work_end, "work", "work_end%debug 'in function "
 522                                 "work'", tnf_ulong, err, err,
 523                                 tnf_ulong, crecords, crecords);
 524                 thr_exit(NULL);
 525         }
 526         if (dncp)
 527                 free_dd_list(dh[table], dncp);
 528 
 529         TNF_PROBE_2(work_end, "work", "work_end%debug 'in function work'",
 530                     tnf_ulong, err, err,
 531                     tnf_ulong, crecords, crecords);
 532 
 533         return ((void *) NULL);
 534 }
 535 
 536 /*
 537  * Worker thread.
 538  */
 539 static void    *
 540 dowork(void *argp)
 541 {
 542         int             i = (int)argp;
 543         timestruc_t     to;
 544         work_t          *wptr;
 545         dn_rec_t        dn;
 546 
 547         (void) memset((char *)&dn, '\0', sizeof (dn));
 548         (void) mutex_lock(&mtx);
 549         for (; time_to_go == 0; ) {
 550                 TNF_PROBE_1(dowork, "dowork",
 551                             "dowork%debug 'in function dowork'",
 552                             tnf_long, thread_number, i);
 553 
 554                 to.tv_sec = time(NULL) + random() & 0x3;
 555                 to.tv_nsec = 0;
 556 
 557                 if (slp > 0.0) {
 558                         to.tv_sec = time(NULL) + slp;
 559                         to.tv_nsec = (slp - (double)((int)slp)) * 1000000000.0;
 560                 } else if (slp < 0.0) {
 561                         to.tv_sec = time(NULL) + abs((int)slp);
 562                         to.tv_nsec = (slp + abs((double)((int)slp))) *
 563                                 1000000000.0;
 564                 }
 565                 /* give up processor */
 566                 if (slp != 0.0) {
 567                         (void) mutex_unlock(&mtx);
 568                         (void) cond_timedwait(&never, &mtx, &to);
 569                 }
 570                 ops_outstanding++;
 571                 (void) mutex_unlock(&mtx);
 572 
 573                 if (spawn_helper) {
 574                         wptr = (work_t *)malloc(sizeof (work_t));
 575                         wptr->thread = i * 2;
 576                         wptr->isthreaded = B_TRUE;
 577                         (void) cond_init(&wptr->cv, USYNC_THREAD, NULL);
 578                         (void) mutex_init(&wptr->mtx, USYNC_THREAD, NULL);
 579                         (void) mutex_lock(&wptr->mtx);
 580 
 581                         /* fire up helper thread */
 582                         if (thr_create(NULL, 0, douwork ? uwork : work,
 583                                         (void *)wptr, 0, &tp[i * 2]) != 0)
 584                                 fprintf(stderr, "can't spawn lthread %d\n", i);
 585 
 586                         /* wait for completion */
 587                         (void) cond_wait(&wptr->cv, &wptr->mtx);
 588                         (void) mutex_unlock(&wptr->mtx);
 589                         (void) thr_join(tp[i * 2], NULL, NULL);
 590                         free_work_t(wptr);
 591                 } else {
 592                         wptr = (work_t *)malloc(sizeof (work_t));
 593                         wptr->isthreaded = B_FALSE;
 594                         wptr->thread = i;
 595                         wptr->dnp = &dn;
 596                         if (douwork) {
 597                                 (void) uwork((void *)wptr);
 598                         } else {
 599                                 (void) work((void *)wptr);
 600                         }
 601                         free_work_t(wptr);
 602                 }
 603                 (void) mutex_lock(&mtx);
 604                 tops++;
 605                 ops_outstanding--;
 606                 TNF_PROBE_0(dowork_end, "dowork", "");
 607         }
 608         (void) mutex_unlock(&mtx);
 609         thr_exit(NULL);
 610 
 611         return ((void *) NULL);
 612 }
 613 
 614 /*
 615  * Signal handler routine. All signals handled by calling thread.
 616  */
 617 /* ARGSUSED */
 618 static void    *
 619 sig_handle(void *arg)
 620 {
 621         int             i;
 622         int             sig;
 623         sigset_t        set;
 624         timespec_t      ts;
 625         siginfo_t       si;
 626         int             go;
 627         int             oldi;
 628         ulong_t         minavg;
 629         time_t          minstime;
 630 
 631         (void) sigfillset(&set); /* catch all signals */
 632 
 633         ts.tv_sec = sample_time;
 634         ts.tv_nsec = 0L;
 635 
 636         for (;;) {
 637                 (void) mutex_lock(&mtx);
 638                 go = time_to_go;
 639                 (void) mutex_unlock(&mtx);
 640                 if (go)
 641                         break;
 642 
 643                 switch (sig = sigtimedwait(&set, &si, &ts)) {
 644                 case -1:
 645                 case SIGHUP:
 646                         old = time(NULL);
 647                         oldi = new = unstarted = 0;
 648                         for (i = 0; i < threads; i++) {
 649                                 if (timp[i] == NULL)
 650                                         unstarted++;
 651                                 if (timp[i] && timp[i] < old) {
 652                                         old = timp[i];
 653                                         oldi = i;
 654                                 }
 655                                 if (timp[i] && timp[i] > new)
 656                                         new = timp[i];
 657                         }
 658 
 659                         if (start == 0) {
 660                                 /* toss initial sample */
 661                                 ostart = start = time(NULL);
 662                                 (void) mutex_lock(&mtx);
 663                                 otops = tops = 0;
 664                                 (void) mutex_unlock(&mtx);
 665                                 minind = 0;
 666                         } else {
 667                                 minops[minind] = tops - otops;
 668                                 mintim[minind] = ostart;
 669                                 otops = tops;
 670                                 ostart = time(NULL);
 671                                 minind = minind + 1 > nsamples - 1 ? 0 :
 672                                     minind + 1;
 673                                 minstime = 0;
 674                                 minavg = 0;
 675                                 for (i = 0; i < nsamples; i++) {
 676                                         if (mintim[i])
 677                                                 minavg += minops[i];
 678                                         if (minstime == 0)
 679                                                 minstime = mintim[i];
 680                                         else if (mintim[i] &&
 681                                             mintim[i] < minstime)
 682                                                 minstime = mintim[i];
 683                                 }
 684 
 685                                 fprintf(stderr, "%9.9d: Totops %d Curr %d "\
 686                                     "Persec %4.2f (%4.2f) Oldest %d (%d) "\
 687                                     "Gap %d Unstarted %d\n",
 688                                         time(NULL),
 689                                         tops,
 690                                         ops_outstanding,
 691                                         (double)tops / (double)(time(NULL)
 692                                                 - start),
 693                                         (double)minavg / (double)(time(NULL)
 694                                                 - minstime),
 695                                         time(NULL) - old,
 696                                         oldi,
 697                                         new - old,
 698                                         unstarted);
 699                         }
 700                         break;
 701                 default:
 702                         (void) mutex_lock(&mtx);
 703                         time_to_go++;
 704                         (void) mutex_unlock(&mtx);
 705                         break;
 706                 }
 707         }
 708         thr_exit(NULL);
 709         return ((void *) sig);  /* NOTREACHED */
 710 }
 711 
 712 int     fd[0x10000];
 713 /*
 714  * open handler routine.
 715  */
 716 /* ARGSUSED */
 717 static void    *
 718 open_handle(void *arg)
 719 {
 720         int     i;
 721 
 722         for (;;) {
 723                 for (i = 0; i < doopen; i++)
 724                         fd[i] = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
 725                 for (i = 0; i < doopen; i++)
 726                         if (fd[i] >= 0)
 727                                 (void) close(fd[i]);
 728         }
 729         return ((void *) NULL); /* NOTREACHED */
 730 }
 731 /*
 732  * test_dstore network[,network] worktype[,worktype] <thr_create flags>
 733  *       <spawn_helper> <nlwp> <nthread> <file> <sleeptype>
 734  *
 735  * network - list of network containers, comma-separated
 736  * worktypes:
 737  *      0 - random
 738  *      1 - cid reads
 739  *      2 - cip reads
 740  *      3 - whole db reads
 741  *      4 - write read write (simulate simple test)
 742  *      5 - modify writes
 743  * sleeptypes:
 744  *      N == * condwait N sec.nsec period
 745  *      -N == condwait a random 1-N sec.nsec period
 746  */
 747 main(int c, char **v)
 748 {
 749         int             i;
 750         timespec_t      to;
 751         uint_t          flags;
 752         int             err;
 753         sigset_t        set;
 754         dhcp_confopt_t *dsp = NULL;
 755         uint32_t        query;
 756         dn_rec_t        dn;
 757         dn_rec_list_t  *dncp = NULL;
 758         struct rlimit   rl;
 759         char            *np;
 760 
 761 #ifdef  DEBUG
 762         mallocctl(MTDEBUGPATTERN, 1);
 763         mallocctl(MTINITBUFFER, 1);
 764 #endif                          /* DEBUG */
 765 
 766         srandom(time(NULL));
 767 
 768         if (dofork)
 769                 if (fork() != 0)
 770                         exit(0);
 771 
 772         if ((err = getrlimit(RLIMIT_NOFILE, &rl)) < 0) {
 773                 (void) fprintf(stderr, "Cannot get open file limit: %s\n",
 774                                 strerror(errno));
 775         }
 776         /* handle cases where limit is infinity */
 777         if (rl.rlim_cur == RLIM_INFINITY) {
 778                 rl.rlim_cur = (rl.rlim_max == RLIM_INFINITY) ?
 779                         OPEN_MAX : rl.rlim_max;
 780         }
 781         /* set NOFILE to unlimited */
 782         rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
 783         if ((err = setrlimit(RLIMIT_NOFILE, &rl)) < 0) {
 784                 (void) fprintf(stderr, "Cannot set open file limit: %s\n",
 785                                 strerror(errno));
 786         }
 787         (void) enable_extended_FILE_stdio(-1, -1);
 788 
 789         if (c == 1) {
 790                 (void) fprintf(stderr,
 791                                 "/*\n"\
 792                                 " * test_dstore network[,network] worktype[,"\
 793                                         "worktype] <thr_create flags>\n"\
 794                                 " *      <spawn_helper> <nlwp> <nthread> "\
 795                                         "<file> <sleeptype>\n"\
 796                                 " *\n"\
 797                                 " * network - list of network containers, "\
 798                                         "comma-separated\n"\
 799                                 " * worktypes:\n"\
 800                                 " *     0 - random\n"\
 801                                 " *     1 - cid reads\n"\
 802                                 " *     2 - cip reads\n"\
 803                                 " *     3 - whole db reads\n"\
 804                                 " *     4 - write read write (simulate simple"\
 805                                         " test)\n"\
 806                                 " *     5 - modify writes\n"\
 807                                 " * sleeptypes:\n"\
 808                                 " *     N == * condwait N sec.nsec period\n"\
 809                                 " *     -N == condwait a random 1-N sec.nsec "\
 810                                         "period\n"\
 811                                 " */\n");
 812                 return (0);
 813         }
 814         network = v[1];
 815 
 816         worktype = strtoul(v[2], 0L, 0L);
 817         flags = strtoul(v[3], 0L, 0L);
 818         spawn_helper = strtoul(v[4], 0L, 0L);
 819         if (strtoul(v[5], 0L, 0L) > 0)
 820                 (void) thr_setconcurrency(strtoul(v[5], 0L, 0L));
 821         threads = strtoul(v[6], 0L, 0L);
 822         fl = v[7];
 823         if (c > 8)
 824                 slp = atof(v[8]);
 825 
 826         if (douwork == 0) {
 827                 /* Load current datastore. */
 828                 (void) read_dsvc_conf(&dsp);
 829                 if ((i = confopt_to_datastore(dsp, &datastore))
 830                         != DSVC_SUCCESS) {
 831                         (void) fprintf(stderr, "Invalid datastore: %s\n",
 832                                         dhcpsvc_errmsg(i));
 833                         return (EINVAL);
 834                 }
 835                 for (i = 0, np = strtok(network, ","); np; i++,
 836                         np = strtok(NULL, ",")) {
 837                         net[i].s_addr = inet_addr(np);
 838 
 839                         err = open_dd(&dh[i], &datastore, DSVC_DHCPNETWORK, np,
 840                                         DSVC_READ | DSVC_WRITE);
 841 
 842                         if (err != DSVC_SUCCESS) {
 843                                 (void) fprintf(stderr, "Invalid network: "\
 844                                                 "%s %s\n", np,
 845                                                 dhcpsvc_errmsg(err));
 846                                 return (err);
 847                         }
 848                         /*
 849                          * XXXX: bug: currently can't get the count as
 850                          * advertised
 851                          */
 852                         (void) memset(&dn, '\0', sizeof (dn));
 853                         DSVC_QINIT(query);
 854                         err = lookup_dd(dh[i], B_FALSE, query, -1,
 855                                 (const void *) &dn, (void **) &dncp,
 856                                 &nrecords[i]);
 857                         if (dncp)
 858                                 free_dd_list(dh[i], dncp);
 859 
 860                         if (err != DSVC_SUCCESS) {
 861                                 (void) fprintf(stderr, "Bad nrecords: %s "
 862                                         "[%d]\n", dhcpsvc_errmsg(err),
 863                                         nrecords[i]);
 864                                 return (err);
 865                         }
 866                 }
 867                 ntable = i;
 868         }
 869         TNF_PROBE_2(main, "main",
 870                     "main%debug 'in function main'",
 871                     tnf_ulong, threads, threads,
 872                     tnf_ulong, nrecords, nrecords[i]);
 873 
 874         (void) sigfillset(&set);
 875 
 876         (void) sigdelset(&set, SIGABRT);    /* allow for user abort */
 877 
 878         (void) thr_sigsetmask(SIG_SETMASK, &set, NULL);
 879 
 880         tms = threads * sizeof (thread_t);
 881         if (spawn_helper)
 882                 tms *= 2;
 883         tp = malloc(tms);
 884         tms = (threads * sizeof (time_t) + 0x200) & ~(0x200 - 1);
 885         if (spawn_helper)
 886                 tms *= 2;
 887         timp = malloc(tms);
 888         (void) memset((char *)timp, NULL, tms);
 889 
 890         (void) mutex_init(&mtx, USYNC_THREAD, 0);
 891 
 892         /*
 893          * Create signal handling thread. XXXX: due to threads library
 894          * limitations, this must currently be directly called in the main
 895          * program thread.
 896          */
 897         if ((err = thr_create(NULL, 0, sig_handle, NULL,
 898                                 THR_NEW_LWP | THR_DAEMON | THR_BOUND |
 899                                 THR_DETACHED, &sigthread)) != 0) {
 900                 (void) fprintf(stderr,
 901                 gettext("Cannot start signal handling thread, error: %d\n"),
 902                         err);
 903                 return (err);
 904         }
 905         for (i = 0; i < threads; i++)
 906                 /* fire up monitor thread */
 907                 if (thr_create(NULL, 0, dowork, (void *) i,
 908                         flags, &tp[i]) != 0)
 909                         fprintf(stderr, "can't spawn thread %d\n", i);
 910 
 911         /*
 912          * Create open handling thread.
 913          */
 914         if (doopen && (err = thr_create(NULL, 0, open_handle, NULL,
 915                         THR_NEW_LWP | THR_DAEMON | THR_BOUND | THR_DETACHED,
 916                         &opnthread)) != 0) {
 917                 (void) fprintf(stderr,
 918                 gettext("Cannot start open handling thread, error: %d\n"),
 919                         err);
 920                 return (err);
 921         }
 922 
 923         (void) mutex_lock(&mtx);
 924         for (; time_to_go == 0; ) {
 925                 to.tv_sec = time(NULL) + 10;
 926                 to.tv_nsec = 0L;
 927                 (void) cond_timedwait(&never, &mtx, &to);
 928                 (void) mutex_unlock(&mtx);
 929         }
 930 
 931         /*
 932          * Attempt to join threads.
 933          */
 934         for (i = 0; i < threads; i++)
 935                 (void) thr_join(tp[i], NULL, NULL);
 936 
 937         (void) sleep(5);
 938 
 939         TNF_PROBE_0(main_end, "main", "");
 940 
 941         return (0);
 942 }