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