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