xref: /titanic_52/usr/src/uts/common/ipp/dscpmk/dscpmkddi.c (revision 9acbbeaf2a1ffe5c14b244867d427714fab43c5c)
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 2002 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/systm.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <sys/modctl.h>
34 #include <sys/sunddi.h>
35 #include <ipp/ipp.h>
36 #include <ipp/ipp_config.h>
37 #include <inet/common.h>
38 #include <ipp/dscpmk/dscpmk_impl.h>
39 
40 #define	D_SM_COMMENT	"IPP dscpmk marker module"
41 
42 /* DDI file for dscpmk ipp module */
43 
44 /* default dscp map - dscp unchanged */
45 uint8_t default_dscp_map[DSCPMK_ARRAY_COUNT] = {
46 	0,	1,	2,	3,
47 	4,	5,	6,	7,
48 	8,	9,	10,	11,
49 	12,	13,	14,	15,
50 	16,	17,	18,	19,
51 	20,	21,	22,	23,
52 	24,	25,	26,	27,
53 	28,	29,	30,	31,
54 	32,	33,	34,	35,
55 	36,	37,	38,	39,
56 	40,	41,	42,	43,
57 	44,	45,	46,	47,
58 	48,	49,	50,	51,
59 	52,	53,	54,	55,
60 	56,	57,	58,	59,
61 	60,	61,	62,	63
62 };
63 
64 static int dscpmk_create_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
65 static int dscpmk_modify_action(ipp_action_id_t, nvlist_t **, ipp_flags_t);
66 static int dscpmk_destroy_action(ipp_action_id_t, ipp_flags_t);
67 static int dscpmk_info(ipp_action_id_t, int (*)(nvlist_t *, void *), void *,
68     ipp_flags_t);
69 static int dscpmk_invoke_action(ipp_action_id_t, ipp_packet_t *);
70 
71 /* Creating and updating summary stats */
72 static int dscpmk_summ_statinit(ipp_action_id_t, dscpmk_data_t *);
73 static int dscpmk_update_stats(ipp_stat_t *, void *, int);
74 
75 /* Creating and updating per-dscp stats */
76 static int dscpmk_det_statinit(ipp_action_id_t, dscpmk_data_t *, int);
77 static int dscpmk_update_det_stats(ipp_stat_t *, void *, int);
78 
79 /* Entry points for this IPP module */
80 ipp_ops_t dscpmk_ops = {
81 	IPPO_REV,
82 	dscpmk_create_action,	/* ippo_action_create */
83 	dscpmk_modify_action,	/* ippo_action_modify */
84 	dscpmk_destroy_action,	/* ippo_action_destroy */
85 	dscpmk_info,		/* ippo_action_info */
86 	dscpmk_invoke_action	/* ippo_action_invoke */
87 };
88 
89 extern struct mod_ops mod_ippops;
90 
91 /*
92  * Module linkage information for the kernel.
93  */
94 static struct modlipp modlipp = {
95 	&mod_ippops,
96 	D_SM_COMMENT " %I%",
97 	&dscpmk_ops
98 };
99 
100 static struct modlinkage modlinkage = {
101 	MODREV_1,
102 	(void *)&modlipp,
103 	NULL
104 };
105 
106 
107 int
108 _init(void)
109 {
110 	return (mod_install(&modlinkage));
111 }
112 
113 int
114 _fini(void)
115 {
116 	return (mod_remove(&modlinkage));
117 }
118 
119 int
120 _info(struct modinfo *modinfop)
121 {
122 	return (mod_info(&modlinkage, modinfop));
123 }
124 
125 static int
126 dscpmk_create_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
127 {
128 	nvlist_t *nvlp;
129 	dscpmk_data_t *dscpmk_data;
130 	char *next_action;
131 	int err, cnt;
132 	int32_t *tbl;
133 	uint_t nelem = DSCPMK_ARRAY_COUNT;
134 	uint32_t bstats;
135 
136 	ASSERT((nvlpp != NULL) && (*nvlpp != NULL));
137 
138 	nvlp = *nvlpp;
139 	*nvlpp = NULL;		/* nvlist should be NULL on return */
140 
141 	if ((dscpmk_data = kmem_zalloc(DSCPMK_DATA_SZ, KM_NOSLEEP)) == NULL) {
142 		nvlist_free(nvlp);
143 		return (ENOMEM);
144 	}
145 
146 	/* parse next action name */
147 	if ((err = nvlist_lookup_string(nvlp, DSCPMK_NEXT_ACTION_NAME,
148 	    &next_action)) != 0) {
149 		nvlist_free(nvlp);
150 		dscpmk0dbg(("dscpmk_create_action: invalid config, " \
151 		    "next_action name missing\n"));
152 		kmem_free(dscpmk_data, DSCPMK_DATA_SZ);
153 		return (err);
154 	}
155 
156 	if ((dscpmk_data->next_action = ipp_action_lookup(next_action))
157 	    == IPP_ACTION_INVAL) {
158 		nvlist_free(nvlp);
159 		dscpmk0dbg(("dscpmk_create_action: next_action "\
160 		    "invalid\n"));
161 		kmem_free(dscpmk_data, DSCPMK_DATA_SZ);
162 		return (EINVAL);
163 	}
164 
165 	/* Fill in the default value */
166 	bcopy(default_dscp_map, dscpmk_data->dscp_map,
167 	    sizeof (default_dscp_map));
168 	/*
169 	 * parse dscp_map, if present. Note that the module gets
170 	 * the entire array with unchanged entries marked with -1.
171 	 */
172 	if ((err = nvlist_lookup_int32_array(nvlp, DSCPMK_DSCP_MAP,
173 	    &tbl, &nelem)) == 0) {
174 		for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
175 			if ((tbl[cnt] != DSCPMK_UNCHANGED_DSCP) && (tbl[cnt] !=
176 			    dscpmk_data->dscp_map[cnt])) {
177 				dscpmk_data->dscp_map[cnt] = tbl[cnt];
178 			}
179 		}
180 	}
181 
182 
183 	/* parse summary_stats boolean */
184 	if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
185 	    != 0) {
186 		dscpmk_data->summary_stats = B_FALSE;
187 	} else {
188 		dscpmk_data->summary_stats = (bstats != 0) ? B_TRUE : B_FALSE;
189 		/* If stats is needed, initialize the stats structure */
190 		if (dscpmk_data->summary_stats) {
191 			if ((err = dscpmk_summ_statinit(aid, dscpmk_data))
192 			    != 0) {
193 				nvlist_free(nvlp);
194 				kmem_free(dscpmk_data, DSCPMK_DATA_SZ);
195 				return (err);
196 			}
197 		}
198 	}
199 
200 	/*
201 	 * Initialize per-dscp stats; B_FALSE in present indicates a dscp
202 	 * with this value (count) is not present in the map.
203 	 */
204 	for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
205 		dscpmk_data->dscp_stats[cnt].present = B_FALSE;
206 		dscpmk_data->dscp_stats[cnt].npackets = 0;
207 	}
208 
209 	/* parse detailed_stats boolean */
210 	if ((err = nvlist_lookup_uint32(nvlp, DSCPMK_DETAILED_STATS, &bstats))
211 	    != 0) {
212 		dscpmk_data->detailed_stats = B_FALSE;
213 	} else {
214 		dscpmk_data->detailed_stats = (bstats != 0) ? B_TRUE : B_FALSE;
215 		/* If stats is needed, initialize the stats structure */
216 		if (dscpmk_data->detailed_stats) {
217 			for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
218 				int val = dscpmk_data->dscp_map[cnt];
219 				if (dscpmk_data->dscp_stats[val].present) {
220 					continue;
221 				}
222 				dscpmk_data->dscp_stats[val].present = B_TRUE;
223 				if ((err = dscpmk_det_statinit(aid, dscpmk_data,
224 				    val)) != 0) {
225 					nvlist_free(nvlp);
226 					kmem_free(dscpmk_data, DSCPMK_DATA_SZ);
227 					return (err);
228 				    }
229 			}
230 		}
231 	}
232 
233 	/* Free the nvlist */
234 	nvlist_free(nvlp);
235 
236 	/* set action chain reference */
237 	if ((err = ipp_action_ref(aid, dscpmk_data->next_action, flags)) != 0) {
238 		dscpmk0dbg(("dscpmk_create_action: ipp_action_ref " \
239 		    "returned with error %d\n", err));
240 		if (dscpmk_data->summary_stats) {
241 			ipp_stat_destroy(dscpmk_data->stats);
242 		}
243 		if (dscpmk_data->detailed_stats) {
244 			for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
245 				if (dscpmk_data->dscp_stats[cnt].present) {
246 					ipp_stat_destroy(
247 					    dscpmk_data->dscp_stats[cnt].stats);
248 				}
249 			}
250 		}
251 		kmem_free(dscpmk_data, DSCPMK_DATA_SZ);
252 		return (err);
253 	}
254 
255 	ipp_action_set_ptr(aid, (void *)dscpmk_data);
256 	return (0);
257 }
258 
259 static int
260 dscpmk_modify_action(ipp_action_id_t aid, nvlist_t **nvlpp, ipp_flags_t flags)
261 {
262 	nvlist_t *nvlp;
263 	int err = 0, cnt;
264 	uint8_t config_type;
265 	char *next_action_name;
266 	uint32_t bstats;
267 	uint_t nelem = DSCPMK_ARRAY_COUNT;
268 	int32_t *tbl;
269 	ipp_action_id_t next_action;
270 	dscpmk_data_t *dscpmk_data;
271 
272 	ASSERT((nvlpp != NULL) && (*nvlpp != NULL));
273 
274 	nvlp = *nvlpp;
275 	*nvlpp = NULL;		/* nvlist should be NULL when this returns */
276 
277 	if ((err = nvlist_lookup_byte(nvlp, IPP_CONFIG_TYPE, &config_type))
278 	    != 0) {
279 		nvlist_free(nvlp);
280 		dscpmk0dbg(("dscpmk_modify_action: invalid cfg. type\n"));
281 		return (err);
282 	}
283 
284 	if (config_type != IPP_SET) {
285 		nvlist_free(nvlp);
286 		dscpmk0dbg(("dscpmk_modify_action: invalid cfg. type " \
287 		    "%d\n", config_type));
288 		return (EINVAL);
289 	}
290 
291 	dscpmk_data = (dscpmk_data_t *)ipp_action_get_ptr(aid);
292 	ASSERT(dscpmk_data != NULL);
293 
294 	/* parse next action name, if present */
295 	if ((err = nvlist_lookup_string(nvlp, DSCPMK_NEXT_ACTION_NAME,
296 	    &next_action_name)) == 0) {
297 		/* lookup action name to get action id */
298 		if ((next_action = ipp_action_lookup(next_action_name))
299 		    == IPP_ACTION_INVAL) {
300 			nvlist_free(nvlp);
301 			dscpmk0dbg(("dscpmk_modify_action: next_action "\
302 			    "invalid\n"));
303 			return (EINVAL);
304 		}
305 		/* reference new action */
306 		if ((err = ipp_action_ref(aid, next_action, flags)) != 0) {
307 			nvlist_free(nvlp);
308 			dscpmk0dbg(("dscpmk_modify_action: ipp_action_ref " \
309 			    "returned with error %d\n", err));
310 			return (err);
311 		}
312 		/* unref old action */
313 		err = ipp_action_unref(aid, dscpmk_data->next_action, flags);
314 		ASSERT(err == 0);
315 		dscpmk_data->next_action = next_action;
316 	}
317 
318 	/*
319 	 * parse dscp_map, if present. Note that the module gets
320 	 * the entire array with unchanged entries marked with -1.
321 	 * If this array is absent during modification, it means revert to
322 	 * the default table.
323 	 */
324 	if ((err = nvlist_lookup_int32_array(nvlp, DSCPMK_DSCP_MAP,
325 	    &tbl, &nelem)) == 0) {
326 		for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
327 			if ((tbl[cnt] != DSCPMK_UNCHANGED_DSCP) && (tbl[cnt] !=
328 			    dscpmk_data->dscp_map[cnt])) {
329 				dscpmk_data->dscp_map[cnt] = tbl[cnt];
330 			}
331 		}
332 	} else {
333 		bcopy(default_dscp_map, dscpmk_data->dscp_map,
334 		    sizeof (default_dscp_map));
335 	}
336 
337 	/* parse summary_stats boolean, if present */
338 	if ((err = nvlist_lookup_uint32(nvlp, IPP_ACTION_STATS_ENABLE, &bstats))
339 	    == 0) {
340 		boolean_t val = (bstats != 0) ? B_TRUE : B_FALSE;
341 		/* Turning on stats */
342 		if (!dscpmk_data->summary_stats && val) {
343 			if ((err = dscpmk_summ_statinit(aid, dscpmk_data))
344 			    != 0) {
345 				nvlist_free(nvlp);
346 				return (err);
347 			}
348 		/* Turning off stats */
349 		} else if (!val && dscpmk_data->summary_stats) {
350 			ipp_stat_destroy(dscpmk_data->stats);
351 
352 		}
353 		dscpmk_data->summary_stats = val;
354 	}
355 
356 	/* parse detailed_stats boolean */
357 	if ((err = nvlist_lookup_uint32(nvlp, DSCPMK_DETAILED_STATS, &bstats))
358 	    == 0) {
359 		boolean_t val = (bstats != 0) ? B_TRUE : B_FALSE;
360 		if (dscpmk_data->detailed_stats && !val) {
361 			for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
362 				if (dscpmk_data->dscp_stats[cnt].present) {
363 					dscpmk_data->dscp_stats[cnt].present =
364 					    B_FALSE;
365 					ipp_stat_destroy(dscpmk_data->
366 					    dscp_stats[cnt].stats);
367 				}
368 			}
369 		}
370 		dscpmk_data->detailed_stats = val;
371 	}
372 
373 	/* The map might have changed */
374 	if (dscpmk_data->detailed_stats) {
375 		for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
376 			int val = dscpmk_data->dscp_map[cnt];
377 			if (!dscpmk_data->dscp_stats[val].present) {
378 				dscpmk_data->dscp_stats[val].present = B_TRUE;
379 				if ((err = dscpmk_det_statinit(aid, dscpmk_data,
380 				    val)) != 0) {
381 					nvlist_free(nvlp);
382 					return (err);
383 				}
384 			}
385 		}
386 	}
387 
388 	/* Free the nvlist */
389 	nvlist_free(nvlp);
390 	return (0);
391 }
392 
393 static int
394 dscpmk_destroy_action(ipp_action_id_t aid, ipp_flags_t flags)
395 {
396 	dscpmk_data_t *dscpmk_data;
397 	int err, cnt;
398 
399 	dscpmk_data = (dscpmk_data_t *)ipp_action_get_ptr(aid);
400 	ASSERT(dscpmk_data != NULL);
401 
402 	/* Destroy stats, if gathered */
403 	if (dscpmk_data->summary_stats) {
404 		ipp_stat_destroy(dscpmk_data->stats);
405 	}
406 
407 	if (dscpmk_data->detailed_stats) {
408 		for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
409 			if (dscpmk_data->dscp_stats[cnt].present) {
410 				ipp_stat_destroy(dscpmk_data->dscp_stats[cnt].
411 				    stats);
412 			}
413 		}
414 	}
415 
416 	/* unreference the action */
417 	err = ipp_action_unref(aid, dscpmk_data->next_action, flags);
418 	ASSERT(err == 0);
419 
420 	kmem_free(dscpmk_data, DSCPMK_DATA_SZ);
421 	return (0);
422 }
423 
424 static int
425 dscpmk_invoke_action(ipp_action_id_t aid, ipp_packet_t *packet)
426 {
427 	dscpmk_data_t *dscpmk_data;
428 	mblk_t *mp = NULL;
429 	ip_priv_t *priv;
430 	int err;
431 
432 	ASSERT(packet != NULL);
433 
434 	/* get mblk from ipp_packet structure */
435 	mp = ipp_packet_get_data(packet);
436 	priv = (ip_priv_t *)ipp_packet_get_private(packet);
437 
438 	dscpmk_data = (dscpmk_data_t *)ipp_action_get_ptr(aid);
439 	ASSERT(dscpmk_data != NULL);
440 
441 	/* dscpmk packet as configured */
442 	if ((err = dscpmk_process(&mp, dscpmk_data, priv->proc)) != 0) {
443 		return (err);
444 	} else {
445 		/* return packet with next action set */
446 		return (ipp_packet_next(packet, dscpmk_data->next_action));
447 	}
448 }
449 
450 static int
451 dscpmk_det_statinit(ipp_action_id_t aid, dscpmk_data_t *dscpmk_data, int val)
452 {
453 	int err = 0;
454 	dscpmk_dscp_stats_t *statp;
455 	char stats_string[15];
456 
457 	(void) sprintf(stats_string, "dscpmk_dscp0x%x", val);
458 
459 	/* install stats entry */
460 	if ((err = ipp_stat_create(aid, stats_string, DSCPMK_DSCP_STATS_COUNT,
461 	    dscpmk_update_det_stats, dscpmk_data,
462 	    &dscpmk_data->dscp_stats[val].stats)) != 0) {
463 		dscpmk0dbg(("dscpmk_det_statinit: ipp_stat_create returned "\
464 		    "with error %d\n", err));
465 		return (err);
466 	}
467 
468 	statp = (dscpmk_dscp_stats_t *)
469 	    (dscpmk_data->dscp_stats[val].stats)->ipps_data;
470 	ASSERT(statp != NULL);
471 
472 	if ((err = ipp_stat_named_init(dscpmk_data->dscp_stats[val].stats,
473 	    "dscp", IPP_STAT_UINT32, &statp->dscp)) != 0) {
474 		dscpmk0dbg(("dscpmk_det_statinit: ipp_stat_named_init "\
475 		    "returned with error %d\n", err));
476 		return (err);
477 	}
478 
479 	if ((err = ipp_stat_named_init(dscpmk_data->dscp_stats[val].stats,
480 	    "npackets", IPP_STAT_UINT64, &statp->npackets)) != 0) {
481 		dscpmk0dbg(("dscpmk_det_statinit: ipp_stat_named_init "\
482 		    "returned with error %d\n", err));
483 		return (err);
484 	}
485 
486 	ipp_stat_install(dscpmk_data->dscp_stats[val].stats);
487 	return (0);
488 }
489 
490 
491 static int
492 dscpmk_summ_statinit(ipp_action_id_t aid, dscpmk_data_t *dscpmk_data)
493 {
494 	int err = 0;
495 	dscpmk_stat_t *statp;
496 
497 	/* install stats entry */
498 	if ((err = ipp_stat_create(aid, DSCPMK_STATS_STRING, DSCPMK_STATS_COUNT,
499 	    dscpmk_update_stats, dscpmk_data, &dscpmk_data->stats)) != 0) {
500 		dscpmk0dbg(("dscpmk_create_action: ipp_stat_create returned " \
501 		    "with error %d\n", err));
502 		return (err);
503 	}
504 
505 	statp = (dscpmk_stat_t *)(dscpmk_data->stats)->ipps_data;
506 	ASSERT(statp != NULL);
507 
508 	if ((err = ipp_stat_named_init(dscpmk_data->stats, "npackets",
509 	    IPP_STAT_UINT64, &statp->npackets)) != 0) {
510 		dscpmk0dbg(("dscpmk_summ_statinit: ipp_stat_named_init " \
511 		    "returned with error %d\n", err));
512 		return (err);
513 	}
514 
515 	if ((err = ipp_stat_named_init(dscpmk_data->stats, "dscp_changed",
516 	    IPP_STAT_UINT64, &statp->dscp_changed)) != 0) {
517 		dscpmk0dbg(("dscpmk_summ_statinit: ipp_stat_named_init " \
518 		    "returned with error %d\n", err));
519 		return (err);
520 	}
521 
522 	if ((err = ipp_stat_named_init(dscpmk_data->stats, "dscp_unchanged",
523 	    IPP_STAT_UINT64, &statp->dscp_unchanged)) != 0) {
524 		dscpmk0dbg(("dscpmk_summ_statinit: ipp_stat_named_init " \
525 		    "returned with error %d\n", err));
526 		return (err);
527 	}
528 
529 	if ((err = ipp_stat_named_init(dscpmk_data->stats, "ipackets",
530 	    IPP_STAT_UINT64, &statp->ipackets)) != 0) {
531 		dscpmk0dbg(("dscpmk_summ_statinit: ipp_stat_named_init " \
532 		    "returned with error %d\n", err));
533 		return (err);
534 	}
535 
536 	if ((err = ipp_stat_named_init(dscpmk_data->stats, "epackets",
537 	    IPP_STAT_UINT64, &statp->epackets)) != 0) {
538 		dscpmk0dbg(("dscpmk_summ_statinit: ipp_stat_named_init " \
539 		    "returned with error %d\n", err));
540 		return (err);
541 	}
542 
543 	ipp_stat_install(dscpmk_data->stats);
544 	return (0);
545 }
546 
547 /* ARGSUSED */
548 static int
549 dscpmk_update_det_stats(ipp_stat_t *sp, void *arg, int rw)
550 {
551 	dscpmk_data_t *dscpmk_data = (dscpmk_data_t *)arg;
552 	dscpmk_dscp_stats_t *statp;
553 	uint32_t count;
554 
555 	for (count = 0; count < DSCPMK_ARRAY_COUNT; count++) {
556 		if (!dscpmk_data->dscp_stats[count].present)
557 			continue;
558 		statp = (dscpmk_dscp_stats_t *)
559 		    (dscpmk_data->dscp_stats[count].stats)->ipps_data;
560 		ASSERT(statp != NULL);
561 		(void) ipp_stat_named_op(&statp->npackets,
562 		    &dscpmk_data->dscp_stats[count].npackets, rw);
563 		(void) ipp_stat_named_op(&statp->dscp, &count, rw);
564 	}
565 	return (0);
566 }
567 
568 static int
569 dscpmk_update_stats(ipp_stat_t *sp, void *arg, int rw)
570 {
571 	dscpmk_data_t *dscpmk_data = (dscpmk_data_t *)arg;
572 	dscpmk_stat_t *snames = (dscpmk_stat_t *)sp->ipps_data;
573 	ASSERT(dscpmk_data != NULL);
574 	ASSERT(snames != NULL);
575 
576 	(void) ipp_stat_named_op(&snames->npackets, &dscpmk_data->npackets, rw);
577 	(void) ipp_stat_named_op(&snames->dscp_changed, &dscpmk_data->changed,
578 	    rw);
579 	(void) ipp_stat_named_op(&snames->dscp_unchanged,
580 	    &dscpmk_data->unchanged, rw);
581 	(void) ipp_stat_named_op(&snames->ipackets, &dscpmk_data->ipackets, rw);
582 	(void) ipp_stat_named_op(&snames->epackets, &dscpmk_data->epackets, rw);
583 
584 	return (0);
585 }
586 
587 /* ARGSUSED */
588 static int
589 dscpmk_info(ipp_action_id_t aid, int (*fn)(nvlist_t *, void *), void *arg,
590     ipp_flags_t flags)
591 {
592 	nvlist_t *nvlp;
593 	dscpmk_data_t *dscpmk_data;
594 	char *next_action;
595 	int err, cnt;
596 	int32_t dscp_map[DSCPMK_ARRAY_COUNT];
597 
598 	ASSERT(fn != NULL);
599 
600 	dscpmk_data = (dscpmk_data_t *)ipp_action_get_ptr(aid);
601 	ASSERT(dscpmk_data != NULL);
602 
603 	/* allocate nvlist to be passed back */
604 	if ((err = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_NOSLEEP)) != 0) {
605 		dscpmk0dbg(("dscpmk_info: error allocating memory\n"));
606 		return (err);
607 	}
608 
609 	/* look up next action with the next action id */
610 	if ((err = ipp_action_name(dscpmk_data->next_action,
611 	    &next_action)) != 0) {
612 		dscpmk0dbg(("dscpmk_info: next action not available\n"));
613 		nvlist_free(nvlp);
614 		return (err);
615 	}
616 
617 	/* add next action name */
618 	if ((err = nvlist_add_string(nvlp, DSCPMK_NEXT_ACTION_NAME,
619 	    next_action)) != 0) {
620 		dscpmk0dbg(("dscpmk_info: error adding next action\n"));
621 		nvlist_free(nvlp);
622 		kmem_free(next_action, (strlen(next_action) + 1));
623 		return (err);
624 	}
625 
626 	/* free action name */
627 	kmem_free(next_action, (strlen(next_action) + 1));
628 
629 	/* add config type */
630 	if ((err = nvlist_add_byte(nvlp, IPP_CONFIG_TYPE, IPP_SET)) != 0) {
631 		dscpmk0dbg(("dscpmk_info: error adding config type\n"));
632 		nvlist_free(nvlp);
633 		return (err);
634 	}
635 
636 	/* add dscp map */
637 	bcopy(dscpmk_data->dscp_map, dscp_map, sizeof (dscp_map));
638 	for (cnt = 0; cnt < DSCPMK_ARRAY_COUNT; cnt++) {
639 		    dscp_map[cnt] = dscpmk_data->dscp_map[cnt];
640 	}
641 	if ((err = nvlist_add_int32_array(nvlp, DSCPMK_DSCP_MAP,
642 	    dscp_map, DSCPMK_ARRAY_COUNT)) != 0) {
643 		dscpmk0dbg(("dscpmk_info: error adding dscp map\n"));
644 		nvlist_free(nvlp);
645 		return (err);
646 	}
647 
648 	/* add summary stats boolean */
649 	if ((err = nvlist_add_uint32(nvlp, IPP_ACTION_STATS_ENABLE,
650 	    (dscpmk_data->summary_stats ? 1 : 0))) != 0) {
651 		dscpmk0dbg(("dscpmk_info: error adding stats status\n"));
652 		nvlist_free(nvlp);
653 		return (err);
654 	}
655 
656 	/* add detailed stats boolean */
657 	if ((err = nvlist_add_uint32(nvlp, DSCPMK_DETAILED_STATS,
658 	    (dscpmk_data->detailed_stats ? 1 : 0))) != 0) {
659 		dscpmk0dbg(("dscpmk_info: error adding det stats status\n"));
660 		nvlist_free(nvlp);
661 		return (err);
662 	}
663 
664 	/* call back with nvlist */
665 	err = fn(nvlp, arg);
666 
667 	nvlist_free(nvlp);
668 	return (err);
669 }
670