xref: /illumos-gate/usr/src/uts/common/ipp/meters/tokenmtddi.c (revision 69b1fd3f24d0ee2e682883606201c61f52085805)
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 }
954