xref: /illumos-gate/usr/src/uts/sun4v/io/iospc/iospc.c (revision 8b92a81b)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * IO Performance Counter Driver
29  */
30 
31 #include <sys/types.h>
32 #include <sys/ddi.h>
33 #include <sys/modctl.h>
34 #include "iospc.h"
35 
36 /* Debugging level. */
37 #ifdef DEBUG
38 int iospc_debug = 0;
39 #endif /* DEBUG */
40 
41 /* State structure anchor. */
42 void *iospc_state_p;
43 
44 static int iospc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
45 static int iospc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
46 static int iospc_create_name_kstat(iospc_grp_t *grp);
47 static void iospc_delete_name_kstats(kstat_t **name_kstats_pp,
48     int num_kstats);
49 static kstat_t *iospc_create_cntr_kstat(char *name, int dev_inst,
50     int (*update)(kstat_t *, int), iospc_ksinfo_t *ksinfop, int num_pics);
51 static int iospc_kstat_update(kstat_t *ksp, int rw);
52 static kstat_t *iospc_create_picN_kstat(char *mod_name, int pic,
53     uint64_t mask, int num_ev, iospc_event_t *ev_array);
54 
55 iospc_grp_t **iospc_leaf_grps = NULL;
56 int iospc_kstat_inited = 0;
57 kmutex_t iospc_mutex;
58 
59 static struct dev_ops iospc_ops = {
60 	DEVO_REV,
61 	0,
62 	nulldev,
63 	nulldev,
64 	nulldev,
65 	iospc_attach,
66 	iospc_detach,
67 	nodev,
68 	NULL,
69 	NULL,
70 	nodev
71 };
72 
73 extern struct mod_ops mod_driverops;
74 
75 static struct modldrv md = {
76 	&mod_driverops,
77 	"IO Perf Counter Driver",
78 	&iospc_ops,
79 };
80 
81 static struct modlinkage ml = {
82 	MODREV_1,
83 	(void *)&md,
84 	NULL
85 };
86 
87 /*
88  * One-time module-wide initialization.
89  */
90 int
_init(void)91 _init(void)
92 {
93 	int rval;
94 
95 	/* Initialize per-leaf soft state pointer. */
96 	if ((rval = ddi_soft_state_init(&iospc_state_p,
97 	    sizeof (iospc_t), 1)) != DDI_SUCCESS)
98 		return (rval);
99 
100 	/* If all checks out, install the module. */
101 	if ((rval = mod_install(&ml)) != DDI_SUCCESS) {
102 		ddi_soft_state_fini(&iospc_state_p);
103 		return (rval);
104 	}
105 	mutex_init(&iospc_mutex, NULL, MUTEX_DRIVER, NULL);
106 	return (DDI_SUCCESS);
107 }
108 
109 /*
110  * One-time module-wide cleanup, after last detach is done.
111  */
112 int
_fini(void)113 _fini(void)
114 {
115 	int rval;
116 
117 	/*
118 	 * Remove the module first as this operation is the only thing here
119 	 * which can fail.
120 	 */
121 	rval = mod_remove(&ml);
122 	if (rval != DDI_SUCCESS)
123 		return (rval);
124 
125 	if (iospc_leaf_grps != NULL) {
126 		iospc_kstat_fini();
127 		mutex_enter(&iospc_mutex);
128 		iospc_kstat_inited = 0;
129 		(void) rfios_unbind_group();
130 		iospc_leaf_grps = NULL;
131 		mutex_exit(&iospc_mutex);
132 	}
133 
134 	mutex_destroy(&iospc_mutex);
135 
136 	/* Free px soft state */
137 	ddi_soft_state_fini(&iospc_state_p);
138 
139 	return (DDI_SUCCESS);
140 }
141 
142 int
_info(struct modinfo * modinfop)143 _info(struct modinfo *modinfop)
144 {
145 	return (mod_info(&ml, modinfop));
146 }
147 
148 /*
149  * Per-instance initialization.  Suspend/resume not supported.
150  */
151 static int
iospc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)152 iospc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
153 {
154 	iospc_t *iospc_p;
155 	int instance = ddi_get_instance(dip);
156 	char *ptr;
157 
158 	IOSPC_DBG2("iospc: iospc_attach: enter\n");
159 	switch (cmd) {
160 	case DDI_RESUME:
161 	case DDI_ATTACH:
162 		/* Initialize one-time kstat structures. */
163 		mutex_enter(&iospc_mutex);
164 		if (!iospc_kstat_inited) {
165 			if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
166 			    0, "compatible", &ptr)) != DDI_PROP_SUCCESS)
167 				goto bad_property;
168 
169 			if ((strcmp(ptr, "SUNW,ktios-pr") == 0) ||
170 			    (strcmp(ptr, "SUNW,rfios-pr") == 0)) {
171 				iospc_leaf_grps = rfios_bind_group();
172 			} else {
173 				ddi_prop_free(ptr);
174 				goto bad_property;
175 			}
176 
177 			ddi_prop_free(ptr);
178 
179 			if (iospc_kstat_init() != DDI_SUCCESS)
180 				goto bad_kstat_init;
181 
182 			iospc_kstat_inited++;
183 		}
184 		mutex_exit(&iospc_mutex);
185 
186 		if (ddi_soft_state_zalloc(iospc_state_p, instance) !=
187 		    DDI_SUCCESS) {
188 			goto bad_softstate;
189 		}
190 
191 		iospc_p = (iospc_t *)ddi_get_soft_state(iospc_state_p,
192 		    instance);
193 
194 		iospc_p->iospc_dip = dip;
195 
196 		/* Set up kstats. */
197 
198 		if (iospc_kstat_attach(iospc_p) != DDI_SUCCESS)
199 			goto bad_kstat_attach;
200 
201 		IOSPC_DBG2("iospc: iospc_attach: exit SUCCESS\n");
202 
203 		return (DDI_SUCCESS);
204 
205 bad_kstat_attach:
206 		(void) ddi_soft_state_free(iospc_state_p, instance);
207 bad_softstate:
208 		iospc_kstat_fini();
209 bad_kstat_init:
210 bad_property:
211 		mutex_enter(&iospc_mutex);
212 		IOSPC_DBG2("iospc: iospc_attach: exit FAILURE\n");
213 		return (DDI_FAILURE);
214 
215 	default:
216 		return (DDI_FAILURE);
217 	}
218 }
219 
220 /*
221  * Per-instance cleanup.  Suspend/resume not supported.
222  */
223 static int
iospc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)224 iospc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
225 {
226 	int instance = ddi_get_instance(dip);
227 
228 	IOSPC_DBG2("iospc: iospc_detach: enter\n");
229 	iospc_t *iospc_p = (iospc_t *)ddi_get_soft_state(
230 	    iospc_state_p, instance);
231 
232 	switch (cmd) {
233 	case DDI_SUSPEND:
234 	case DDI_DETACH:
235 		iospc_kstat_detach(iospc_p);
236 		(void) ddi_soft_state_free(iospc_state_p, instance);
237 
238 		IOSPC_DBG2("iospc: iospc_detach: exit - SUCCESS\n");
239 		return (DDI_SUCCESS);
240 
241 	default:
242 		IOSPC_DBG2("iospc: iospc_detach: exit - FAILURE\n");
243 		return (DDI_FAILURE);
244 	}
245 }
246 
247 #define	PIC_STR_LEN	5	/* Size of a PICx name string. */
248 
249 /*
250  * One-time initialization for this module.
251  */
252 int
iospc_kstat_init()253 iospc_kstat_init()
254 {
255 	iospc_grp_t **grp_pp;
256 	iospc_grp_t *grp_p;
257 
258 	IOSPC_DBG2("iospc: kstat_init: enter\n");
259 
260 	/*
261 	 * Initialize the name kstats for each group, drawing upon the table
262 	 * for values.
263 	 */
264 	for (grp_pp = iospc_leaf_grps; *grp_pp != NULL; grp_pp++) {
265 
266 		grp_p = *grp_pp;
267 
268 		IOSPC_DBG2("Setting up group for %s\n", grp_p->grp_name);
269 
270 		/* Create basic pic event-type pair. */
271 		grp_p->name_kstats_pp = kmem_zalloc((grp_p->num_counters *
272 		    sizeof (kstat_t)), KM_SLEEP);
273 		if (iospc_create_name_kstat(grp_p) != DDI_SUCCESS) {
274 			iospc_kstat_fini();
275 			IOSPC_DBG1("iospc: init: failure exit\n");
276 			return (DDI_FAILURE);
277 		}
278 	}
279 
280 	IOSPC_DBG2("iospc: kstat_init: success exit\n");
281 
282 	return (DDI_SUCCESS);
283 }
284 
285 /*
286  * Per-instance initialization for this module.
287  */
288 int
iospc_kstat_attach(iospc_t * iospc_p)289 iospc_kstat_attach(iospc_t *iospc_p)
290 {
291 	iospc_grp_t **grp_pp;
292 	iospc_grp_t *grp_p;
293 	iospc_ksinfo_t *ksinfo_p;
294 
295 	int i;
296 
297 	IOSPC_DBG2("iospc: kstat_attach %d: enter\n",
298 	    ddi_get_instance(iospc_p->iospc_dip));
299 
300 	/* Set up kstats for each group. */
301 	for (i = 0, grp_pp = iospc_leaf_grps; *grp_pp != NULL; i++, grp_pp++) {
302 
303 		if (i >= IOSPC_MAX_NUM_GRPS)
304 			goto err;
305 
306 		grp_p = *grp_pp;
307 
308 		/*
309 		 * ksinfo_p keeps all info needed by iospc_kstat_update,
310 		 * which is fired off asynchronously on demand by the kstat
311 		 * framework.
312 		 */
313 		ksinfo_p = (iospc_ksinfo_t *)kmem_zalloc(
314 		    sizeof (iospc_ksinfo_t), KM_SLEEP);
315 
316 		ksinfo_p->iospc_p = iospc_p;
317 		ksinfo_p->grp_p  = grp_p;
318 
319 		/* Also save in state structure, for later cleanup. */
320 		iospc_p->iospc_ksinfo_p[i] = ksinfo_p;
321 
322 		/* Create counter kstats */
323 		ksinfo_p->cntr_ksp = iospc_create_cntr_kstat(grp_p->grp_name,
324 		    ddi_get_instance(iospc_p->iospc_dip),
325 		    iospc_kstat_update, ksinfo_p, grp_p->num_counters);
326 
327 		if (ksinfo_p->cntr_ksp == NULL)
328 			goto err;
329 
330 		if (grp_p->access_init(iospc_p, ksinfo_p) != SUCCESS)
331 			goto err;
332 	}
333 
334 	IOSPC_DBG2("iospc: kstat_attach: success exit\n");
335 	return (DDI_SUCCESS);
336 err:
337 	iospc_kstat_detach(iospc_p);
338 	IOSPC_DBG2("iospc: kstat_attach: failure exit\n");
339 	return (DDI_FAILURE);
340 }
341 
342 /*
343  * Create the name kstats for each group.
344  */
345 static int
iospc_create_name_kstat(iospc_grp_t * grp_p)346 iospc_create_name_kstat(iospc_grp_t *grp_p)
347 {
348 	int i;
349 
350 	for (i = 0; i < grp_p->num_counters; i++) {
351 		grp_p->name_kstats_pp[i] = iospc_create_picN_kstat(
352 		    grp_p->grp_name, i,
353 		    grp_p->regsel_p->fields_p[i].event_offset,
354 		    grp_p->regsel_p->fields_p[i].num_events,
355 		    grp_p->regsel_p->fields_p[i].events_p);
356 
357 		if (grp_p->name_kstats_pp[i] == NULL)
358 			return (DDI_FAILURE);
359 	}
360 	return (DDI_SUCCESS);
361 }
362 
363 /*
364  * Create the picN kstat. Returns a pointer to the
365  * kstat which the driver must store to allow it
366  * to be deleted when necessary.
367  */
368 static kstat_t *
iospc_create_picN_kstat(char * mod_name,int pic,uint64_t ev_offset,int num_ev,iospc_event_t * ev_array)369 iospc_create_picN_kstat(char *mod_name, int pic, uint64_t ev_offset,
370     int num_ev, iospc_event_t *ev_array)
371 {
372 	int event;
373 	char pic_name[PIC_STR_LEN];
374 	kstat_t	*picN_ksp = NULL;
375 	struct kstat_named *pic_named_data;
376 
377 	(void) snprintf(pic_name, PIC_STR_LEN, "pic%1d", pic);
378 
379 	if ((picN_ksp = kstat_create(mod_name, 0, pic_name,
380 	    "bus", KSTAT_TYPE_NAMED, num_ev, 0)) == NULL) {
381 		return (NULL);
382 	}
383 
384 	/* NOTE: Number of events is assumed to always be non-zero. */
385 
386 	pic_named_data = (struct kstat_named *)picN_ksp->ks_data;
387 
388 	/*
389 	 * Fill up data section of the kstat
390 	 * Write event names and their associated pcr masks.
391 	 * num_ev - 1 is because CLEAR_PIC is added separately.
392 	 */
393 	for (event = 0; event < num_ev - 1; event++) {
394 		pic_named_data[event].value.ui64 =
395 		    ev_array[event].value << ev_offset;
396 
397 		kstat_named_init(&pic_named_data[event],
398 		    ev_array[event].name, KSTAT_DATA_UINT64);
399 	}
400 
401 	/*
402 	 * add the clear_pic entry
403 	 */
404 	pic_named_data[event].value.ui64 =
405 	    (uint64_t)~(ev_array[event].value << ev_offset);
406 
407 	kstat_named_init(&pic_named_data[event], ev_array[event].name,
408 	    KSTAT_DATA_UINT64);
409 
410 	kstat_install(picN_ksp);
411 
412 	return (picN_ksp);
413 }
414 
415 /*
416  * Create the "counters" kstat.
417  */
418 static kstat_t *
iospc_create_cntr_kstat(char * name,int dev_inst,int (* update)(kstat_t *,int),iospc_ksinfo_t * ksinfop,int num_pics)419 iospc_create_cntr_kstat(char *name, int dev_inst,
420     int (*update)(kstat_t *, int), iospc_ksinfo_t *ksinfop, int num_pics)
421 {
422 	int i;
423 	char pic_str[PIC_STR_LEN];
424 	struct kstat *counters_ksp;
425 	struct kstat_named *counters_named_data;
426 
427 	IOSPC_DBG2("iospc_create_cntr_kstat: name: %s instance: %d\n",
428 	    name, dev_inst);
429 
430 	/*
431 	 * Size of kstat is num_pics + 1. extra one for pcr.
432 	 */
433 
434 	if ((counters_ksp = kstat_create(name, dev_inst, "counters", "bus",
435 	    KSTAT_TYPE_NAMED, num_pics + 1, KSTAT_FLAG_WRITABLE)) == NULL) {
436 		return (NULL);
437 	}
438 
439 	counters_named_data = (struct kstat_named *)(counters_ksp->ks_data);
440 	kstat_named_init(&counters_named_data[0], "pcr", KSTAT_DATA_UINT64);
441 
442 	for (i = 0; i < num_pics; i++) {
443 		(void) snprintf(pic_str, PIC_STR_LEN, "pic%1d", i);
444 
445 		kstat_named_init(&counters_named_data[i+1], pic_str,
446 		    KSTAT_DATA_UINT64);
447 	}
448 
449 	/*
450 	 * Store the reg type and other info. in the kstat's private field
451 	 * so that they are available to the update function.
452 	 */
453 	counters_ksp->ks_private = (void *)ksinfop;
454 	counters_ksp->ks_update = update;
455 
456 	kstat_install(counters_ksp);
457 
458 	return (counters_ksp);
459 }
460 
461 /*
462  * Program a performance counter.
463  *
464  * reggroup is which type of counter.
465  * counter is the counter number.
466  * event is the event to program for that counter.
467  */
468 static int
iospc_perfcnt_program(iospc_t * iospc_p,iospc_grp_t * grp_p,iospc_ksinfo_t * ksinfo_p,uint64_t new_events)469 iospc_perfcnt_program(iospc_t *iospc_p, iospc_grp_t *grp_p,
470     iospc_ksinfo_t *ksinfo_p, uint64_t new_events)
471 {
472 	uint64_t old_events;
473 	int rval = SUCCESS;
474 	uint64_t event_mask;
475 	int counter;
476 
477 	IOSPC_DBG1(
478 	    "iospc_perfcnt_program enter: new_events:0x%" PRIx64 "\n",
479 	    new_events);
480 
481 	if ((rval = grp_p->access(iospc_p, ksinfo_p->arg, IOSPC_REG_READ,
482 	    grp_p->regsel_p->regoff, &old_events)) != SUCCESS)
483 		goto done_pgm;
484 
485 	IOSPC_DBG1("  old_events:0x%" PRIx64 "\n", old_events);
486 
487 	for (counter = 0; counter < grp_p->num_counters; counter++) {
488 
489 		if (grp_p->counters_p[counter].zero_regoff == NO_REGISTER)
490 			continue;
491 
492 		event_mask = grp_p->regsel_p->fields_p[counter].event_mask <<
493 		    grp_p->regsel_p->fields_p[counter].event_offset;
494 
495 		IOSPC_DBG1(
496 		    "grp:%s, counter:%d, zero_regoff:0x%lx, "
497 		    "event_mask:0x%" PRIx64 ", old&mask:0x%lx, "
498 		    "new&mask:0x%lx\n",
499 		    grp_p->grp_name, counter,
500 		    grp_p->counters_p[counter].zero_regoff,
501 		    event_mask, old_events & event_mask,
502 		    new_events & event_mask);
503 
504 		if ((old_events & event_mask) ==
505 		    (new_events & event_mask))
506 			continue;
507 
508 		IOSPC_DBG1("Zeroing counter %d\n", counter);
509 
510 		if ((rval = grp_p->access(iospc_p, ksinfo_p->arg,
511 		    IOSPC_REG_WRITE, grp_p->counters_p[counter].zero_regoff,
512 		    &grp_p->counters_p[counter].zero_value)) != SUCCESS)
513 			goto done_pgm;
514 	}
515 
516 	if (old_events != new_events) {
517 
518 		IOSPC_DBG1("old != new, setting event reg %ld to 0x%lx\n",
519 		    grp_p->regsel_p->regoff, new_events);
520 
521 		if ((rval = grp_p->access(iospc_p, ksinfo_p->arg,
522 		    IOSPC_REG_WRITE, grp_p->regsel_p->regoff, &new_events))
523 		    != SUCCESS) {
524 			IOSPC_DBG1(
525 			    "Write of new event data failed, "
526 			    "select reg offset: %ld\n",
527 			    grp_p->regsel_p->regoff);
528 			goto done_pgm;
529 		}
530 	}
531 done_pgm:
532 	IOSPC_DBG1("iospc_perfcnt_program: returning status %d.\n", rval);
533 	return (rval);
534 }
535 
536 /*
537  * kstat update function. Handles reads/writes
538  * from/to kstat.
539  */
540 static int
iospc_kstat_update(kstat_t * ksp,int rw)541 iospc_kstat_update(kstat_t *ksp, int rw)
542 {
543 	struct kstat_named *data_p;
544 	int counter;
545 	iospc_ksinfo_t *ksinfop = ksp->ks_private;
546 	iospc_grp_t *grp_p = ksinfop->grp_p;
547 	iospc_t *iospc_p = ksinfop->iospc_p;
548 
549 	data_p = (struct kstat_named *)ksp->ks_data;
550 
551 	if (rw == KSTAT_WRITE) {
552 
553 		IOSPC_DBG2("iospc_kstat_update: wr %ld\n",
554 		    data_p[0].value.ui64);
555 
556 		/*
557 		 * Fields without programmable events won't be zeroed as
558 		 * iospc_perfcnt_program is what zeros them.
559 		 */
560 
561 		/* This group has programmable events. */
562 		if (grp_p->regsel_p->regoff != NO_REGISTER) {
563 
564 			IOSPC_DBG2("write: regoff has valid register\n");
565 			if (iospc_perfcnt_program(iospc_p, grp_p, ksinfop,
566 			    data_p[0].value.ui64) != SUCCESS)
567 				return (EIO);
568 		}
569 
570 	} else {	/* Read the event register and all of the counters. */
571 
572 		/* This group has programmable events. */
573 		if (grp_p->regsel_p->regoff != NO_REGISTER) {
574 
575 			IOSPC_DBG2("read: regoff has valid register\n");
576 
577 			if (grp_p->access(iospc_p, ksinfop->arg, IOSPC_REG_READ,
578 			    grp_p->regsel_p->regoff, &data_p[0].value.ui64)
579 			    != SUCCESS)
580 				return (EIO);
581 		} else
582 			data_p[0].value.ui64 = 0ull;
583 
584 		IOSPC_DBG2("iospc_kstat_update: rd event %lx\n",
585 		    data_p[0].value.ui64);
586 
587 		for (counter = 0; counter < grp_p->num_counters; counter++) {
588 
589 			if (grp_p->access(iospc_p, ksinfop->arg, IOSPC_REG_READ,
590 			    grp_p->counters_p[counter].regoff,
591 			    &data_p[counter + 1].value.ui64) != SUCCESS)
592 				return (EIO);
593 
594 			IOSPC_DBG2("cntr%d, off:0x%lx, val:0x%lx\n", counter,
595 			    grp_p->counters_p[counter].regoff,
596 			    data_p[counter + 1].value.ui64);
597 		}
598 	}
599 	return (SUCCESS);
600 }
601 
602 void
iospc_kstat_fini()603 iospc_kstat_fini()
604 {
605 	iospc_grp_t **grp_pp;
606 	iospc_grp_t *grp_p;
607 	int j;
608 
609 	IOSPC_DBG2("iospc_kstat_fini called\n");
610 
611 	for (j = 0, grp_pp = iospc_leaf_grps; *grp_pp != NULL; j++, grp_pp++) {
612 		grp_p = *grp_pp;
613 		if (grp_p->name_kstats_pp != NULL) {
614 			iospc_delete_name_kstats(grp_p->name_kstats_pp,
615 			    grp_p->num_counters);
616 			kmem_free(grp_p->name_kstats_pp,
617 			    grp_p->num_counters * sizeof (kstat_t));
618 			grp_p->name_kstats_pp = NULL;
619 		}
620 	}
621 }
622 
623 static void
iospc_delete_name_kstats(kstat_t ** name_kstats_pp,int num_kstats)624 iospc_delete_name_kstats(kstat_t **name_kstats_pp, int num_kstats)
625 {
626 	int i;
627 
628 	if (name_kstats_pp != NULL) {
629 		for (i = 0; i < num_kstats; i++) {
630 			if (name_kstats_pp[i] != NULL)
631 				kstat_delete(name_kstats_pp[i]);
632 		}
633 	}
634 }
635 
636 void
iospc_kstat_detach(iospc_t * iospc_p)637 iospc_kstat_detach(iospc_t *iospc_p)
638 {
639 	iospc_grp_t **grp_pp;
640 	iospc_grp_t *grp_p;
641 	int i;
642 
643 	IOSPC_DBG2("iospc_kstat_detach called\n");
644 
645 	for (i = 0, grp_pp = iospc_leaf_grps; *grp_pp != NULL; i++, grp_pp++) {
646 
647 		if (i >= IOSPC_MAX_NUM_GRPS)
648 			return;
649 
650 		grp_p = *grp_pp;
651 		if (iospc_p->iospc_ksinfo_p[i] != NULL) {
652 
653 			grp_p->access_fini(iospc_p, iospc_p->iospc_ksinfo_p[i]);
654 
655 			if (iospc_p->iospc_ksinfo_p[i]->cntr_ksp != NULL)
656 				kstat_delete(
657 				    iospc_p->iospc_ksinfo_p[i]->cntr_ksp);
658 			kmem_free(iospc_p->iospc_ksinfo_p[i],
659 			    sizeof (iospc_ksinfo_t));
660 		}
661 
662 	}
663 }
664