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