3434#define  NUM_LOAD_CERTS  128
3535#define  NUM_KEYS  16
3636#define  KEY_ALGO  "rsa:2048"
37+ #define  W_PROBABILITY  50
38+ #define  MAX_WRITERS  0
3739#define  RUN_TIME  5
3840#define  QUANTILES  5
3941#define  NONCE_CFG  "file:servercert.pem"
4042#define  CTX_SHARE_THREADS  1
4143
4244static  size_t  timeout_us  =  RUN_TIME  *  1000000 ;
4345static  size_t  quantiles  =  QUANTILES ;
46+ static  size_t  max_writers  =  MAX_WRITERS ;
47+ static  size_t  w_probability  =  W_PROBABILITY  *  65536  / 100 ;
4448
4549enum  verbosity  {
4650    VERBOSITY_TERSE ,
@@ -52,6 +56,11 @@ enum verbosity {
5256    VERBOSITY_MAX__ 
5357};
5458
59+ static  enum  mode  {
60+     MODE_R ,
61+     MODE_RW , /* "MODE_W" is just MODE_RW with 100% write probability */ 
62+ } mode  =  MODE_R ;
63+ 
5564enum  nonce_type  {
5665    NONCE_GENERATED ,
5766    NONCE_PATH ,
@@ -61,6 +70,7 @@ struct call_times {
6170    uint64_t  duration ;
6271    uint64_t  total_count ;
6372    uint64_t  total_found ;
73+     uint64_t  total_added ;
6474    uint64_t  min_count ;
6575    uint64_t  max_count ;
6676    double  avg ;
@@ -84,8 +94,11 @@ struct thread_data {
8494    struct  {
8595        uint64_t  count ;
8696        uint64_t  found ;
97+         uint64_t  added_certs ;
8798        OSSL_TIME  end_time ;
8899    } * q_data ;
100+     size_t  cert_count ;
101+     X509  * * certs ;
89102    X509_STORE_CTX  * ctx ;
90103} * thread_data ;
91104
@@ -718,24 +731,38 @@ do_x509storeissuer(size_t num)
718731    OSSL_TIME  q_end ;
719732    size_t  q  =  0 ;
720733    size_t  count  =  0 ;
734+     size_t  add  =  0 ;
721735    size_t  found  =  0 ;
722736
723737    td -> start_time  =  ossl_time_now ();
724738    duration .t  =  max_time .t  -  td -> start_time .t ;
725739    q_end .t  =  duration .t  / quantiles  +  td -> start_time .t ;
726740
727741    do  {
728-         if  (X509_STORE_CTX_get1_issuer (& issuer , td -> ctx , x509_nonce ) !=  0 ) {
729-             found ++ ;
730-             X509_free (issuer );
742+         if  (td -> cert_count  >  0  &&  (rand () % 65536  <  w_probability )) {
743+             size_t  cert_id  =  add  % td -> cert_count ;
744+ 
745+             if  (!X509_STORE_add_cert (store , td -> certs [cert_id ])) {
746+                 warnx ("thread %zu: Failed to add generated certificate %zu" 
747+                       " to the store" , num , cert_id );
748+             } else  {
749+                 add ++ ;
750+             }
751+         } else  {
752+             if  (X509_STORE_CTX_get1_issuer (& issuer , td -> ctx , x509_nonce ) !=  0 ) {
753+                 found ++ ;
754+                 X509_free (issuer );
755+             }
756+             issuer  =  NULL ;
731757        }
732-          issuer   =   NULL ; 
758+ 
733759        count ++ ;
734760        if  ((count  &  0x3f ) ==  0 ) {
735761            time  =  ossl_time_now ();
736762            if  (time .t  >= q_end .t ) {
737763                td -> q_data [q ].count  =  count ;
738764                td -> q_data [q ].found  =  found ;
765+                 td -> q_data [q ].added_certs  =  add ;
739766                td -> q_data [q ].end_time  =  time ;
740767                q_end .t  =  (duration .t  *  (++ q  +  1 )) / quantiles  +  td -> start_time .t ;
741768            }
@@ -744,6 +771,7 @@ do_x509storeissuer(size_t num)
744771
745772    td -> q_data [quantiles  -  1 ].count  =  count ;
746773    td -> q_data [quantiles  -  1 ].found  =  found ;
774+     td -> q_data [quantiles  -  1 ].added_certs  =  add ;
747775    td -> q_data [quantiles  -  1 ].end_time  =  time ;
748776}
749777
@@ -790,17 +818,21 @@ get_calltimes(struct call_times *times, int verbosity)
790818                (q  ? thread_data [i ].q_data [q  -  1 ].count  : 0 );
791819            uint64_t  found  =  thread_data [i ].q_data [q ].found  - 
792820                (q  ? thread_data [i ].q_data [q  -  1 ].found  : 0 );
821+             uint64_t  add  =  thread_data [i ].q_data [q ].added_certs  - 
822+                 (q  ? thread_data [i ].q_data [q  -  1 ].added_certs  : 0 );
793823
794824            times [q ].duration  +=  thread_data [i ].q_data [q ].end_time .t  -  start_t ;
795825            times [q ].total_count  +=  count ;
796826            times [q ].total_found  +=  found ;
827+             times [q ].total_added  +=  add ;
797828        }
798829    }
799830
800831    for  (size_t  q  =  0 ; q  <  quantiles ; q ++ ) {
801832        times [quantiles ].duration  +=  times [q ].duration ;
802833        times [quantiles ].total_count  +=  times [q ].total_count ;
803834        times [quantiles ].total_found  +=  times [q ].total_found ;
835+         times [quantiles ].total_added  +=  times [q ].total_added ;
804836    }
805837
806838    for  (size_t  q  =  (quantiles  ==  1 ); q  <= quantiles ; q ++ )
@@ -885,14 +917,18 @@ report_result(int verbosity)
885917            printf (": avg: %9.3lf us, median: %9.3lf us" 
886918                   ", min: %9.3lf us @thread %3zu, max: %9.3lf us @thread %3zu" 
887919                   ", stddev: %9.3lf us (%8.4lf%%)" 
888-                    ", hits %9zu of %9zu (%8.4lf%%)\n" ,
920+                    ", hits %9zu of %9zu (%8.4lf%%)" 
921+                    ", added certs: %zu\n" ,
889922                   times [i ].avg , times [i ].median ,
890923                   times [i ].min , times [i ].min_idx ,
891924                   times [i ].max , times [i ].max_idx ,
892925                   times [i ].stddev ,
893926                   100.0  *  times [i ].stddev  / times [i ].avg ,
894-                    times [i ].total_found , times [i ].total_count ,
895-                    100.0  *  times [i ].total_found  / (times [i ].total_count ));
927+                    times [i ].total_found ,
928+                    (times [i ].total_count  -  times [i ].total_added ),
929+                    100.0  *  times [i ].total_found 
930+                            / (times [i ].total_count  -  times [i ].total_added ),
931+                    times [i ].total_added );
896932        }
897933        break ;
898934    }
@@ -904,8 +940,8 @@ usage(char * const argv[])
904940    fprintf (stderr ,
905941            "Usage: %s [-t] [-v] [-q N] [-T time] [-G num] [-g num] " 
906942            "[-k num_keys] [-K keyalg[:bits][:param=value...]] " 
907-             "[-n nonce_type:type_args] " 
908-             "[-C threads] certsdir [certsdir...] threadcount\n" 
943+             "[-n nonce_type:type_args] [-m mode] [-w writer_threads]  " 
944+             "[-W percentage] [- C threads] certsdir [certsdir...] threadcount\n" 
909945            "\t-t\tTerse output\n" 
910946            "\t-v\tVerbose output.  Multiple usage increases verbosity.\n" 
911947            "\t-q\tGather information about temporal N-quantiles.\n" 
@@ -928,6 +964,13 @@ usage(char * const argv[])
928964            "\t\t\tfile:PATH - load nonce certificate from PATH;\n" 
929965            "\t\t\tif PATH is relative, the provided certsdir's are searched.\n" 
930966            "\t\tDefault: "  NONCE_CFG  "\n" 
967+             "\t-m\tTest mode, can be one of r, rw.  Default: r\n" 
968+             "\t-w\tMaximum number of threads that attempt addition\n" 
969+             "\t\tof the new certificates to the store in rw mode,\n" 
970+             "\t\t0 is unlimited.  Default: "  OPENSSL_MSTR (MAX_WRITERS ) "\n" 
971+             "\t-W\tProbability of a certificate being written\n" 
972+             "\t\tto the store, instead of being queried,\n" 
973+             "\t\tin percents.  Default: "  OPENSSL_MSTR (W_PROBABILITY ) "\n" 
931974            "\t-C\tNumber of threads that share the same X.509\n" 
932975            "\t\tstore context object.  Default: " 
933976            OPENSSL_MSTR (CTX_SHARE_THREADS ) "\n" 
@@ -951,8 +994,23 @@ parse_timeout(const char * const optarg)
951994    return  timeout_s  *  1e6 ;
952995}
953996
997+ static  double 
998+ parse_probability (const  char  *  const  optarg )
999+ {
1000+     char  * endptr  =  NULL ;
1001+     double  prob ;
1002+ 
1003+     prob  =  strtod (optarg , & endptr );
1004+ 
1005+     if  (endptr  ==  NULL  ||  * endptr  !=  '\0'  ||  prob  <  0  ||  prob  >  100 )
1006+         errx (EXIT_FAILURE , "incorrect probability value: \"%s\"" , optarg );
1007+ 
1008+     return  prob ;
1009+ }
1010+ 
9541011/** 
9551012 * Parse nonce configuration string. Currently supported formats: 
1013+  *  * "gen" - generate a nonce certificate 
9561014 *  * "file:PATH" - where PATH is either a relative path (that will be then 
9571015 *                  checked against the list of directories provided), 
9581016 *                  or an absolute one. 
@@ -1014,7 +1072,7 @@ main(int argc, char *argv[])
10141072
10151073    parse_nonce_cfg (NONCE_CFG , & nonce_cfg );
10161074
1017-     while  ((opt  =  getopt (argc , argv , "tvq:T:G:g:k:K:n :C:" )) !=  -1 ) {
1075+     while  ((opt  =  getopt (argc , argv , "tvq:T:G:g:k:K:m:n:w:W :C:" )) !=  -1 ) {
10181076        switch  (opt ) {
10191077        case  't' : /* terse */ 
10201078            verbosity  =  VERBOSITY_TERSE ;
@@ -1051,9 +1109,24 @@ main(int argc, char *argv[])
10511109        case  'K' : /* key type */ 
10521110            gen_key_algo  =  optarg ;
10531111            break ;
1112+         case  'm' : /* mode */ 
1113+             if  (strcasecmp (optarg , "r" ) ==  0 ) {
1114+                 mode  =  MODE_R ;
1115+             } else  if  (strcasecmp (optarg , "rw" ) ==  0 ) {
1116+                 mode  =  MODE_RW ;
1117+             } else  {
1118+                 errx (EXIT_FAILURE , "Unknown mode: \"%s\"" , optarg );
1119+             }
1120+             break ;
10541121        case  'n' : /* nonce */ 
10551122            parse_nonce_cfg (optarg , & nonce_cfg );
10561123            break ;
1124+         case  'w' : /* maximum writers */ 
1125+             max_writers  =  parse_int (optarg , 0 , INT_MAX ,
1126+                                     "maximum number of writers" );
1127+         case  'W' : /* percent of writes */ 
1128+             w_probability  =  (size_t ) (parse_probability (optarg ) *  65536  / 100 );
1129+             break ;
10571130        case  'C' : /* how many threads share X509_STORE_CTX */ 
10581131            ctx_share_cnt  =  parse_int (optarg , 1 , INT_MAX ,
10591132                                      "X509_STORE_CTX share degree" );
@@ -1074,6 +1147,11 @@ main(int argc, char *argv[])
10741147    if  (num_gen_certs  <  num_gen_load_certs )
10751148        errx (EXIT_FAILURE , "Cannot load more certificates than generate" );
10761149
1150+     if  (num_gen_certs  ==  num_gen_load_certs  &&  mode  ==  MODE_RW )
1151+         errx (EXIT_FAILURE , "No generated certificates to use after" 
1152+                            " the initially loaded ones, please increase" 
1153+                            " -G to be more than -g" );
1154+ 
10771155    if  (argv [optind ] ==  NULL )
10781156        errx (EXIT_FAILURE , "certsdir is missing" );
10791157
@@ -1177,6 +1255,23 @@ main(int argc, char *argv[])
11771255        }
11781256    }
11791257
1258+     if  (mode  ==  MODE_RW ) {
1259+         size_t  writers  =  max_writers  ? OSSL_MIN (max_writers , threadcount )
1260+                                      : threadcount ;
1261+         size_t  cnt  =  num_gen_certs  -  num_gen_load_certs ;
1262+ 
1263+         /* 
1264+          * Point each writer thread at the part of the generated certs 
1265+          * array it uses for store population. 
1266+          */ 
1267+         for  (size_t  i  =  0 ; i  <  writers ; i ++ ) {
1268+             size_t  offs  =  (cnt  *  i ) / writers ;
1269+ 
1270+             thread_data [i ].certs  =  gen_certs  +  num_gen_load_certs  +  offs ;
1271+             thread_data [i ].cert_count  =  OSSL_MAX (cnt  / writers , 1 );
1272+         }
1273+     }
1274+ 
11801275    max_time  =  ossl_time_add (ossl_time_now (), ossl_us2time (timeout_us ));
11811276
11821277    if  (!perflib_run_multi_thread_test (do_x509storeissuer , threadcount , & duration ))
@@ -1185,6 +1280,9 @@ main(int argc, char *argv[])
11851280    if  (error )
11861281        errx (EXIT_FAILURE , "Error during test" );
11871282
1283+     if  (mode  ==  MODE_RW )
1284+         report_store_size (store , "after the test run" , verbosity );
1285+ 
11881286    report_result (verbosity );
11891287
11901288    ret  =  EXIT_SUCCESS ;
0 commit comments