xref: /illumos-gate/usr/src/cmd/mdb/common/modules/smartpqi/smartpqi.c (revision 533affcbc7fc4d0c8132976ea454aaa715fe2307)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2023 Nexenta by DDN, Inc. All rights reserved.
14  */
15 
16 #include <sys/kmem.h>
17 #include <sys/proc.h>
18 #include <sys/time.h>
19 #include <sys/conf.h>
20 #include <sys/file.h>
21 #include <sys/ddi.h>
22 #include <sys/ddi_impldefs.h>
23 #include <sys/modctl.h>
24 #include <sys/sunddi.h>
25 #include <sys/scsi/scsi.h>
26 #include <sys/scsi/impl/scsi_reset_notify.h>
27 #include <sys/sunmdi.h>
28 #include <sys/mdi_impldefs.h>
29 
30 #include <smartpqi.h>
31 #include <smartpqi_hw.h>
32 
33 #include <sys/scsi/scsi_types.h>
34 #include <sys/disp.h>
35 #include <sys/types.h>
36 #include <sys/mdb_modapi.h>
37 
38 #define	INVALID_OPT_VAL ((uintptr_t)(-1))
39 
40 /* ---- Forward references ---- */
41 static int smartpqi(uintptr_t, uint_t, int, const mdb_arg_t *);
42 static void smartpqi_help(void);
43 
44 static const mdb_dcmd_t dcmds[] = {
45 	{
46 		"smartpqi", "-c <controller number> [-v]",
47 		"display smartpqi state",
48 		smartpqi,
49 		smartpqi_help
50 	},
51 	{ NULL }
52 };
53 
54 static const mdb_modinfo_t modinfo = {
55 	MDB_API_VERSION, dcmds, NULL
56 };
57 
58 static void smartpqi_help(void)
59 {
60 	mdb_printf("%s",
61 	    "-c <cntlr> display the state for <cntlr> and the no."
62 	    " of devices attached.\n"
63 	    "-v provide detailed information about each device attached.\n");
64 }
65 
66 char *
67 bool_to_str(int v)
68 {
69 	return (v ? "TRUE" : "FALSE");
70 }
71 
72 const mdb_modinfo_t *
73 _mdb_init(void)
74 {
75 	return (&modinfo);
76 }
77 
78 static void
79 display_sense_data(struct scsi_extended_sense data)
80 {
81 	mdb_printf("    SCSI sense data es_key 0x%x  ", data.es_key);
82 	mdb_printf("    es_add_code 0x%x  ", data.es_add_code);
83 	mdb_printf("    es_qual_code 0x%x\n", data.es_qual_code);
84 }
85 
86 static void
87 display_scsi_status(struct scsi_arq_status scsi_status)
88 {
89 	mdb_printf("    req pkt status\t\t\t0x%x\n",
90 	    *(int8_t *)&scsi_status.sts_rqpkt_status);
91 	mdb_printf("    req pkt resid\t\t\t0x%x\n",
92 	    scsi_status.sts_rqpkt_resid);
93 	mdb_printf("    req pkt state\t\t\t%d\n", scsi_status.sts_rqpkt_state);
94 	mdb_printf("    req pkt state\t\t\t%d\n",
95 	    scsi_status.sts_rqpkt_statistics);
96 	if (scsi_status.sts_status.sts_chk)
97 		display_sense_data(scsi_status.sts_sensedata);
98 }
99 
100 static char *
101 cmd_action_str(pqi_cmd_action_t action, char *tmpstr, int tmplen)
102 {
103 	switch (action) {
104 	case PQI_CMD_UNINIT:
105 		return ("UNINIT");
106 	case PQI_CMD_QUEUE:
107 		return ("QUEUE");
108 	case PQI_CMD_START:
109 		return ("START");
110 	case PQI_CMD_CMPLT:
111 		return ("COMPLETE");
112 	case PQI_CMD_TIMEOUT:
113 		return ("TIMEOUT");
114 	case PQI_CMD_FAIL:
115 		return ("FAIL");
116 	default:
117 		(void) mdb_snprintf(tmpstr, tmplen, "BAD ACTION <0x%x>",
118 		    action);
119 		return (tmpstr);
120 	}
121 }
122 
123 struct scsi_key_strings pqi_cmds[] = {
124 	SCSI_CMDS_KEY_STRINGS,
125 	BMIC_READ,	"BMIC Read",
126 	BMIC_WRITE,	"BMIC Write",
127 	CISS_REPORT_LOG,	"CISS Report Logical",
128 	CISS_REPORT_PHYS,	"CISS Report Physical",
129 	-1,	NULL
130 };
131 
132 static char *
133 mdb_cdb_to_str(uint8_t scsi_cmd, char *tmpstr, int tmplen)
134 {
135 	int	i = 0;
136 
137 	while (pqi_cmds[i].key != -1) {
138 		if (scsi_cmd == pqi_cmds[i].key)
139 			return ((char *)pqi_cmds[i].message);
140 		i++;
141 	}
142 	(void) mdb_snprintf(tmpstr, tmplen, "<undecoded cmd 0x%x>", scsi_cmd);
143 	return (tmpstr);
144 }
145 
146 static void
147 display_cdb(uint8_t *cdb)
148 {
149 	int	i, tmplen;
150 	char	tmpstr[64];
151 
152 	tmplen = sizeof (tmpstr);
153 	mdb_printf("CDB %s", mdb_cdb_to_str(cdb[0], tmpstr, tmplen));
154 	for (i = 1; i < SCSI_CDB_SIZE; i++)
155 		mdb_printf(":%02x", cdb[i]);
156 
157 	mdb_printf("\n");
158 }
159 
160 static char *
161 pqi_iu_type_to_str(int val)
162 {
163 	switch (val) {
164 	case PQI_RESPONSE_IU_RAID_PATH_IO_SUCCESS: return ("Success");
165 	case PQI_RESPONSE_IU_AIO_PATH_IO_SUCCESS: return ("AIO Success");
166 	case PQI_RESPONSE_IU_GENERAL_MANAGEMENT: return ("General");
167 	case PQI_RESPONSE_IU_TASK_MANAGEMENT: return ("Task");
168 	case PQI_RESPONSE_IU_RAID_PATH_IO_ERROR: return ("IO Error");
169 	case PQI_RESPONSE_IU_AIO_PATH_IO_ERROR: return ("AIO IO Error");
170 	case PQI_RESPONSE_IU_AIO_PATH_DISABLED: return ("AIO Path Disabled");
171 	default: return ("UNHANDLED");
172 	}
173 }
174 
175 static void
176 display_raid_error_info(uintptr_t error_info)
177 {
178 	struct pqi_raid_error_info info;
179 	int cnt;
180 
181 	if (error_info == 0)
182 		return;
183 	if ((cnt = mdb_vread((void *)&info, sizeof (struct pqi_raid_error_info),
184 	    (uintptr_t)error_info)) !=
185 	    sizeof (struct pqi_raid_error_info)) {
186 		mdb_warn(" Unable to read Raid error info(%d,%p)\n",
187 		    cnt, error_info);
188 		return;
189 	}
190 
191 	mdb_printf("    ---- Raid error info ----\n");
192 	mdb_printf("    data_in_result       %d\n", info.data_in_result);
193 	mdb_printf("    data_out_result      %d\n", info.data_out_result);
194 	mdb_printf("    status               %d\n", info.status);
195 	mdb_printf("    status_qualifier     %d\n", info.status_qualifier);
196 	mdb_printf("    sense_data_length    %d\n", info.sense_data_length);
197 	mdb_printf("    response_data_length %d\n", info.response_data_length);
198 	mdb_printf("    data_in_transferred  %d\n", info.data_in_transferred);
199 	mdb_printf("    data_out_transferred %d\n", info.data_out_transferred);
200 }
201 
202 static void
203 display_io_request(pqi_io_request_t *io)
204 {
205 	if (io == (pqi_io_request_t *)0)
206 		return;
207 
208 	mdb_printf("    ---- Command IO request ----\n");
209 	mdb_printf("    io_refcount\t\t\t\t%d\n", io->io_refcount);
210 	mdb_printf("    io_index\t\t\t\t%d\n", io->io_index);
211 	mdb_printf("    io_gen\t\t\t\t%d\n", io->io_gen);
212 	mdb_printf("    io_serviced\t\t\t\t%s\n", bool_to_str(io->io_serviced));
213 	mdb_printf("    io_raid_bypass\t\t\t%d\n", io->io_raid_bypass);
214 	mdb_printf("    io_status\t\t\t\t%d\n", io->io_status);
215 	mdb_printf("    io_iu\t\t\t\t0x%p\n", io->io_iu);
216 	mdb_printf("    io_pi\t\t\t\t%d\n", io->io_pi);
217 	mdb_printf("    io_iu_type\t\t\t\t%s\n",
218 	    pqi_iu_type_to_str(io->io_iu_type));
219 	display_raid_error_info((uintptr_t)io->io_error_info);
220 }
221 
222 static int
223 display_cmd(pqi_cmd_t *cmdp)
224 {
225 	int			read_cnt, tmplen;
226 	char			tmpstr[64];
227 	pqi_io_request_t	pqi_io;
228 
229 	tmplen = sizeof (tmpstr);
230 	display_cdb(cmdp->pc_cdb);
231 	mdb_printf("    cur action\t\t\t%s\n",
232 	    cmd_action_str(cmdp->pc_cur_action, tmpstr, tmplen));
233 	mdb_printf("    last action\t\t\t%s\n",
234 	    cmd_action_str(cmdp->pc_last_action, tmpstr, tmplen));
235 	display_scsi_status(cmdp->pc_cmd_scb);
236 	mdb_printf("    pc_dma_count\t\t\t%d\n", cmdp->pc_dma_count);
237 	mdb_printf("    pc_flags\t\t\t\t0x%x\n", cmdp->pc_flags);
238 	mdb_printf("    pc_statuslen\t\t\t%d\n", cmdp->pc_statuslen);
239 	mdb_printf("    pc_cmdlen\t\t\t\t%d\n", cmdp->pc_cmdlen);
240 
241 	if (cmdp->pc_io_rqst == (pqi_io_request_t *)0)
242 		return (DCMD_OK);
243 
244 	read_cnt = mdb_vread(&pqi_io, sizeof (pqi_io_request_t),
245 	    (uintptr_t)cmdp->pc_io_rqst);
246 	if (read_cnt == -1) {
247 		mdb_warn(" Error reading IO structure address 0x%p - "
248 		    "skipping diplay of IO commands\n",
249 		    cmdp->pc_io_rqst);
250 		return (DCMD_ERR);
251 	} else if (read_cnt != sizeof (pqi_io_request_t)) {
252 		mdb_warn(" cannot read IO structure count %d at0x%p - "
253 		    "skipping diplay of IO commands\n",
254 		    read_cnt, cmdp->pc_io_rqst);
255 		return (DCMD_ERR);
256 	} else {
257 		display_io_request(&pqi_io);
258 	}
259 	return (DCMD_OK);
260 }
261 
262 /*
263  * listp  - the pointer to the head of the linked list
264  * sz     - size of the lest element to be read
265  * current - pointer to current list_node structure in local storage
266  */
267 static list_node_t *
268 pqi_list_next(list_node_t *listp, size_t sz, void *structp,
269     list_node_t *current)
270 {
271 	int rval;
272 
273 	if (current->list_next == (list_node_t *)listp)
274 		return ((list_node_t *)NULL);
275 
276 	if (current->list_next == (list_node_t *)NULL)
277 		return ((list_node_t *)NULL);
278 
279 	if (current->list_next == current->list_prev)
280 		return ((list_node_t *)NULL);
281 
282 	rval = mdb_vread(structp, sz, (uintptr_t)current->list_next);
283 	if (rval == -1 || (size_t)rval != sz) {
284 		mdb_warn("Error reading a next list element so "
285 		    "skipping display of remaining elements\n");
286 		return ((list_node_t *)NULL);
287 	}
288 	return (current);
289 }
290 
291 static void
292 pqi_list_head(list_t list, uint8_t *drvp, size_t offset,
293     list_node_t **list_anchor)
294 {
295 	*list_anchor = (list_node_t *)(drvp +
296 	    offset + offsetof(list_t, list_head));
297 	if (*list_anchor == list.list_head.list_next) {
298 		*list_anchor = NULL;
299 	}
300 }
301 
302 static int
303 pqi_device_list_head(list_t s_devnodes, uint8_t *addr,
304     list_node_t **dev_head, struct pqi_device *dev)
305 {
306 	int rval;
307 
308 	pqi_list_head(s_devnodes, addr, offsetof(struct pqi_state, s_devnodes),
309 	    dev_head);
310 	if (*dev_head == NULL)
311 		return (DCMD_ERR);
312 
313 	rval = mdb_vread((void *)dev, sizeof (struct pqi_device),
314 	    (uintptr_t)s_devnodes.list_head.list_next);
315 	if (rval == -1) {
316 		mdb_warn(" cannot read device list head (0x%p)\n",
317 		    *dev_head);
318 		return (DCMD_ERR);
319 	}
320 	return (DCMD_OK);
321 }
322 
323 static int
324 pqi_cmd_list_head(list_t cmds, uint8_t *addr,
325     list_node_t **cmd_head, struct pqi_cmd *cmdp)
326 {
327 	int rval;
328 
329 	pqi_list_head(cmds, (uint8_t *)addr,
330 	    offsetof(struct pqi_device, pd_cmd_list),
331 	    cmd_head);
332 	/* Read in the first entry of the command list */
333 	rval = mdb_vread(cmdp, sizeof (struct pqi_cmd),
334 	    (uintptr_t)cmds.list_head.list_next);
335 	if (rval == -1) {
336 		mdb_warn(" cannot read initial entry in "
337 		    "command list (0x%p)\n", cmds.list_head.list_next);
338 	}
339 	return (rval);
340 }
341 
342 static char *
343 pqi_get_guid(char *pd_guid)
344 {
345 	static char myguid[41];
346 
347 	if (mdb_vread(myguid, sizeof (myguid) - 1, (uintptr_t)pd_guid) == -1)
348 		myguid[0] = '\0';
349 
350 	return (myguid);
351 }
352 
353 static void
354 display_device_info(pqi_device_t *dev)
355 {
356 	char str[40];
357 
358 	mdb_printf("-- Device pd_target %d --\n", dev->pd_target);
359 
360 	mdb_printf("pd_devtype\t\t\t\t%d\n", dev->pd_devtype);
361 	mdb_printf("device pd_flags\t\t\t\t0x%x\n", dev->pd_flags);
362 	mdb_printf("pd_active_cmds\t\t\t\t%d\n", dev->pd_active_cmds);
363 
364 	mdb_printf("pd_dip\t\t\t\t\t0x%p\n", dev->pd_dip);
365 	mdb_printf("pd_pip\t\t\t\t\t0x%p\n", dev->pd_pip);
366 	mdb_printf("pd_pip_offlined\t\t\t\t0x%p\n", dev->pd_pip_offlined);
367 
368 	mdb_printf("pd_online\t\t\t\t%s\n", bool_to_str(dev->pd_online));
369 	mdb_printf("pd_scanned\t\t\t\t%s\n", bool_to_str(dev->pd_scanned));
370 	mdb_printf("pd_phys_dev\t\t\t\t%s\n", bool_to_str(dev->pd_phys_dev));
371 	mdb_printf("pd_external_raid\t\t\t%s\n",
372 	    bool_to_str(dev->pd_external_raid));
373 	mdb_printf("pd_pd_aio_enabled\t\t\t%s\n",
374 	    bool_to_str(dev->pd_aio_enabled));
375 
376 	mdb_printf("GUID\t\t\t\t\t%s\n", pqi_get_guid(dev->pd_guid));
377 
378 	(void) strncpy(str, (char *)(dev->pd_vendor), sizeof (dev->pd_vendor));
379 	str[sizeof (dev->pd_vendor)] = '\0';
380 	mdb_printf("pd_vendor\t\t\t\t%s\n", str);
381 	(void) strncpy(str, (char *)(dev->pd_model), sizeof (dev->pd_model));
382 	str[sizeof (dev->pd_model)] = '\0';
383 	mdb_printf("pd_model\t\t\t\t%s\n", str);
384 }
385 
386 /*
387  * display device info: number of drives attached, number of commands running on
388  * each device, drive data and command data.
389  */
390 static void
391 pqi_display_devices(list_t s_devnodes, pqi_state_t *drvp, uint_t dev_verbose)
392 {
393 	int rval;
394 	int dev_count = 0;
395 	struct pqi_device d;
396 	pqi_device_t *next_dp;
397 	pqi_cmd_t *cmdp;
398 
399 	struct list_node *list_head;
400 	struct list_node *d_list_head;
401 	struct list_node *dev_current;
402 	struct list_node *cmd_current;
403 	struct pqi_device *d_drvrp; /* driver addr of device list entry */
404 
405 	mdb_printf("---- Devices for controller (0x%p) ----\n",
406 	    ((uint8_t *)drvp) +
407 	    offsetof(struct pqi_state, s_devnodes));
408 
409 	rval = pqi_device_list_head(s_devnodes,
410 	    (uint8_t *)drvp, &d_list_head, &d);
411 	if (d_list_head == NULL) {
412 		mdb_printf("Number of devices %d\n", dev_count);
413 		return;
414 	}
415 	cmdp = (pqi_cmd_t *)mdb_alloc(sizeof (struct pqi_cmd), UM_SLEEP|UM_GC);
416 
417 	next_dp = &d;
418 
419 	dev_current = (list_node_t *)((uint8_t *)(&d) +
420 	    offsetof(struct pqi_device, pd_list));
421 	d_drvrp = (pqi_device_t *)(s_devnodes.list_head.list_next);
422 	while (dev_current != NULL) {
423 		if (dev_verbose) {
424 			display_device_info((pqi_device_t *)&d);
425 
426 			/* now display command information */
427 			rval = pqi_cmd_list_head(d.pd_cmd_list,
428 			    (uint8_t *)d_drvrp, &list_head, cmdp);
429 			if (rval == -1) {
430 				mdb_warn("unable to read the command list head"
431 				    " for device %d\n", d.pd_target);
432 				list_head = NULL;
433 			}
434 			if (list_head != NULL) {
435 				mdb_printf("    ---- Commands for device %d"
436 				    " (0x%p) ----\n",
437 				    next_dp->pd_target, list_head);
438 
439 				cmd_current =
440 				    (list_node_t *)((uint8_t *)(cmdp) +
441 				    offsetof(struct pqi_cmd, pc_list));
442 
443 				while (cmd_current != NULL) {
444 					rval = display_cmd(cmdp);
445 					if (rval != DCMD_OK) {
446 						mdb_warn("Display of commands"
447 						    " aborted (%d)\n",
448 						    rval);
449 						break;
450 					}
451 
452 					cmd_current = pqi_list_next(list_head,
453 					    sizeof (struct pqi_cmd),
454 					    (void *)cmdp, cmd_current);
455 				}
456 			}
457 		}
458 		d_drvrp = (pqi_device_t *)(next_dp->pd_list.list_next);
459 		dev_current = pqi_list_next(
460 		    d_list_head,
461 		    sizeof (struct pqi_device),
462 		    (void*)next_dp,
463 		    dev_current);
464 		dev_count++;
465 	}
466 
467 	if (!dev_verbose)
468 		mdb_printf("Number of devices\t\t\t%d\n", dev_count);
469 }
470 
471 static void
472 pqi_display_instance(pqi_state_t *pqi_statep)
473 {
474 	mdb_printf("s_dip\t\t\t\t\t0x%p\n", pqi_statep->s_dip);
475 	mdb_printf("s_flags\t\t\t\t\t0x%x\n", pqi_statep->s_flags);
476 	mdb_printf("s_firmware_version\t\t\t%s\n",
477 	    pqi_statep->s_firmware_version);
478 
479 	mdb_printf("s_offline\t\t\t\t%s\ns_disable_mpxio\t\t\t\t%s\n",
480 	    bool_to_str(pqi_statep->s_offline),
481 	    bool_to_str(pqi_statep->s_disable_mpxio));
482 	mdb_printf("s_debug level\t\t\t\t%d\n", pqi_statep->s_debug_level);
483 
484 	mdb_printf("---- State for watchdog----\n");
485 	mdb_printf("s_intr_count\t\t\t\t%d\n", pqi_statep->s_intr_count);
486 	mdb_printf("s_last_intr_count\t\t\t%d\n",
487 	    pqi_statep->s_last_intr_count);
488 	mdb_printf("s_last_heartbeat_count\t\t\t%d\n",
489 	    pqi_statep->s_last_heartbeat_count);
490 
491 	mdb_printf("---- PQI cpabilities from controller ----\n");
492 	mdb_printf("s_max_inbound_queues\t\t\t%d\n",
493 	    pqi_statep->s_max_inbound_queues);
494 	mdb_printf("s_max_elements_per_iq\t\t\t%d\n",
495 	    pqi_statep->s_max_elements_per_iq);
496 	mdb_printf("s_max_iq_element_length\t\t\t%d\n",
497 	    pqi_statep->s_max_iq_element_length);
498 	mdb_printf("s_max_outbound_queues\t\t\t%d\n",
499 	    pqi_statep->s_max_outbound_queues);
500 	mdb_printf("s_max_elements_per_oq\t\t\t%d\n",
501 	    pqi_statep->s_max_elements_per_oq);
502 	mdb_printf("s_max_elements_per_oq\t\t\t%d\n",
503 	    pqi_statep->s_max_elements_per_oq);
504 	mdb_printf("s_max_oq_element_length\t\t\t%d\n",
505 	    pqi_statep->s_max_oq_element_length);
506 	mdb_printf("s_max_inbound_iu_length_per_firmware\t%d\n",
507 	    pqi_statep->s_max_inbound_iu_length_per_firmware);
508 	mdb_printf("s_max_inbound_queues\t\t\t%d\n",
509 	    pqi_statep->s_max_inbound_queues);
510 	mdb_printf("s_inbound_spanning_supported:\t\t%d\n",
511 	    pqi_statep->s_inbound_spanning_supported);
512 	mdb_printf("s_outbound_spanning_supported:\t\t%dk\n",
513 	    pqi_statep->s_outbound_spanning_supported);
514 	mdb_printf("s_outbound_spanning_supported:\t\t%d\n",
515 	    pqi_statep->s_outbound_spanning_supported);
516 	mdb_printf("s_pqi_mode_enabled:\t\t\t%d\n",
517 	    pqi_statep->s_pqi_mode_enabled);
518 	mdb_printf("s_cmd_queue_len\t\t\t\t%d\n", pqi_statep->s_cmd_queue_len);
519 
520 	mdb_printf("---- SIS capabilities from controller ----\n");
521 	mdb_printf("s_max_sg_entries\t\t\t%d\n", pqi_statep->s_max_sg_entries);
522 	mdb_printf("s_max_xfer_size\t\t\t\t%d\n", pqi_statep->s_max_xfer_size);
523 	mdb_printf("s_max_outstainding_requests\t\t%d\n",
524 	    pqi_statep->s_max_sg_entries);
525 
526 	mdb_printf("---- Computed values from config ----\n");
527 	mdb_printf("s_max_sg_per_iu\t\t\t\t%d\n", pqi_statep->s_max_sg_per_iu);
528 	mdb_printf("s_num_elements_per_iq\t\t\t%d\n",
529 	    pqi_statep->s_num_elements_per_iq);
530 	mdb_printf("s_num_elements_per_oq\t\t\t%d\n",
531 	    pqi_statep->s_num_elements_per_oq);
532 	mdb_printf("s_max_inbound_iu_length\t\t\t%d\n",
533 	    pqi_statep->s_max_inbound_iu_length);
534 	mdb_printf("s_num_queue_groups\t\t\t%d\n",
535 	    pqi_statep->s_num_queue_groups);
536 	mdb_printf("s_max_io_slots\t\t\t\t%d\n", pqi_statep->s_max_io_slots);
537 	mdb_printf("s_sg_chain_buf_length\t\t\t%d\n",
538 	    pqi_statep->s_sg_chain_buf_length);
539 	mdb_printf("s_max_sectors\t\t\t\t%d\n", pqi_statep->s_max_sectors);
540 
541 	mdb_printf("---- IO slot information ----\n");
542 	mdb_printf("s_io_rqst_pool\t\t\t\t0x%p\n", pqi_statep->s_io_rqst_pool);
543 	mdb_printf("s_io_wait_cnt\t\t\t\t%d\n", pqi_statep->s_io_wait_cnt);
544 	mdb_printf("s_next_io_slot\t\t\t\t%d\n", pqi_statep->s_next_io_slot);
545 	mdb_printf("s_io_need\t\t\t\t%d\n", pqi_statep->s_io_need);
546 	mdb_printf("s_io_had2wait\t\t\t\t%d\n", pqi_statep->s_io_had2wait);
547 	mdb_printf("s_io_sig\t\t\t\t%d\n", pqi_statep->s_io_sig);
548 }
549 
550 static int
551 pqi_getopts(uintptr_t addr, int argc, const mdb_arg_t *argv, uintptr_t *cntlr,
552     uint_t *print_devices)
553 {
554 	uintptr_t device = INVALID_OPT_VAL;
555 
556 	*cntlr = INVALID_OPT_VAL;
557 	*print_devices = FALSE;
558 	mdb_getopts(argc, argv,
559 	    'v', MDB_OPT_SETBITS, TRUE, print_devices,
560 	    'c', MDB_OPT_UINTPTR, (uintptr_t)cntlr,
561 	    'd', MDB_OPT_UINTPTR, (uintptr_t)&device,
562 	    NULL);
563 
564 	if (*cntlr == INVALID_OPT_VAL) {
565 		mdb_warn("-c <controller> required\n");
566 		return (DCMD_USAGE);
567 	}
568 
569 	return (DCMD_OK);
570 }
571 
572 static int
573 smartpqi(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
574 {
575 	int array_size;
576 	int rval;
577 	int pqi_statesz = sizeof (struct pqi_state);
578 	uintptr_t instance = INVALID_OPT_VAL;
579 	uintptr_t adr;
580 	void   **array_vaddr;
581 	void   **pqi_array;
582 	pqi_state_t *pqi_drvp;
583 	struct i_ddi_soft_state ss;
584 	pqi_state_t *pqi_statep;
585 	uint_t print_devices = 0;
586 
587 	if ((flags & DCMD_ADDRSPEC) == 0) {
588 		/*
589 		 * MDB has this peculiarity that addr can be non-null
590 		 * from a previous invocation:
591 		 * e.g. 0xfffffef49cd64000::smartpqi,
592 		 * but flags shows that
593 		 * the command line is ::spamrtpqi or ::smartpai <options>
594 		 * To make sure the desired command line options are
595 		 * honored, we set addr to 0 and proceed with evaluating
596 		 * these command as entered.
597 		 */
598 		addr = (uintptr_t)0;
599 	}
600 	rval = pqi_getopts(addr, argc, argv, &instance, &print_devices);
601 	if (rval != DCMD_OK) {
602 		return (rval);
603 	}
604 
605 	/* read the address of the pqi_state variable in the smartpqi driver */
606 	if (mdb_readvar((void *)&adr, "pqi_state") == -1) {
607 		mdb_warn("Cannot read pqi driver variable pqi_softstate.\n");
608 		return (DCMD_ERR);
609 	}
610 	/* now read the i_ddi_soft_state structure pointer */
611 	if (mdb_vread((void *)&ss, sizeof (ss), adr) != sizeof (ss)) {
612 		mdb_warn("Cannot read smartpqi softstate struct"
613 		    " pqi_state (Invalid pointer?(0x%p)).\n",
614 		    (uintptr_t)adr);
615 		return (DCMD_ERR);
616 	}
617 	/*
618 	 * now allocate space for the array containing the pqi_state
619 	 * pointers and read in this array
620 	 */
621 	array_size = ss.n_items * (sizeof (void*));
622 	array_vaddr = ss.array;
623 	pqi_array = (void **)mdb_alloc(array_size, UM_SLEEP|UM_GC);
624 	if (mdb_vread(pqi_array, array_size, (uintptr_t)array_vaddr) !=
625 	    array_size) {
626 		mdb_warn("Corrupted softstate struct\n");
627 		return (DCMD_ERR);
628 	}
629 
630 	if (instance >= ss.n_items || pqi_array[instance] == NULL) {
631 		mdb_warn("smartpqi - no information available for %d\n",
632 		    instance);
633 		return (DCMD_USAGE);
634 	}
635 
636 	pqi_statep = mdb_alloc(sizeof (struct pqi_state), UM_SLEEP|UM_GC);
637 	adr = (uintptr_t)pqi_array[instance];
638 
639 	pqi_drvp = (pqi_state_t *)adr;
640 	if (mdb_vread(pqi_statep, pqi_statesz, adr) != pqi_statesz) {
641 		mdb_warn("Cannot read pqi_state. adr 0x%p, size %d\n",
642 		    adr, pqi_statesz);
643 		return (DCMD_ERR);
644 	}
645 	mdb_printf("-------- Controller %d  pqi_state (0x%p) --------\n",
646 	    instance, adr);
647 	pqi_display_instance(pqi_statep);
648 
649 	pqi_display_devices(pqi_statep->s_devnodes, pqi_drvp, print_devices);
650 
651 	return (DCMD_OK);
652 }
653