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 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/types.h>
  27 #include <sys/atomic.h>
  28 #include <sys/systm.h>
  29 #include <sys/socket.h>
  30 #include <netinet/in.h>
  31 #include <sys/modctl.h>
  32 #include <sys/sunddi.h>
  33 #include <ipp/ipp.h>
  34 #include <ipp/ipp_config.h>
  35 #include <inet/common.h>
  36 #include <ipp/meters/meter_impl.h>
  37 
  38 #define D_SM_COMMENT    "IPP Single-Two Rate Token Meter"
  39 
  40 /* DDI file for tokenmt ipp module */
  41 
  42 /* Default DSCP to colour mapping for colour-aware meter */
  43 enum meter_colour default_dscp_to_colour[64] = {
  44         TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
  45         TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
  46         TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
  47         TOKENMT_YELLOW, TOKENMT_GREEN, TOKENMT_RED, TOKENMT_GREEN,
  48         TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
  49         TOKENMT_YELLOW, TOKENMT_GREEN, TOKENMT_RED, TOKENMT_GREEN,
  50         TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
  51         TOKENMT_YELLOW, TOKENMT_GREEN, TOKENMT_RED, TOKENMT_GREEN,
  52         TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
  53         TOKENMT_YELLOW, TOKENMT_GREEN, TOKENMT_RED, TOKENMT_GREEN,
  54         TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
  55         TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
  56         TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
  57         TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
  58         TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN,
  59         TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN, TOKENMT_GREEN
  60 };
  61 
  62 static int tokenmt_create_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
  63 static int tokenmt_modify_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
  64 static int tokenmt_destroy_action(ipp_action_id_t, ipp_flags_t);
  65 static int tokenmt_info(ipp_action_id_t, int (*)(nvlist_t *, void *), void *,
  66     ipp_flags_t);
  67 static int tokenmt_invoke_action(ipp_action_id_t, ipp_packet_t *);
  68 
  69 /* Initialize stats */
  70 static int tokenmt_statinit(ipp_action_id_t, tokenmt_data_t *);
  71 
  72 /* Stats callback function */
  73 static int tokenmt_update_stats(ipp_stat_t *, void *, int);
  74 
  75 ipp_ops_t tokenmt_ops = {
  76         IPPO_REV,
  77         tokenmt_create_action,  /* ippo_action_create */
  78         tokenmt_modify_action,  /* ippo_action_modify */
  79         tokenmt_destroy_action, /* ippo_action_destroy */
  80         tokenmt_info,           /* ippo_action_info */
  81         tokenmt_invoke_action   /* ippo_action_invoke */
  82 };
  83 
  84 extern struct mod_ops mod_ippops;
  85 
  86 /*
  87  * Module linkage information for the kernel.
  88  */
  89 static struct modlipp modlipp = {
  90         &mod_ippops,
  91         D_SM_COMMENT,
  92         &tokenmt_ops
  93 };
  94 
  95 static struct modlinkage modlinkage = {
  96         MODREV_1,
  97         { (void *)&modlipp, NULL }
  98 };
  99 
 100 
 101 int
 102 _init(void)
 103 {
 104         return (mod_install(&modlinkage));
 105 }
 106 
 107 int
 108 _fini(void)
 109 {
 110         return (mod_remove(&modlinkage));
 111 }
 112 
 113 int
 114 _info(struct modinfo *modinfop)
 115 {
 116         return (mod_info(&modlinkage, modinfop));
 117 }
 118 
 119 /* ARGSUSED */
 120 static int
 121 tokenmt_create_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
 122 {
 123         nvlist_t *nvlp;
 124         tokenmt_data_t *tokenmt_data;
 125         char *next_action;
 126         tokenmt_cfg_t *cfg_parms;
 127         uint32_t mode;
 128         uint32_t bstats;
 129         int rc, rc2;
 130         int32_t *colour_tbl;
 131         uint_t nelem = 64;
 132 
 133         nvlp = *nvlpp;
 134         *nvlpp = NULL;          /* nvlist should be NULL on return */
 135 
 136         if ((cfg_parms = kmem_zalloc(TOKENMT_CFG_SZ, KM_NOSLEEP)) == NULL) {
 137                 nvlist_free(nvlp);
 138                 return (ENOMEM);
 139         }
 140 
 141         /* parse red next action name */
 142         if ((rc = nvlist_lookup_string(nvlp, TOKENMT_RED_ACTION_NAME,
 143             &next_action)) != 0) {
 144                 nvlist_free(nvlp);
 145                 tokenmt0dbg(("tokenmt_create_action:invalid config, red "\
 146                     "action name missing\n"));
 147                 kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 148                 return (rc);
 149         }
 150         if ((cfg_parms->red_action = ipp_action_lookup(next_action))
 151             == IPP_ACTION_INVAL) {
 152                 nvlist_free(nvlp);
 153                 tokenmt0dbg(("tokenmt_create_action: red action invalid\n"));
 154                 kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 155                 return (EINVAL);
 156         }
 157 
 158         /* parse yellow next action name, if present  this is Two Rate meter */
 159         if ((rc = nvlist_lookup_string(nvlp, TOKENMT_YELLOW_ACTION_NAME,
 160             &next_action)) == 0) {
 161                 if ((cfg_parms->yellow_action = ipp_action_lookup(next_action))
 162                     == IPP_ACTION_INVAL) {
 163                         nvlist_free(nvlp);
 164                         tokenmt0dbg(("tokenmt_create_action: yellow action "\
 165                             "invalid\n"));
 166                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 167                         return (EINVAL);
 168                 }
 169         } else {
 170                 cfg_parms->yellow_action = TOKENMT_NO_ACTION;
 171         }
 172 
 173         /* parse green next action name */
 174         if ((rc = nvlist_lookup_string(nvlp, TOKENMT_GREEN_ACTION_NAME,
 175             &next_action)) != 0) {
 176                 nvlist_free(nvlp);
 177                 tokenmt0dbg(("tokenmt_create_action:invalid config, green " \
 178                     "action name missing\n"));
 179                 kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 180                 return (rc);
 181         }
 182         if ((cfg_parms->green_action = ipp_action_lookup(next_action))
 183             == IPP_ACTION_INVAL) {
 184                 nvlist_free(nvlp);
 185                 tokenmt0dbg(("tokenmt_create_action: green action invalid\n"));
 186                 kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 187                 return (EINVAL);
 188         }
 189 
 190         /* parse committed rate  - in kilo bits / sec */
 191         if ((rc = nvlist_lookup_uint32(nvlp, TOKENMT_COMMITTED_RATE,
 192             &cfg_parms->committed_rate)) != 0) {
 193                 nvlist_free(nvlp);
 194                 tokenmt0dbg(("tokenmt_create_action: invalid config, "\
 195                     " committed rate missing\n"));
 196                 kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 197                 return (rc);
 198         }
 199         if (cfg_parms->committed_rate == 0) {
 200                 nvlist_free(nvlp);
 201                 tokenmt0dbg(("tokenmt_create_action: invalid committed rate, "\
 202                     "%u\n", cfg_parms->committed_rate));
 203                 kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 204                 return (EINVAL);
 205         }
 206 
 207         /* parse committed burst in bits */
 208         if ((rc = nvlist_lookup_uint32(nvlp, TOKENMT_COMMITTED_BURST,
 209             &cfg_parms->committed_burst)) != 0) {
 210                 nvlist_free(nvlp);
 211                 tokenmt0dbg(("tokenmt_create_action: invalid config, "\
 212                     " committed burst missing\n"));
 213                 kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 214                 return (rc);
 215         }
 216 
 217 
 218         /*
 219          * If the peak burst size is specified, make sure we have the
 220          * yellow action.
 221          */
 222         if ((rc = nvlist_lookup_uint32(nvlp, TOKENMT_PEAK_BURST,
 223             &cfg_parms->peak_burst)) == 0) {
 224                 if (cfg_parms->yellow_action == TOKENMT_NO_ACTION) {
 225                         nvlist_free(nvlp);
 226                         tokenmt0dbg(("tokenmt_create_action: peak burst "\
 227                             "specified without yellow action\n"));
 228                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 229                         return (EINVAL);
 230                 }
 231         } else if (cfg_parms->yellow_action != TOKENMT_NO_ACTION) {
 232                 nvlist_free(nvlp);
 233                 tokenmt0dbg(("tokenmt_create_action: peak burst must be "\
 234                     "provided with yellow action\n"));
 235                 kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 236                 return (EINVAL);
 237         }
 238 
 239         /* Check if we have a peak_rate */
 240         if ((rc = nvlist_lookup_uint32(nvlp, TOKENMT_PEAK_RATE,
 241             &cfg_parms->peak_rate)) == 0) {
 242                 if (cfg_parms->yellow_action == TOKENMT_NO_ACTION) {
 243                         nvlist_free(nvlp);
 244                         tokenmt0dbg(("tokenmt_create_action: peak rate "\
 245                             "specified without yellow action\n"));
 246                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 247                         return (EINVAL);
 248                 } else if ((cfg_parms->peak_rate == 0) ||
 249                     (cfg_parms->peak_rate < cfg_parms->committed_rate)) {
 250                         nvlist_free(nvlp);
 251                         tokenmt0dbg(("tokenmt_create_action: invalid "\
 252                             "peak rate, %u\n", cfg_parms->peak_rate));
 253                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 254                         return (EINVAL);
 255                 }
 256                 cfg_parms->tokenmt_type = TRTCL_TOKENMT;
 257         } else {
 258                 cfg_parms->tokenmt_type = SRTCL_TOKENMT;
 259         }
 260 
 261         /* Validate the committed and peak burst size */
 262         if (cfg_parms->tokenmt_type == SRTCL_TOKENMT) {
 263                 if ((cfg_parms->committed_burst == 0) &&
 264                     (cfg_parms->peak_burst == 0)) {
 265                         nvlist_free(nvlp);
 266                         tokenmt0dbg(("tokenmt_create_action: at least one "\
 267                             "burst size must be non-zero\n"));
 268                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 269                         return (EINVAL);
 270                 }
 271         } else {        /* TRTCL_TOKENMT */
 272                 if ((cfg_parms->committed_burst == 0) ||
 273                     (cfg_parms->peak_burst == 0)) {
 274                         nvlist_free(nvlp);
 275                         tokenmt0dbg(("tokenmt_create_action: both the "\
 276                             "burst sizes must be non-zero\n"));
 277                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 278                         return (EINVAL);
 279                 }
 280         }
 281 
 282         /* just copy default colour mapping */
 283         bcopy(default_dscp_to_colour, cfg_parms->dscp_to_colour,
 284             sizeof (default_dscp_to_colour));
 285 
 286         /* parse mode, if present */
 287         if ((rc = nvlist_lookup_uint32(nvlp, TOKENMT_COLOUR_AWARE,
 288             &mode)) != 0) {
 289                 cfg_parms->colour_aware = B_FALSE;
 290         } else {
 291                 cfg_parms->colour_aware = (mode == 0) ? B_FALSE : B_TRUE;
 292         }
 293 
 294         /* Get the dscp to colour mapping array */
 295         if (cfg_parms->colour_aware) {
 296                 if ((rc = nvlist_lookup_int32_array(nvlp,
 297                     TOKENMT_COLOUR_MAP, &colour_tbl, &nelem)) == 0) {
 298                         int count;
 299                         for (count = 0; count < 64; count++) {
 300                                 if (colour_tbl[count] == -1)
 301                                         continue;
 302                                 cfg_parms->dscp_to_colour[count] =
 303                                     colour_tbl[count];
 304                         }
 305                 }
 306         }
 307 
 308         /* parse stats */
 309         if ((rc = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
 310             != 0) {
 311                 cfg_parms->stats = B_FALSE;
 312         } else {
 313                 cfg_parms->stats = (bstats == 0) ? B_FALSE : B_TRUE;
 314         }
 315 
 316         nvlist_free(nvlp);
 317 
 318         /* Initialize other stuff */
 319         tokenmt_data = kmem_zalloc(TOKENMT_DATA_SZ, KM_NOSLEEP);
 320         if (tokenmt_data == NULL) {
 321                 kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 322                 return (ENOMEM);
 323         }
 324 
 325         /* Initialize stats, if required */
 326         if (cfg_parms->stats) {
 327                 if ((rc = tokenmt_statinit(aid, tokenmt_data)) != 0) {
 328                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 329                         kmem_free(tokenmt_data, TOKENMT_DATA_SZ);
 330                         return (rc);
 331                 }
 332         }
 333 
 334         /* set action chain reference */
 335         if ((rc = ipp_action_ref(aid, cfg_parms->red_action, flags)) != 0) {
 336                 tokenmt0dbg(("tokenmt_create_action: ipp_action_ref " \
 337                     "returned with error %d", rc));
 338                 goto cleanup;
 339         }
 340         if ((rc = ipp_action_ref(aid, cfg_parms->green_action, flags)) != 0) {
 341                 tokenmt0dbg(("tokenmt_create_action: ipp_action_ref " \
 342                     "returned with error %d", rc));
 343                 rc2 = ipp_action_unref(aid, cfg_parms->red_action, flags);
 344                 ASSERT(rc2 == 0);
 345                 goto cleanup;
 346         }
 347 
 348         if (cfg_parms->yellow_action != TOKENMT_NO_ACTION) {
 349                 if ((rc = ipp_action_ref(aid, cfg_parms->yellow_action,
 350                     flags)) != 0) {
 351                         tokenmt0dbg(("tokenmt_create_action: ipp_action_ref "\
 352                             "returned with error %d", rc));
 353                         rc2 = ipp_action_unref(aid, cfg_parms->red_action,
 354                             flags);
 355                         ASSERT(rc2 == 0);
 356                         rc2 = ipp_action_unref(aid, cfg_parms->green_action,
 357                             flags);
 358                         ASSERT(rc2 == 0);
 359                         goto cleanup;
 360                 }
 361         }
 362 
 363 
 364         tokenmt_data->cfg_parms = cfg_parms;
 365 
 366         tokenmt_data->committed_tokens = cfg_parms->committed_burst;
 367         tokenmt_data->peak_tokens = cfg_parms->peak_burst;
 368         tokenmt_data->last_seen = gethrtime();
 369 
 370         mutex_init(&tokenmt_data->tokenmt_lock, NULL, MUTEX_DEFAULT, 0);
 371         ipp_action_set_ptr(aid, (void *)tokenmt_data);
 372         return (0);
 373 
 374 cleanup:
 375         if (cfg_parms->stats) {
 376                 ipp_stat_destroy(tokenmt_data->stats);
 377         }
 378         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 379         kmem_free(tokenmt_data, TOKENMT_DATA_SZ);
 380         return (rc);
 381 }
 382 
 383 static int
 384 tokenmt_modify_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
 385 {
 386         nvlist_t *nvlp;
 387         int err = 0, err2;
 388         uint8_t config_type;
 389         char *next_action_name;
 390         ipp_action_id_t next_action;
 391         uint32_t rate, cbs, pbs;
 392         tokenmt_cfg_t *cfg_parms, *old_cfg;
 393         tokenmt_data_t *tokenmt_data;
 394         uint32_t bstats, mode;
 395         int32_t *colour_tbl;
 396         uint_t nelem = 64;
 397 
 398         nvlp = *nvlpp;
 399         *nvlpp = NULL;          /* nvlist should be NULL when this returns */
 400 
 401         if ((err = nvlist_lookup_byte(nvlp, IPP_CONFIG_TYPE, &config_type))
 402             != 0) {
 403                 nvlist_free(nvlp);
 404                 tokenmt0dbg(("tokenmt_modify_action: invalid configuration "\
 405                     "type"));
 406                 return (err);
 407         }
 408 
 409         if (config_type != IPP_SET) {
 410                 nvlist_free(nvlp);
 411                 tokenmt0dbg(("tokenmt_modify_action: invalid configuration "\
 412                     "type %d", config_type));
 413                 return (EINVAL);
 414         }
 415 
 416         tokenmt_data = (tokenmt_data_t *)ipp_action_get_ptr(aid);
 417         old_cfg = tokenmt_data->cfg_parms;
 418 
 419         cfg_parms = kmem_zalloc(TOKENMT_CFG_SZ, KM_NOSLEEP);
 420         if (cfg_parms == NULL) {
 421                 nvlist_free(nvlp);
 422                 tokenmt0dbg(("tokenmt_modify_action: memory allocation "\
 423                     "failure\n"));
 424                 return (ENOMEM);
 425         }
 426 
 427         /* Just copy all and change as needed */
 428         bcopy(old_cfg, cfg_parms, TOKENMT_CFG_SZ);
 429 
 430         /* parse red action name, if present */
 431         if ((err = nvlist_lookup_string(nvlp, TOKENMT_RED_ACTION_NAME,
 432             &next_action_name)) == 0) {
 433                 /* Get action id */
 434                 if ((next_action = ipp_action_lookup(next_action_name))
 435                     == IPP_ACTION_INVAL) {
 436                         nvlist_free(nvlp);
 437                         tokenmt0dbg(("tokenmt_modify_action: next_action "\
 438                             "invalid"));
 439                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 440                         return (EINVAL);
 441                 }
 442                 cfg_parms->red_action = next_action;
 443         }
 444 
 445         /* parse yellow action name, if present */
 446         if ((err = nvlist_lookup_string(nvlp, TOKENMT_YELLOW_ACTION_NAME,
 447             &next_action_name)) == 0) {
 448                 /* Get action id */
 449                 if ((next_action = ipp_action_lookup(next_action_name))
 450                     == IPP_ACTION_INVAL) {
 451                         nvlist_free(nvlp);
 452                         tokenmt0dbg(("tokenmt_modify_action: next_action "\
 453                             "invalid"));
 454                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 455                         return (EINVAL);
 456                 }
 457                 cfg_parms->yellow_action = next_action;
 458         } else {
 459                 cfg_parms->yellow_action = TOKENMT_NO_ACTION;
 460         }
 461 
 462         /* parse green action name, if present */
 463         if ((err = nvlist_lookup_string(nvlp, TOKENMT_GREEN_ACTION_NAME,
 464             &next_action_name)) == 0) {
 465                 /* Get action id */
 466                 if ((next_action = ipp_action_lookup(next_action_name))
 467                     == IPP_ACTION_INVAL) {
 468                         nvlist_free(nvlp);
 469                         tokenmt0dbg(("tokenmt_modify_action: next_action "\
 470                             "invalid"));
 471                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 472                         return (EINVAL);
 473                 }
 474                 cfg_parms->green_action = next_action;
 475         }
 476 
 477         /* parse committed rate, if present */
 478         if ((err = nvlist_lookup_uint32(nvlp, TOKENMT_COMMITTED_RATE, &rate))
 479             == 0) {
 480                 if (rate == 0) {
 481                         nvlist_free(nvlp);
 482                         tokenmt0dbg(("tokenmt_modify_action: invalid "\
 483                             "committed rate %u\n", cfg_parms->committed_rate));
 484                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 485                         return (EINVAL);
 486                 }
 487                 cfg_parms->committed_rate = rate;
 488         }
 489 
 490         /* parse committed burst, if present */
 491         if (nvlist_lookup_uint32(nvlp, TOKENMT_COMMITTED_BURST, &cbs) == 0) {
 492                 cfg_parms->committed_burst = cbs;
 493         }
 494 
 495 
 496         if (nvlist_lookup_uint32(nvlp, TOKENMT_PEAK_BURST, &pbs) == 0) {
 497                 cfg_parms->peak_burst = pbs;
 498         } else {
 499                 cfg_parms->peak_burst = 0;
 500         }
 501 
 502         /* If the peak rate is not specified, then it means single rate meter */
 503         if (nvlist_lookup_uint32(nvlp, TOKENMT_PEAK_RATE, &rate) == 0) {
 504                 cfg_parms->peak_rate = rate;
 505                 if ((rate == 0) || (rate < cfg_parms->committed_rate)) {
 506                         nvlist_free(nvlp);
 507                         tokenmt0dbg(("tokenmt_modify_action: invalid "\
 508                             "committed rate %u\n", cfg_parms->committed_rate));
 509                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 510                         return (EINVAL);
 511                 }
 512                 cfg_parms->tokenmt_type = TRTCL_TOKENMT;
 513         } else {
 514                 cfg_parms->peak_rate = 0;
 515                 cfg_parms->tokenmt_type = SRTCL_TOKENMT;
 516         }
 517 
 518         if (cfg_parms->yellow_action == TOKENMT_NO_ACTION) {
 519                 if ((cfg_parms->peak_burst != 0) ||
 520                     (cfg_parms->tokenmt_type == TRTCL_TOKENMT)) {
 521                         nvlist_free(nvlp);
 522                         tokenmt0dbg(("tokenmt_modify_action: yellow action "\
 523                             "missing\n"));
 524                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 525                         return (EINVAL);
 526                 }
 527         } else {
 528                 if ((cfg_parms->tokenmt_type != TRTCL_TOKENMT) &&
 529                     (cfg_parms->peak_burst == 0)) {
 530                         nvlist_free(nvlp);
 531                         tokenmt0dbg(("tokenmt_modify_action: peak "\
 532                             "burst/rate missing\n"));
 533                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 534                         return (EINVAL);
 535                 }
 536         }
 537 
 538         /* Validate the committed and peak burst size */
 539         if (cfg_parms->tokenmt_type == SRTCL_TOKENMT) {
 540                 if ((cfg_parms->committed_burst == 0) &&
 541                     (cfg_parms->peak_burst == 0)) {
 542                         nvlist_free(nvlp);
 543                         tokenmt0dbg(("tokenmt_modify_action: at least one "\
 544                             "burst size must be non-zero\n"));
 545                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 546                         return (EINVAL);
 547                 }
 548         } else {        /* TRTCL_TOKENMT */
 549                 if ((cfg_parms->committed_burst == 0) ||
 550                     (cfg_parms->peak_burst == 0)) {
 551                         nvlist_free(nvlp);
 552                         tokenmt0dbg(("tokenmt_modify_action: both the "\
 553                             "burst sizes must be non-zero\n"));
 554                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 555                         return (EINVAL);
 556                 }
 557         }
 558 
 559         /* parse mode */
 560         if (nvlist_lookup_uint32(nvlp, TOKENMT_COLOUR_AWARE, &mode) == 0) {
 561                 cfg_parms->colour_aware = (mode == 0) ? B_FALSE : B_TRUE;
 562         } else {
 563                 cfg_parms->colour_aware = B_FALSE;
 564         }
 565 
 566         if (cfg_parms->colour_aware) {
 567                 if (nvlist_lookup_int32_array(nvlp, TOKENMT_COLOUR_MAP,
 568                     &colour_tbl, &nelem) == 0) {
 569                         int count;
 570                         for (count = 0; count < 64; count++) {
 571                                 if (colour_tbl[count] == -1)
 572                                         continue;
 573                                 cfg_parms->dscp_to_colour[count] =
 574                                     colour_tbl[count];
 575                         }
 576                 } else {
 577                         bcopy(default_dscp_to_colour, cfg_parms->dscp_to_colour,
 578                             sizeof (default_dscp_to_colour));
 579                 }
 580         }
 581 
 582         /* parse stats, if present */
 583         if (nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats) == 0) {
 584                 cfg_parms->stats = (bstats == 0) ? B_FALSE : B_TRUE;
 585                 if (cfg_parms->stats && !old_cfg->stats) {
 586                         if ((err = tokenmt_statinit(aid, tokenmt_data)) != 0) {
 587                                 nvlist_free(nvlp);
 588                                 kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 589                                 return (err);
 590                         }
 591                 } else if (!cfg_parms->stats && old_cfg->stats) {
 592                         ipp_stat_destroy(tokenmt_data->stats);
 593                 }
 594         }
 595 
 596         /* Can we ref all the new actions? */
 597         if ((err = ipp_action_ref(aid, cfg_parms->red_action, flags)) != 0) {
 598                 tokenmt0dbg(("tokenmt_modify_data: can't ref. red action\n"));
 599                 kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 600                 return (err);
 601         }
 602         if ((err = ipp_action_ref(aid, cfg_parms->green_action, flags)) != 0) {
 603                 tokenmt0dbg(("tokenmt_modify_data:can't ref. green action\n"));
 604                 err2 = ipp_action_unref(aid, cfg_parms->red_action, flags);
 605                 ASSERT(err2 == 0);
 606                 kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 607                 return (err);
 608         }
 609 
 610         if (cfg_parms->yellow_action != TOKENMT_NO_ACTION) {
 611                 if ((err = ipp_action_ref(aid, cfg_parms->yellow_action,
 612                     flags)) != 0) {
 613                         tokenmt0dbg(("tokenmt_modify_data:can't ref. yellow "\
 614                             "action\n"));
 615                         err2 = ipp_action_unref(aid, cfg_parms->red_action,
 616                             flags);
 617                         ASSERT(err2 == 0);
 618                         err2 = ipp_action_unref(aid, cfg_parms->green_action,
 619                             flags);
 620                         ASSERT(err2 == 0);
 621                         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 622                         return (err);
 623                 }
 624         }
 625 
 626 
 627         /* Actually modify the configuration */
 628         mutex_enter(&tokenmt_data->tokenmt_lock);
 629         tokenmt_data->cfg_parms = cfg_parms;
 630         mutex_exit(&tokenmt_data->tokenmt_lock);
 631 
 632         /* Un-ref the old actions */
 633         err = ipp_action_unref(aid, old_cfg->red_action, flags);
 634         ASSERT(err == 0);
 635         if (old_cfg->yellow_action != TOKENMT_NO_ACTION) {
 636                 err = ipp_action_unref(aid, old_cfg->yellow_action, flags);
 637                 ASSERT(err == 0);
 638         }
 639         err = ipp_action_unref(aid, old_cfg->green_action, flags);
 640         ASSERT(err == 0);
 641 
 642         /* Free the old configuration */
 643         kmem_free(old_cfg, TOKENMT_CFG_SZ);
 644         return (0);
 645 }
 646 
 647 static int
 648 tokenmt_destroy_action(ipp_action_id_t aid, ipp_flags_t flags)
 649 {
 650         tokenmt_data_t *tokenmt_data;
 651         tokenmt_cfg_t *cfg_parms;
 652         int rc;
 653 
 654         tokenmt_data = (tokenmt_data_t *)ipp_action_get_ptr(aid);
 655         ASSERT(tokenmt_data != NULL);
 656 
 657         cfg_parms = tokenmt_data->cfg_parms;
 658 
 659         if (cfg_parms->stats) {
 660                 ipp_stat_destroy(tokenmt_data->stats);
 661         }
 662 
 663         /* unreference the action */
 664         rc = ipp_action_unref(aid, cfg_parms->red_action, flags);
 665         ASSERT(rc == 0);
 666         if (cfg_parms->yellow_action != TOKENMT_NO_ACTION) {
 667                 rc = ipp_action_unref(aid, cfg_parms->yellow_action, flags);
 668                 ASSERT(rc == 0);
 669         }
 670         rc = ipp_action_unref(aid, cfg_parms->green_action, flags);
 671         ASSERT(rc == 0);
 672 
 673         mutex_destroy(&tokenmt_data->tokenmt_lock);
 674         kmem_free(cfg_parms, TOKENMT_CFG_SZ);
 675         kmem_free(tokenmt_data, TOKENMT_DATA_SZ);
 676         return (0);
 677 }
 678 
 679 static int
 680 tokenmt_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet)
 681 {
 682         tokenmt_data_t *tokenmt_data;
 683         ipp_action_id_t next_action;
 684         mblk_t *mp = NULL;
 685         int rc;
 686 
 687         /* get mblk from ipp_packet structure */
 688         mp = ipp_packet_get_data(packet);
 689         tokenmt_data = (tokenmt_data_t *)ipp_action_get_ptr(aid);
 690         ASSERT(tokenmt_data != NULL);
 691 
 692         /* meter packet as configured */
 693         if ((rc = tokenmt_process(&mp, tokenmt_data, &next_action)) != 0) {
 694                 return (rc);
 695         } else {
 696                 return (ipp_packet_next(packet, next_action));
 697         }
 698 }
 699 
 700 static int
 701 tokenmt_statinit(ipp_action_id_t aid, tokenmt_data_t *tokenmt_data) {
 702 
 703         int rc = 0;
 704         meter_stat_t *statsp;
 705 
 706         /* install stats entry */
 707         if ((rc = ipp_stat_create(aid, TOKENMT_STATS_STRING, METER_STATS_COUNT,
 708             tokenmt_update_stats, tokenmt_data, &tokenmt_data->stats)) != 0) {
 709                 tokenmt0dbg(("tokenmt_statinit: ipp_stat_create failed "\
 710                     " with %d\n", rc));
 711                 return (rc);
 712         }
 713 
 714         statsp = (meter_stat_t *)(tokenmt_data->stats)->ipps_data;
 715         ASSERT(statsp != NULL);
 716 
 717         if ((rc = ipp_stat_named_init(tokenmt_data->stats, "red_packets",
 718             IPP_STAT_UINT64, &statsp->red_packets)) != 0) {
 719                 tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
 720                     " with %d\n", rc));
 721                 return (rc);
 722         }
 723         if ((rc = ipp_stat_named_init(tokenmt_data->stats, "yellow_packets",
 724             IPP_STAT_UINT64, &statsp->yellow_packets)) != 0) {
 725                 tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
 726                     " with %d\n", rc));
 727                 return (rc);
 728         }
 729         if ((rc = ipp_stat_named_init(tokenmt_data->stats, "green_packets",
 730             IPP_STAT_UINT64, &statsp->green_packets)) != 0) {
 731                 tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
 732                     " with %d\n", rc));
 733                 return (rc);
 734         }
 735         if ((rc = ipp_stat_named_init(tokenmt_data->stats, "red_bits",
 736             IPP_STAT_UINT64, &statsp->red_bits)) != 0) {
 737                 tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
 738                     " with %d\n", rc));
 739                 return (rc);
 740         }
 741         if ((rc = ipp_stat_named_init(tokenmt_data->stats, "yellow_bits",
 742             IPP_STAT_UINT64, &statsp->yellow_bits)) != 0) {
 743                 tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
 744                     " with %d\n", rc));
 745                 return (rc);
 746         }
 747         if ((rc = ipp_stat_named_init(tokenmt_data->stats, "green_bits",
 748             IPP_STAT_UINT64, &statsp->green_bits)) != 0) {
 749                 tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
 750                     " with %d\n", rc));
 751                 return (rc);
 752         }
 753         if ((rc = ipp_stat_named_init(tokenmt_data->stats, "epackets",
 754             IPP_STAT_UINT64, &statsp->epackets)) != 0) {
 755                 tokenmt0dbg(("tokenmt_statinit:ipp_stat_named_init failed "\
 756                     " with %d\n", rc));
 757                 return (rc);
 758         }
 759 
 760         ipp_stat_install(tokenmt_data->stats);
 761 
 762         return (rc);
 763 }
 764 
 765 static int
 766 tokenmt_update_stats(ipp_stat_t *sp, void *args, int rw)
 767 {
 768         tokenmt_data_t *tokenmt_data = (tokenmt_data_t *)args;
 769         meter_stat_t *stats = (meter_stat_t *)sp->ipps_data;
 770 
 771         ASSERT((tokenmt_data != NULL) && (stats != NULL));
 772 
 773         (void) ipp_stat_named_op(&stats->red_packets,
 774             &tokenmt_data->red_packets, rw);
 775         (void) ipp_stat_named_op(&stats->yellow_packets,
 776             &tokenmt_data->yellow_packets, rw);
 777         (void) ipp_stat_named_op(&stats->green_packets,
 778             &tokenmt_data->green_packets, rw);
 779         (void) ipp_stat_named_op(&stats->red_bits,
 780             &tokenmt_data->red_bits, rw);
 781         (void) ipp_stat_named_op(&stats->yellow_bits,
 782             &tokenmt_data->yellow_bits, rw);
 783         (void) ipp_stat_named_op(&stats->green_bits,
 784             &tokenmt_data->green_bits, rw);
 785         (void) ipp_stat_named_op(&stats->epackets, &tokenmt_data->epackets,
 786             rw);
 787 
 788         return (0);
 789 }
 790 
 791 /* ARGSUSED */
 792 static int
 793 tokenmt_info(ipp_action_id_t aid, int (*fn)(nvlist_t *, void *), void *arg,
 794     ipp_flags_t flags)
 795 {
 796         nvlist_t *nvlp;
 797         tokenmt_data_t *tokenmt_data;
 798         tokenmt_cfg_t *cfg_parms;
 799         char *next_action;
 800         int32_t dscp_to_colour[64];
 801         int rc;
 802 
 803         tokenmt_data = (tokenmt_data_t *)ipp_action_get_ptr(aid);
 804         ASSERT(tokenmt_data != NULL);
 805 
 806         cfg_parms = tokenmt_data->cfg_parms;
 807 
 808         /* allocate nvlist to be passed back */
 809         if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) {
 810                 tokenmt0dbg(("tokenmt_info: memory allocation failure\n"));
 811                 return (rc);
 812         }
 813 
 814         /* look up red next action with the next action id */
 815         if ((rc = ipp_action_name(cfg_parms->red_action, &next_action)) != 0) {
 816                 tokenmt0dbg(("tokenmt_info: red_action not available\n"));
 817                 nvlist_free(nvlp);
 818                 return (rc);
 819         }
 820 
 821         /* add next action name */
 822         if ((rc = nvlist_add_string(nvlp, TOKENMT_RED_ACTION_NAME,
 823             next_action)) != 0) {
 824                 nvlist_free(nvlp);
 825                 tokenmt0dbg(("tokenmt_info: error adding red_action\n"));
 826                 kmem_free(next_action, (strlen(next_action) + 1));
 827                 return (rc);
 828         }
 829 
 830         /* free action name */
 831         kmem_free(next_action, (strlen(next_action) + 1));
 832 
 833 
 834         /* look up yellow next action with the next action id */
 835         if (cfg_parms->yellow_action != TOKENMT_NO_ACTION) {
 836                 if ((rc = ipp_action_name(cfg_parms->yellow_action,
 837                     &next_action)) != 0) {
 838                         tokenmt0dbg(("tokenmt_info: yellow_action not "\
 839                             "available\n"));
 840                         nvlist_free(nvlp);
 841                         return (rc);
 842                 }
 843                 /* add next action name */
 844                 if ((rc = nvlist_add_string(nvlp, TOKENMT_YELLOW_ACTION_NAME,
 845                     next_action)) != 0) {
 846                         nvlist_free(nvlp);
 847                         tokenmt0dbg(("tokenmt_info: error adding "\
 848                             "yellow_action\n"));
 849                         kmem_free(next_action, (strlen(next_action) + 1));
 850                         return (rc);
 851                 }
 852                 /* free action name */
 853                 kmem_free(next_action, (strlen(next_action) + 1));
 854         }
 855 
 856         /* look up green next action with the next action id */
 857         if ((rc = ipp_action_name(cfg_parms->green_action,
 858             &next_action)) != 0) {
 859                 tokenmt0dbg(("tokenmt_info: green_action not available\n"));
 860                 nvlist_free(nvlp);
 861                 return (rc);
 862         }
 863 
 864         /* add next action name */
 865         if ((rc = nvlist_add_string(nvlp, TOKENMT_GREEN_ACTION_NAME,
 866             next_action)) != 0) {
 867                 nvlist_free(nvlp);
 868                 tokenmt0dbg(("tokenmt_info: error adding green_action\n"));
 869                 kmem_free(next_action, (strlen(next_action) + 1));
 870                 return (rc);
 871         }
 872 
 873         /* free action name */
 874         kmem_free(next_action, (strlen(next_action) + 1));
 875 
 876         /* add config type */
 877         if ((rc = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) {
 878                 tokenmt0dbg(("tokenmt_info: error adding config_type\n"));
 879                 nvlist_free(nvlp);
 880                 return (rc);
 881         }
 882 
 883         /* add committed_rate  */
 884         if ((rc = nvlist_add_uint32(nvlp, TOKENMT_COMMITTED_RATE,
 885             cfg_parms->committed_rate)) != 0) {
 886                 tokenmt0dbg(("tokenmt_info: error adding committed_rate\n"));
 887                 nvlist_free(nvlp);
 888                 return (rc);
 889         }
 890 
 891         if (cfg_parms->tokenmt_type == TRTCL_TOKENMT) {
 892                 /* add peak  rate */
 893                 if ((rc = nvlist_add_uint32(nvlp, TOKENMT_PEAK_RATE,
 894                     cfg_parms->peak_rate)) != 0) {
 895                         tokenmt0dbg(("tokenmt_info: error adding peak_rate\n"));
 896                         nvlist_free(nvlp);
 897                         return (rc);
 898                 }
 899         }
 900 
 901         /* add committed_burst  */
 902         if ((rc = nvlist_add_uint32(nvlp, TOKENMT_COMMITTED_BURST,
 903             cfg_parms->committed_burst)) != 0) {
 904                 tokenmt0dbg(("tokenmt_info: error adding committed_burst\n"));
 905                 nvlist_free(nvlp);
 906                 return (rc);
 907         }
 908 
 909         /* add peak_burst  */
 910         if (cfg_parms->peak_burst != 0) {
 911                 if ((rc = nvlist_add_uint32(nvlp, TOKENMT_PEAK_BURST,
 912                     cfg_parms->peak_burst)) != 0) {
 913                         tokenmt0dbg(("tokenmt_info: error adding peak "\
 914                             "burst\n"));
 915                         nvlist_free(nvlp);
 916                         return (rc);
 917                 }
 918         }
 919 
 920         /* add colour aware  */
 921         if ((rc = nvlist_add_uint32(nvlp, TOKENMT_COLOUR_AWARE,
 922             cfg_parms->colour_aware)) != 0) {
 923                 tokenmt0dbg(("tokenmt_info: error adding mode\n"));
 924                 nvlist_free(nvlp);
 925                 return (rc);
 926         }
 927 
 928         if (cfg_parms->colour_aware) {
 929                 bcopy(cfg_parms->dscp_to_colour, dscp_to_colour,
 930                     sizeof (cfg_parms->dscp_to_colour));
 931                 if ((rc = nvlist_add_int32_array(nvlp, TOKENMT_COLOUR_MAP,
 932                     dscp_to_colour, 64)) != 0) {
 933                         tokenmt0dbg(("tokenmt_info: error adding colour "\
 934                             "array\n"));
 935                         nvlist_free(nvlp);
 936                         return (rc);
 937                 }
 938         }
 939 
 940         if ((rc = nvlist_add_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
 941             (uint32_t)cfg_parms->stats)) != 0) {
 942                 tokenmt0dbg(("tokenmt_info: error adding stats status\n"));
 943                 nvlist_free(nvlp);
 944                 return (rc);
 945         }
 946 
 947         /* call back with nvlist */
 948         rc = fn(nvlp, arg);
 949 
 950         nvlist_free(nvlp);
 951         return (rc);
 952 }