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 /*
  23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <sys/types.h>
  28 #include <sys/atomic.h>
  29 #include <sys/systm.h>
  30 #include <sys/socket.h>
  31 #include <netinet/in.h>
  32 #include <sys/modctl.h>
  33 #include <sys/sunddi.h>
  34 #include <ipp/ipp.h>
  35 #include <ipp/ipp_config.h>
  36 #include <inet/common.h>
  37 #include <ipp/meters/meter_impl.h>
  38 
  39 #define D_SM_COMMENT    "IPP Sliding Window Meter"
  40 
  41 /* DDI file for tswtcl ipp module */
  42 
  43 static int tswtcl_create_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
  44 static int tswtcl_modify_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
  45 static int tswtcl_destroy_action(ipp_action_id_t, ipp_flags_t);
  46 static int tswtcl_info(ipp_action_id_t, int (*)(nvlist_t *, void *), void *,
  47     ipp_flags_t);
  48 static int tswtcl_invoke_action(ipp_action_id_t, ipp_packet_t *);
  49 
  50 /* Stats init function */
  51 static int tswtcl_statinit(ipp_action_id_t, tswtcl_data_t *);
  52 
  53 /* Stats callback function */
  54 static int tswtcl_update_stats(ipp_stat_t *, void *, int);
  55 
  56 ipp_ops_t tswtcl_ops = {
  57         IPPO_REV,
  58         tswtcl_create_action,   /* ippo_action_create */
  59         tswtcl_modify_action,   /* ippo_action_modify */
  60         tswtcl_destroy_action,  /* ippo_action_destroy */
  61         tswtcl_info,            /* ippo_action_info */
  62         tswtcl_invoke_action    /* ippo_action_invoke */
  63 };
  64 
  65 extern struct mod_ops mod_ippops;
  66 
  67 /*
  68  * Module linkage information for the kernel.
  69  */
  70 static struct modlipp modlipp = {
  71         &mod_ippops,
  72         D_SM_COMMENT,
  73         &tswtcl_ops
  74 };
  75 
  76 static struct modlinkage modlinkage = {
  77         MODREV_1,
  78         { (void *)&modlipp, NULL }
  79 };
  80 
  81 
  82 int
  83 _init(void)
  84 {
  85         return (mod_install(&modlinkage));
  86 }
  87 
  88 int
  89 _fini(void)
  90 {
  91         return (mod_remove(&modlinkage));
  92 }
  93 
  94 int
  95 _info(struct modinfo *modinfop)
  96 {
  97         return (mod_info(&modlinkage, modinfop));
  98 }
  99 
 100 /* ARGSUSED */
 101 static int
 102 tswtcl_create_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
 103 {
 104         nvlist_t *nvlp;
 105         tswtcl_data_t *tswtcl_data;
 106         tswtcl_cfg_t *cfg_parms;
 107         char *next_action;
 108         uint32_t bstats;
 109         int rc, rc2;
 110 
 111         nvlp = *nvlpp;
 112         *nvlpp = NULL;          /* nvlist should be NULL on return */
 113 
 114 
 115         if ((cfg_parms = kmem_alloc(TSWTCL_CFG_SZ, KM_NOSLEEP)) == NULL) {
 116                 nvlist_free(nvlp);
 117                 return (ENOMEM);
 118         }
 119 
 120         /* parse red next action name */
 121         if ((rc = nvlist_lookup_string(nvlp, TSWTCL_RED_ACTION_NAME,
 122             &next_action)) != 0) {
 123                 nvlist_free(nvlp);
 124                 tswtcl0dbg(("tswtcl_create_action:invalid config, red action" \
 125                     " name missing\n"));
 126                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 127                 return (rc);
 128         }
 129         if ((cfg_parms->red_action = ipp_action_lookup(next_action))
 130             == IPP_ACTION_INVAL) {
 131                 nvlist_free(nvlp);
 132                 tswtcl0dbg(("tswtcl_create_action: red action invalid\n"));
 133                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 134                 return (EINVAL);
 135         }
 136 
 137         /* parse yellow next action name */
 138         if ((rc = nvlist_lookup_string(nvlp, TSWTCL_YELLOW_ACTION_NAME,
 139             &next_action)) != 0) {
 140                 nvlist_free(nvlp);
 141                 tswtcl0dbg(("tswtcl_create_action:invalid config, yellow " \
 142                     "action name missing\n"));
 143                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 144                 return (rc);
 145         }
 146         if ((cfg_parms->yellow_action = ipp_action_lookup(next_action))
 147             == IPP_ACTION_INVAL) {
 148                 nvlist_free(nvlp);
 149                 tswtcl0dbg(("tswtcl_create_action: yellow action invalid\n"));
 150                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 151                 return (EINVAL);
 152         }
 153 
 154         /* parse green next action name */
 155         if ((rc = nvlist_lookup_string(nvlp, TSWTCL_GREEN_ACTION_NAME,
 156             &next_action)) != 0) {
 157                 nvlist_free(nvlp);
 158                 tswtcl0dbg(("tswtcl_create_action:invalid config, green " \
 159                     "action name missing\n"));
 160                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 161                 return (rc);
 162         }
 163         if ((cfg_parms->green_action = ipp_action_lookup(next_action))
 164             == IPP_ACTION_INVAL) {
 165                 nvlist_free(nvlp);
 166                 tswtcl0dbg(("tswtcl_create_action: green action invalid\n"));
 167                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 168                 return (EINVAL);
 169         }
 170 
 171         /* parse committed rate  - in bits / sec */
 172         if ((rc = nvlist_lookup_uint32(nvlp, TSWTCL_COMMITTED_RATE,
 173             &cfg_parms->committed_rate)) != 0) {
 174                 nvlist_free(nvlp);
 175                 tswtcl0dbg(("tswtcl_create_action: invalid config, "\
 176                     " committed rate missing\n"));
 177                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 178                 return (rc);
 179         }
 180 
 181         /* parse peak rate  - in bits / sec */
 182         if ((rc = nvlist_lookup_uint32(nvlp, TSWTCL_PEAK_RATE,
 183             &cfg_parms->peak_rate)) != 0) {
 184                 nvlist_free(nvlp);
 185                 tswtcl0dbg(("tswtcl_create_action: invalid config, "\
 186                     " peak rate missing\n"));
 187                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 188                 return (rc);
 189         }
 190 
 191         if (cfg_parms->peak_rate < cfg_parms->committed_rate) {
 192                 nvlist_free(nvlp);
 193                 tswtcl0dbg(("tswtcl_create_action: invalid config, "\
 194                     " peak rate < committed rate\n"));
 195                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 196                 return (EINVAL);
 197         }
 198 
 199         /* parse window - in msec */
 200         if ((rc = nvlist_lookup_uint32(nvlp, TSWTCL_WINDOW,
 201             &cfg_parms->window)) != 0) {
 202                 nvlist_free(nvlp);
 203                 tswtcl0dbg(("tswtcl_create_action: invalid config, "\
 204                     " window missing\n"));
 205                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 206                 return (rc);
 207         }
 208         /* convert to nsec */
 209         cfg_parms->nsecwindow = (uint64_t)cfg_parms->window *
 210             METER_MSEC_TO_NSEC;
 211 
 212         /* parse stats */
 213         if ((rc = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
 214             != 0) {
 215                 cfg_parms->stats = B_FALSE;
 216         } else {
 217                 cfg_parms->stats = (boolean_t)bstats;
 218         }
 219 
 220         nvlist_free(nvlp);
 221 
 222         /* Initialize other stuff */
 223         tswtcl_data = kmem_zalloc(TSWTCL_DATA_SZ, KM_NOSLEEP);
 224         if (tswtcl_data == NULL) {
 225                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 226                 return (ENOMEM);
 227         }
 228 
 229         if (cfg_parms->stats) {
 230                 if ((rc = tswtcl_statinit(aid, tswtcl_data)) != 0) {
 231                         kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 232                         kmem_free(tswtcl_data, TSWTCL_DATA_SZ);
 233                         return (rc);
 234                 }
 235         }
 236 
 237         /* set action chain reference */
 238         if ((rc = ipp_action_ref(aid, cfg_parms->red_action, flags)) != 0) {
 239                 tswtcl0dbg(("tswtcl_create_action: ipp_action_ref " \
 240                     "returned with error %d", rc));
 241                 goto cleanup;
 242         }
 243         if ((rc = ipp_action_ref(aid, cfg_parms->yellow_action, flags)) != 0) {
 244                 tswtcl0dbg(("tswtcl_create_action: ipp_action_ref " \
 245                     "returned with error %d", rc));
 246                 rc2 = ipp_action_unref(aid, cfg_parms->red_action, flags);
 247                 ASSERT(rc2 == 0);
 248                 goto cleanup;
 249         }
 250         if ((rc = ipp_action_ref(aid, cfg_parms->green_action, flags)) != 0) {
 251                 tswtcl0dbg(("tswtcl_create_action: ipp_action_ref " \
 252                     "returned with error %d", rc));
 253                 rc2 = ipp_action_unref(aid, cfg_parms->red_action, flags);
 254                 ASSERT(rc2 == 0);
 255                 rc2 = ipp_action_unref(aid, cfg_parms->yellow_action, flags);
 256                 ASSERT(rc2 == 0);
 257                 goto cleanup;
 258         }
 259 
 260         /* Initializations */
 261         cfg_parms->pminusc = cfg_parms->peak_rate - cfg_parms->committed_rate;
 262         tswtcl_data->cfg_parms = cfg_parms;
 263         tswtcl_data->avg_rate = cfg_parms->committed_rate;
 264         mutex_init(&tswtcl_data->tswtcl_lock, NULL, MUTEX_DEFAULT, 0);
 265         tswtcl_data->win_front = gethrtime();
 266         ipp_action_set_ptr(aid, (void *)tswtcl_data);
 267 
 268         return (0);
 269 
 270 cleanup:
 271         if (cfg_parms->stats) {
 272                 ipp_stat_destroy(tswtcl_data->stats);
 273         }
 274         kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 275         kmem_free(tswtcl_data, TSWTCL_DATA_SZ);
 276         return (rc);
 277 
 278 }
 279 
 280 static int
 281 tswtcl_modify_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
 282 {
 283 
 284         nvlist_t *nvlp;
 285         int err = 0, err2;
 286         uint8_t config_type;
 287         char *next_action_name;
 288         ipp_action_id_t next_action;
 289         uint32_t rate;
 290         tswtcl_cfg_t *cfg_parms, *old_cfg;
 291         tswtcl_data_t *tswtcl_data;
 292         uint32_t bstats;
 293 
 294         nvlp = *nvlpp;
 295         *nvlpp = NULL;          /* nvlist should be NULL when this returns */
 296 
 297         if ((err = nvlist_lookup_byte(nvlp, IPP_CONFIG_TYPE, &config_type))
 298             != 0) {
 299                 nvlist_free(nvlp);
 300                 tswtcl0dbg(("tswtcl_modify_action:invalid configuration type"));
 301                 return (err);
 302         }
 303 
 304         if (config_type != IPP_SET) {
 305                 nvlist_free(nvlp);
 306                 tswtcl0dbg(("tswtcl_modify_action:invalid configuration type " \
 307                     "%d", config_type));
 308                 return (EINVAL);
 309         }
 310 
 311         tswtcl_data = (tswtcl_data_t *)ipp_action_get_ptr(aid);
 312         old_cfg = tswtcl_data->cfg_parms;
 313 
 314         cfg_parms = kmem_alloc(TSWTCL_CFG_SZ, KM_NOSLEEP);
 315         if (cfg_parms == NULL) {
 316                 nvlist_free(nvlp);
 317                 tswtcl0dbg(("tswtcl_modify_action:mem. allocation failure\n"));
 318                 return (ENOMEM);
 319         }
 320 
 321         /* Just copy all and change as needed */
 322         bcopy(old_cfg, cfg_parms, TSWTCL_CFG_SZ);
 323 
 324         /* parse red action name, if present */
 325         if ((err = nvlist_lookup_string(nvlp, TSWTCL_RED_ACTION_NAME,
 326             &next_action_name)) == 0) {
 327                 /* Get action id */
 328                 if ((next_action = ipp_action_lookup(next_action_name))
 329                     == IPP_ACTION_INVAL) {
 330                         nvlist_free(nvlp);
 331                         tswtcl0dbg(("tswtcl_modify_action: red next_action"\
 332                             " invalid\n"));
 333                         kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 334                         return (EINVAL);
 335                 }
 336                 cfg_parms->red_action = next_action;
 337         }
 338 
 339         /* parse yellow action name, if present */
 340         if ((err = nvlist_lookup_string(nvlp, TSWTCL_YELLOW_ACTION_NAME,
 341             &next_action_name)) == 0) {
 342                 /* Get action id */
 343                 if ((next_action = ipp_action_lookup(next_action_name))
 344                     == IPP_ACTION_INVAL) {
 345                         nvlist_free(nvlp);
 346                         tswtcl0dbg(("tswtcl_modify_action: yellow next_action"\
 347                             "  invalid\n"));
 348                         kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 349                         return (EINVAL);
 350                 }
 351                 cfg_parms->yellow_action = next_action;
 352         }
 353 
 354         /* parse green action name, if present */
 355         if ((err = nvlist_lookup_string(nvlp, TSWTCL_GREEN_ACTION_NAME,
 356             &next_action_name)) == 0) {
 357                 /* Get action id */
 358                 if ((next_action = ipp_action_lookup(next_action_name))
 359                     == IPP_ACTION_INVAL) {
 360                         nvlist_free(nvlp);
 361                         tswtcl0dbg(("tswtcl_modify_action: green next_action"\
 362                             " invalid\n"));
 363                         kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 364                         return (EINVAL);
 365                 }
 366                 cfg_parms->green_action = next_action;
 367         }
 368 
 369         /* parse committed rate, if present */
 370         if ((err = nvlist_lookup_uint32(nvlp, TSWTCL_COMMITTED_RATE, &rate))
 371             == 0) {
 372                 cfg_parms->committed_rate = rate;
 373         }
 374 
 375         /* parse peak rate, if present */
 376         if ((err = nvlist_lookup_uint32(nvlp, TSWTCL_PEAK_RATE, &rate))
 377             == 0) {
 378                 cfg_parms->peak_rate = rate;
 379         }
 380 
 381         if (cfg_parms->peak_rate < cfg_parms->committed_rate) {
 382                 nvlist_free(nvlp);
 383                 tswtcl0dbg(("tswtcl_create_action: invalid config, "\
 384                     " peak rate < committed rate\n"));
 385                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 386                 return (EINVAL);
 387         }
 388 
 389         /* parse window - in msec */
 390         if ((err = nvlist_lookup_uint32(nvlp, TSWTCL_WINDOW,
 391             &cfg_parms->window)) != 0) {
 392                 cfg_parms->nsecwindow = (uint64_t)cfg_parms->window *
 393                     METER_MSEC_TO_NSEC;
 394         }
 395 
 396         /* parse stats, if present */
 397         if (nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats) == 0) {
 398                 cfg_parms->stats = (boolean_t)bstats;
 399                 if (cfg_parms->stats && !old_cfg->stats) {
 400                         if ((err = tswtcl_statinit(aid, tswtcl_data)) != 0) {
 401                                 nvlist_free(nvlp);
 402                                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 403                                 return (err);
 404                         }
 405                 } else if (!cfg_parms->stats && old_cfg->stats) {
 406                         ipp_stat_destroy(tswtcl_data->stats);
 407                 }
 408         }
 409 
 410         /* Can we ref all the new actions? */
 411         if ((err = ipp_action_ref(aid, cfg_parms->red_action, flags)) != 0) {
 412                 tswtcl0dbg(("tswtcl_modify_data: can't ref. red action\n"));
 413                 nvlist_free(nvlp);
 414                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 415                 return (err);
 416         }
 417 
 418         if ((err = ipp_action_ref(aid, cfg_parms->yellow_action, flags)) != 0) {
 419                 tswtcl0dbg(("tswtcl_modify_data:can't ref. yellow action\n"));
 420                 nvlist_free(nvlp);
 421                 err2 = ipp_action_unref(aid, cfg_parms->red_action, flags);
 422                 ASSERT(err2 == 0);
 423                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 424                 return (err);
 425         }
 426 
 427         if ((err = ipp_action_ref(aid, cfg_parms->green_action, flags)) != 0) {
 428                 tswtcl0dbg(("tswtcl_modify_data:can't ref. green action\n"));
 429                 nvlist_free(nvlp);
 430                 err2 = ipp_action_unref(aid, cfg_parms->red_action, flags);
 431                 ASSERT(err2 == 0);
 432                 err2 = ipp_action_unref(aid, cfg_parms->yellow_action, flags);
 433                 ASSERT(err2 == 0);
 434                 kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 435                 return (err);
 436         }
 437 
 438         /* Re-compute pminusc */
 439         cfg_parms->pminusc = cfg_parms->peak_rate - cfg_parms->committed_rate;
 440 
 441         /* Actually modify the configuration */
 442         mutex_enter(&tswtcl_data->tswtcl_lock);
 443         tswtcl_data->cfg_parms = cfg_parms;
 444         mutex_exit(&tswtcl_data->tswtcl_lock);
 445 
 446         /* Un-ref the old actions */
 447         err = ipp_action_unref(aid, old_cfg->red_action, flags);
 448         ASSERT(err == 0);
 449         err = ipp_action_unref(aid, old_cfg->yellow_action, flags);
 450         ASSERT(err == 0);
 451         err = ipp_action_unref(aid, old_cfg->green_action, flags);
 452         ASSERT(err == 0);
 453 
 454         /* Free the old configuration */
 455         kmem_free(old_cfg, TSWTCL_CFG_SZ);
 456 
 457         nvlist_free(nvlp);
 458 
 459         return (0);
 460 }
 461 
 462 static int
 463 tswtcl_destroy_action(ipp_action_id_t aid, ipp_flags_t flags)
 464 {
 465         tswtcl_data_t *tswtcl_data;
 466         tswtcl_cfg_t *cfg_parms;
 467         int rc;
 468 
 469         tswtcl_data = (tswtcl_data_t *)ipp_action_get_ptr(aid);
 470         ASSERT(tswtcl_data != NULL);
 471 
 472         cfg_parms = tswtcl_data->cfg_parms;
 473 
 474         if (cfg_parms->stats) {
 475                 ipp_stat_destroy(tswtcl_data->stats);
 476         }
 477 
 478         /* unreference the action */
 479         rc = ipp_action_unref(aid, cfg_parms->red_action, flags);
 480         ASSERT(rc == 0);
 481         rc = ipp_action_unref(aid, cfg_parms->yellow_action, flags);
 482         ASSERT(rc == 0);
 483         rc = ipp_action_unref(aid, cfg_parms->green_action, flags);
 484         ASSERT(rc == 0);
 485 
 486         mutex_destroy(&tswtcl_data->tswtcl_lock);
 487         kmem_free(cfg_parms, TSWTCL_CFG_SZ);
 488         kmem_free(tswtcl_data, TSWTCL_DATA_SZ);
 489         return (0);
 490 }
 491 
 492 static int
 493 tswtcl_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet)
 494 {
 495         tswtcl_data_t *tswtcl_data;
 496         ipp_action_id_t next_action;
 497         mblk_t *mp = NULL;
 498         int rc;
 499 
 500         /* get mblk from ipp_packet structure */
 501         mp = ipp_packet_get_data(packet);
 502         tswtcl_data = (tswtcl_data_t *)ipp_action_get_ptr(aid);
 503         ASSERT(tswtcl_data != NULL);
 504 
 505         /* tswtcl packet as configured */
 506         if ((rc = tswtcl_process(&mp, tswtcl_data, &next_action)) != 0) {
 507                 return (rc);
 508         } else {
 509                 return (ipp_packet_next(packet, next_action));
 510         }
 511 }
 512 
 513 static int
 514 tswtcl_statinit(ipp_action_id_t aid, tswtcl_data_t *tswtcl_data)
 515 {
 516         int rc = 0;
 517         meter_stat_t *statsp;
 518 
 519         /* install stats entry */
 520         if ((rc = ipp_stat_create(aid, TSWTCL_STATS_STRING, METER_STATS_COUNT,
 521             tswtcl_update_stats, tswtcl_data, &tswtcl_data->stats)) != 0) {
 522                 tswtcl0dbg(("tswtcl_statinit:ipp_stat_create failed "\
 523                     " with %d\n", rc));
 524                 return (rc);
 525         }
 526 
 527         statsp = (meter_stat_t *)(tswtcl_data->stats)->ipps_data;
 528         ASSERT(statsp != NULL);
 529 
 530         if ((rc = ipp_stat_named_init(tswtcl_data->stats, "red_packets",
 531             IPP_STAT_UINT64, &statsp->red_packets)) != 0) {
 532                 tswtcl0dbg(("tswtcl_statinit:ipp_stat_create failed "\
 533                     " with %d\n", rc));
 534                 return (rc);
 535         }
 536         if ((rc = ipp_stat_named_init(tswtcl_data->stats, "red_bits",
 537             IPP_STAT_UINT64, &statsp->red_bits)) != 0) {
 538                 tswtcl0dbg(("tswtcl_statinit:ipp_stat_create failed "\
 539                     " with %d\n", rc));
 540                 return (rc);
 541         }
 542         if ((rc = ipp_stat_named_init(tswtcl_data->stats, "yellow_packets",
 543             IPP_STAT_UINT64, &statsp->yellow_packets)) != 0) {
 544                 tswtcl0dbg(("tswtcl_statinit:ipp_stat_named_init failed "\
 545                     " with %d\n", rc));
 546                 return (rc);
 547         }
 548         if ((rc = ipp_stat_named_init(tswtcl_data->stats, "yellow_bits",
 549             IPP_STAT_UINT64, &statsp->yellow_bits)) != 0) {
 550                 tswtcl0dbg(("tswtcl_statinit:ipp_stat_create failed "\
 551                     " with %d\n", rc));
 552                 return (rc);
 553         }
 554         if ((rc = ipp_stat_named_init(tswtcl_data->stats, "green_packets",
 555             IPP_STAT_UINT64, &statsp->green_packets)) != 0) {
 556                 tswtcl0dbg(("tswtcl_statinit:ipp_stat_named_init failed "\
 557                     " with %d\n", rc));
 558                 return (rc);
 559         }
 560         if ((rc = ipp_stat_named_init(tswtcl_data->stats, "green_bits",
 561             IPP_STAT_UINT64, &statsp->green_bits)) != 0) {
 562                 tswtcl0dbg(("tswtcl_statinit:ipp_stat_create failed "\
 563                     " with %d\n", rc));
 564                 return (rc);
 565         }
 566         if ((rc = ipp_stat_named_init(tswtcl_data->stats, "epackets",
 567             IPP_STAT_UINT64, &statsp->epackets)) != 0) {
 568                 tswtcl0dbg(("tswtcl_statinit:ipp_stat_named_init failed "\
 569                     " with %d\n", rc));
 570                 return (rc);
 571         }
 572         ipp_stat_install(tswtcl_data->stats);
 573 
 574         return (rc);
 575 
 576 }
 577 
 578 static int
 579 tswtcl_update_stats(ipp_stat_t *sp, void *args, int rw)
 580 {
 581         tswtcl_data_t *tswtcl_data = (tswtcl_data_t *)args;
 582         meter_stat_t *stats = (meter_stat_t *)sp->ipps_data;
 583 
 584         ASSERT((tswtcl_data != NULL) && (stats != NULL));
 585 
 586         (void) ipp_stat_named_op(&stats->red_packets, &tswtcl_data->red_packets,
 587             rw);
 588         (void) ipp_stat_named_op(&stats->yellow_packets,
 589             &tswtcl_data->yellow_packets, rw);
 590         (void) ipp_stat_named_op(&stats->green_packets,
 591             &tswtcl_data->green_packets, rw);
 592 
 593         (void) ipp_stat_named_op(&stats->red_bits, &tswtcl_data->red_bits, rw);
 594         (void) ipp_stat_named_op(&stats->yellow_bits,
 595             &tswtcl_data->yellow_bits, rw);
 596         (void) ipp_stat_named_op(&stats->green_bits,
 597             &tswtcl_data->green_bits, rw);
 598 
 599         (void) ipp_stat_named_op(&stats->epackets, &tswtcl_data->epackets,
 600             rw);
 601 
 602         return (0);
 603 }
 604 
 605 /* ARGSUSED */
 606 static int
 607 tswtcl_info(ipp_action_id_t aid, int (*fn)(nvlist_t *, void *), void *arg,
 608     ipp_flags_t flags)
 609 {
 610         nvlist_t *nvlp;
 611         tswtcl_data_t *tswtcl_data;
 612         tswtcl_cfg_t *cfg_parms;
 613         char *next_action;
 614         int rc;
 615 
 616         tswtcl_data = (tswtcl_data_t *)ipp_action_get_ptr(aid);
 617         ASSERT(tswtcl_data != NULL);
 618 
 619         cfg_parms = tswtcl_data->cfg_parms;
 620 
 621         /* allocate nvlist to be passed back */
 622         if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) {
 623                 tswtcl0dbg(("tswtcl_info: memory allocation failure\n"));
 624                 return (rc);
 625         }
 626 
 627         /* look up red next action with the next action id */
 628         if ((rc = ipp_action_name(cfg_parms->red_action, &next_action)) != 0) {
 629                 tswtcl0dbg(("tswtcl_info: red action not available\n"));
 630                 nvlist_free(nvlp);
 631                 return (rc);
 632         }
 633 
 634         /* add next action name */
 635         if ((rc = nvlist_add_string(nvlp, TSWTCL_RED_ACTION_NAME,
 636             next_action)) != 0) {
 637                 tswtcl0dbg(("tswtcl_info: error adding\n"));
 638                 nvlist_free(nvlp);
 639                 kmem_free(next_action, (strlen(next_action) + 1));
 640                 return (rc);
 641         }
 642 
 643         /* free action name */
 644         kmem_free(next_action, (strlen(next_action) + 1));
 645 
 646         /* look up yellow next action with the next action id */
 647         if ((rc = ipp_action_name(cfg_parms->yellow_action,
 648             &next_action)) != 0) {
 649                 tswtcl0dbg(("tswtcl_info: yellow action not available\n"));
 650                 nvlist_free(nvlp);
 651                 return (rc);
 652         }
 653 
 654         /* add next action name */
 655         if ((rc = nvlist_add_string(nvlp, TSWTCL_YELLOW_ACTION_NAME,
 656             next_action)) != 0) {
 657                 tswtcl0dbg(("tswtcl_info: error adding yellow action\n"));
 658                 nvlist_free(nvlp);
 659                 kmem_free(next_action, (strlen(next_action) + 1));
 660                 return (rc);
 661         }
 662         /* free action name */
 663         kmem_free(next_action, (strlen(next_action) + 1));
 664 
 665         /* look up green next action with the next action id */
 666         if ((rc = ipp_action_name(cfg_parms->green_action,
 667             &next_action)) != 0) {
 668                 tswtcl0dbg(("tswtcl_info: green action not available\n"));
 669                 nvlist_free(nvlp);
 670                 return (rc);
 671         }
 672 
 673         /* add next action name */
 674         if ((rc = nvlist_add_string(nvlp, TSWTCL_GREEN_ACTION_NAME,
 675             next_action)) != 0) {
 676                 tswtcl0dbg(("tswtcl_info: error adding green action\n"));
 677                 nvlist_free(nvlp);
 678                 kmem_free(next_action, (strlen(next_action) + 1));
 679                 return (rc);
 680         }
 681 
 682         /* free action name */
 683         kmem_free(next_action, (strlen(next_action) + 1));
 684 
 685         /* add config type */
 686         if ((rc = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) {
 687                 tswtcl0dbg(("tswtcl_info: error adding config_type\n"));
 688                 nvlist_free(nvlp);
 689                 return (rc);
 690         }
 691 
 692         /* add committed_rate  */
 693         if ((rc = nvlist_add_uint32(nvlp, TSWTCL_COMMITTED_RATE,
 694             cfg_parms->committed_rate)) != 0) {
 695                 tswtcl0dbg(("tswtcl_info: error adding committed_rate\n"));
 696                 nvlist_free(nvlp);
 697                 return (rc);
 698         }
 699 
 700         /* add peak_rate  */
 701         if ((rc = nvlist_add_uint32(nvlp, TSWTCL_PEAK_RATE,
 702             cfg_parms->peak_rate)) != 0) {
 703                 tswtcl0dbg(("tswtcl_info: error adding peak_rate\n"));
 704                 nvlist_free(nvlp);
 705                 return (rc);
 706         }
 707 
 708         /* add window  */
 709         if ((rc = nvlist_add_uint32(nvlp, TSWTCL_WINDOW,
 710             cfg_parms->window)) != 0) {
 711                 tswtcl0dbg(("tswtcl_info: error adding window\n"));
 712                 nvlist_free(nvlp);
 713                 return (rc);
 714         }
 715 
 716         if ((rc = nvlist_add_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
 717             (uint32_t)(uintptr_t)tswtcl_data->stats)) != 0) {
 718                 tswtcl0dbg(("tswtcl_info: error adding stats status\n"));
 719                 nvlist_free(nvlp);
 720                 return (rc);
 721         }
 722 
 723         /* call back with nvlist */
 724         rc = fn(nvlp, arg);
 725 
 726         nvlist_free(nvlp);
 727         return (rc);
 728 }