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