xref: /illumos-gate/usr/src/uts/common/io/cpqary3/cpqary3_util.c (revision faac71c002f7c7a98741f991b25937b24f309df0)
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 (C) 2013 Hewlett-Packard Development Company, L.P.
14  */
15 
16 #include <sys/sdt.h>
17 #include "cpqary3.h"
18 
19 /*
20  * Local Functions Definitions
21  */
22 
23 int cpqary3_target_geometry(struct scsi_address *);
24 int8_t cpqary3_detect_target_geometry(cpqary3_t *);
25 
26 /*
27  * Function	: 	cpqary3_read_conf_file
28  * Description	: 	This routine reads the driver configuration file.
29  * Called By	: 	cpqary3_attach()
30  * Parameters	: 	device-information pointer, per_controller
31  * Calls	: 	None
32  * Return Values: 	None
33  */
34 void
35 cpqary3_read_conf_file(dev_info_t *dip, cpqary3_t *cpqary3p)
36 {
37 	char		*ptr;
38 
39 	cpqary3p->noe_support = 0;
40 
41 	/*
42 	 * Plugin the code necessary to read from driver's conf file.
43 	 * As of now, we are not interested in reading the onf file
44 	 * for any purpose.
45 	 *
46 	 * eg. :
47 	 *
48 	 * retvalue = ddi_getprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS,
49 	 *	"cpqary3_online_debug", -1);
50 	 */
51 
52 	/*
53 	 *  We are calling ddi_prop_lookup_string
54 	 *  which gets the property value, which is passed at
55 	 *  the grub menu. If the user wants to use the older
56 	 *  target mapping algorithm,(prior to 1.80)at the grub menu
57 	 *  "cpqary3_tgtmap=off" should be entered. if this
58 	 *  string is entered, then we will set the
59 	 *  value of the variable legacy_mapping to one, which
60 	 *  will be used in
61 	 *  cpqary3_detect_target_geometry()
62 	 *  and cpqary3_probe4LVs(), to decide on the
63 	 *  mapping algorithm
64 	 */
65 
66 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0,
67 	    "cpqary3_tgtmap", &ptr) == DDI_PROP_SUCCESS) {
68 		if (strcmp("off", ptr) == 0) {
69 			cpqary3p->legacy_mapping = 1;
70 		}
71 		ddi_prop_free(ptr);
72 	}
73 
74 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0,
75 	    "cpqary3_noesupport", &ptr) == DDI_PROP_SUCCESS) {
76 		if (strcmp("on", ptr) == 0) {
77 			cpqary3p->noe_support = 1;
78 		}
79 		if (strcmp("off", ptr) == 0) {
80 			cpqary3p->noe_support = 0;
81 		}
82 		ddi_prop_free(ptr);
83 	}
84 }
85 
86 /*
87  * Function	: 	cpqary3_tick_hdlr
88  * Description	: 	This routine is called once in 60 seconds to detect any
89  *			command that is pending with the controller and has
90  *			timed out.
91  *			Once invoked, it re-initializes itself such that it is
92  *			invoked after an interval of 60 seconds.
93  * Called By	: 	kernel
94  * Parameters	: 	per_controller
95  * Calls	: 	None
96  * Return Values: 	None
97  */
98 void
99 cpqary3_tick_hdlr(void *arg)
100 {
101 	clock_t			cpqary3_lbolt;
102 	clock_t			cpqary3_ticks;
103 	cpqary3_t		*ctlr;
104 	cpqary3_pkt_t		*pktp;
105 	struct scsi_pkt		*scsi_pktp;
106 	cpqary3_cmdpvt_t	*local;
107 	volatile CfgTable_t	*ctp;
108 	uint32_t		i;
109 	uint32_t		no_cmds = 0;
110 
111 	/*
112 	 * The per-controller shall be passed as argument.
113 	 * Read the HeartBeat of the controller.
114 	 * if the current heartbeat is the same as the one recorded earlier,
115 	 * the f/w has locked up!!!
116 	 */
117 
118 	if (NULL == (ctlr = (cpqary3_t *)arg))
119 		return;
120 
121 	ctp = (CfgTable_t *)ctlr->ct;
122 
123 	/* CONTROLLER_LOCKUP */
124 	if (ctlr->heartbeat == DDI_GET32(ctlr, &ctp->HeartBeat)) {
125 		if (ctlr->lockup_logged == CPQARY3_FALSE) {
126 			cmn_err(CE_WARN, "CPQary3 : "
127 			    "%s HBA firmware Locked !!!", ctlr->hba_name);
128 			cmn_err(CE_WARN, "CPQary3 : "
129 			    "Please reboot the system");
130 			cpqary3_intr_onoff(ctlr, CPQARY3_INTR_DISABLE);
131 			if (ctlr->host_support & 0x4)
132 				cpqary3_lockup_intr_onoff(ctlr,
133 				    CPQARY3_LOCKUP_INTR_DISABLE);
134 			ctlr->controller_lockup = CPQARY3_TRUE;
135 			ctlr->lockup_logged = CPQARY3_TRUE;
136 		}
137 	}
138 	/* CONTROLLER_LOCKUP */
139 	no_cmds  = (uint32_t)((ctlr->ctlr_maxcmds / 3) *
140 	    NO_OF_CMDLIST_IN_A_BLK);
141 	mutex_enter(&ctlr->sw_mutex);
142 
143 	for (i = 0; i < no_cmds; i++) {
144 		local = &ctlr->cmdmemlistp->pool[i];
145 		ASSERT(local != NULL);
146 		pktp = MEM2PVTPKT(local);
147 
148 		if (!pktp)
149 			continue;
150 
151 		if ((local->cmdpvt_flag == CPQARY3_TIMEOUT) ||
152 		    (local->cmdpvt_flag == CPQARY3_RESET)) {
153 			continue;
154 		}
155 
156 		if (local->occupied == CPQARY3_OCCUPIED) {
157 			scsi_pktp = pktp->scsi_cmd_pkt;
158 			cpqary3_lbolt = ddi_get_lbolt();
159 			if ((scsi_pktp) && (scsi_pktp->pkt_time)) {
160 				cpqary3_ticks = cpqary3_lbolt -
161 				    pktp->cmd_start_time;
162 
163 				if ((drv_hztousec(cpqary3_ticks)/1000000) >
164 				    scsi_pktp->pkt_time) {
165 					scsi_pktp->pkt_reason = CMD_TIMEOUT;
166 					scsi_pktp->pkt_statistics =
167 					    STAT_TIMEOUT;
168 					scsi_pktp->pkt_state = STATE_GOT_BUS |
169 					    STATE_GOT_TARGET | STATE_SENT_CMD;
170 					local->cmdpvt_flag = CPQARY3_TIMEOUT;
171 
172 					/* This should always be the case */
173 					if (scsi_pktp->pkt_comp) {
174 						mutex_exit(&ctlr->sw_mutex);
175 						(*scsi_pktp->pkt_comp)
176 						    (scsi_pktp);
177 						mutex_enter(&ctlr->sw_mutex);
178 						continue;
179 					}
180 				}
181 			}
182 		}
183 	}
184 
185 	ctlr->heartbeat = DDI_GET32(ctlr, &ctp->HeartBeat);
186 	mutex_exit(&ctlr->sw_mutex);
187 	ctlr->tick_tmout_id = timeout(cpqary3_tick_hdlr,
188 	    (caddr_t)ctlr, drv_usectohz(CPQARY3_TICKTMOUT_VALUE));
189 }
190 
191 /*
192  * Function	: 	cpqary3_init_ctlr_resource
193  * Description	: 	This routine initializes the command list, initializes
194  *			the controller, enables the interrupt.
195  * Called By	: 	cpqary3_attach()
196  * Parameters	: 	per_controller
197  * Calls	: 	cpqary3_init_ctlr(), cpqary3_meminit(),
198  * 			cpqary3_intr_onoff(),
199  * Return Values: 	SUCCESS / FAILURE
200  *			[ Shall return failure if any of the mandatory
201  *			initializations / setup of resources fail ]
202  */
203 uint16_t
204 cpqary3_init_ctlr_resource(cpqary3_t *ctlr)
205 {
206 #ifdef CPQARY3_DEBUG_MEM
207 	int8_t i = 0;
208 #endif
209 
210 	/*
211 	 * Initialize the Controller
212 	 * Alocate Memory Pool for driver supported number of Commands
213 	 * return if not successful
214 	 * Allocate target structure for controller and initialize the same
215 	 * Detect all existing targets and allocate target structure for each
216 	 * Determine geometry for all existing targets
217 	 * Initialize the condition variables
218 	 */
219 
220 	RETURN_FAILURE_IF_NULL(ctlr);
221 
222 	if (CPQARY3_FAILURE == cpqary3_init_ctlr(ctlr))
223 		return ((CPQARY3_FAILURE));
224 
225 	if (CPQARY3_FAILURE == cpqary3_meminit(ctlr))
226 		return ((CPQARY3_FAILURE));
227 
228 
229 #ifdef CPQARY3_DEBUG_MEM
230 	/*
231 	 * This code is in place to test the memory management of this driver.
232 	 * This block of code allocates and de-allocates memory as many number
233 	 * of times as given in the for loop.
234 	 * After the for loop is executed, it returns a failure, which in turn
235 	 * would result in attach being failed.
236 	 */
237 	cmn_err(CE_CONT, "CPQary3 : _init_ctlr_resource : Testing memory \n");
238 	for (i = 0; i < 15; i++) {
239 		if (CPQARY3_SUCCESS != cpqary3_meminit(ctlr)) {
240 			cmn_err(CE_CONT, "CPQary3 : meminit failed : "
241 			    "attempt %d \n", i);
242 			return (CPQARY3_FAILURE);
243 		}
244 		cmn_err(CE_CONT,
245 		    "CPQary3 : INIT successful : attempt %d \n", i);
246 		cpqary3_memfini(ctlr, CPQARY3_MEMLIST_DONE |
247 		    CPQARY3_PHYCTGS_DONE | CPQARY3_CMDMEM_DONE);
248 		cmn_err(CE_CONT,
249 		    "CPQary3 : FINI successful : attempt %d \n", i);
250 	}
251 	return (CPQARY3_FAILURE);
252 #endif
253 
254 	ctlr->cpqary3_tgtp[CTLR_SCSI_ID] = MEM_ZALLOC(sizeof (cpqary3_tgt_t));
255 	if (!(ctlr->cpqary3_tgtp[CTLR_SCSI_ID])) {
256 		cmn_err(CE_WARN, "CPQary3: Target Initialization Failed");
257 		cpqary3_memfini(ctlr, CPQARY3_MEMLIST_DONE |
258 		    CPQARY3_PHYCTGS_DONE | CPQARY3_CMDMEM_DONE);
259 		return (CPQARY3_FAILURE);
260 	}
261 	ctlr->cpqary3_tgtp[CTLR_SCSI_ID]->type = CPQARY3_TARGET_CTLR;
262 
263 	cpqary3_intr_onoff(ctlr, CPQARY3_INTR_DISABLE);
264 
265 	/*
266 	 * Initialize all condition variables :
267 	 * for the immediate call back
268 	 * for the disable noe
269 	 * for fulsh cache
270 	 * for probe device
271 	 */
272 
273 	cv_init(&ctlr->cv_immediate_wait, NULL, CV_DRIVER, NULL);
274 	cv_init(&ctlr->cv_noe_wait, NULL, CV_DRIVER, NULL);
275 	cv_init(&ctlr->cv_flushcache_wait, NULL, CV_DRIVER, NULL);
276 	cv_init(&ctlr->cv_abort_wait, NULL, CV_DRIVER, NULL);
277 	cv_init(&ctlr->cv_ioctl_wait, NULL, CV_DRIVER, NULL);
278 
279 	return (CPQARY3_SUCCESS);
280 }
281 
282 /*
283  * Function	: 	cpqary3_target_geometry
284  * Description	: 	This function returns the geometry for the target.
285  * Called By	: 	cpqary3_getcap()
286  * Parameters	:	Target SCSI address
287  * Calls	:	None
288  * Return Values: 	Device Geometry
289  */
290 int
291 cpqary3_target_geometry(struct scsi_address *sa)
292 {
293 	cpqary3_t	*ctlr = SA2CTLR(sa);
294 	cpqary3_tgt_t	*tgtp = ctlr->cpqary3_tgtp[SA2TGT(sa)];
295 
296 	/*
297 	 * The target CHS are stored in the per-target structure
298 	 * during attach time. Use these values
299 	 */
300 	return ((tgtp->properties.drive.heads << 16) |
301 	    tgtp->properties.drive.sectors);
302 }
303 
304 /*
305  * Function	:   	cpqary3_synccmd_alloc
306  * Description	:   	This function allocates the DMA buffer for the commands
307  * Called By	:   	cpqary3_ioctl_send_bmiccmd(),
308  *			cpqary3_ioctl_send_scsicmd()
309  *			cpqary3_send_abortcmd(), cpqary3_flush_cache(),
310  *			cpqary3_probe4LVs(), cpqary3_probe4Tapes(),
311  *			cpqary3_detect_target_geometry()
312  * Parameters	:   	per_controller, buffer size
313  * Calls	:   	cpqary3_alloc_phyctgs_mem(), cpqary3_cmdlist_occupy()
314  * Return Values:   	memp
315  */
316 cpqary3_cmdpvt_t *
317 cpqary3_synccmd_alloc(cpqary3_t *cpqary3p, size_t bufsz)
318 {
319 	cpqary3_private_t	*cmddmah = NULL;
320 	uint32_t		dmabufpa = 0;	/* XXX 32-bit pa? */
321 	cpqary3_cmdpvt_t	*memp = NULL;
322 
323 	/*  first, allocate any necessary dma buffers  */
324 	if (bufsz > 0) {
325 		cpqary3_phyctg_t	*dmah = NULL;
326 		caddr_t			dmabufva = NULL;
327 
328 		/* first, allocate the command's dma handle */
329 		cmddmah = (cpqary3_private_t *)MEM_ZALLOC(sizeof (*cmddmah));
330 		if (cmddmah == NULL) {
331 			cmn_err(CE_WARN, "cpqary3_synccmd_alloc: "
332 			    "no memory for cmddmah");
333 			return (NULL);
334 		}
335 
336 		/* next, allocate dma handle */
337 		dmah = (cpqary3_phyctg_t *)MEM_ZALLOC(sizeof (*dmah));
338 		if (dmah == NULL) {
339 			MEM_SFREE(cmddmah, sizeof (*cmddmah));
340 			cmn_err(CE_WARN, "cpqary3_synccmd_alloc: "
341 			    "no memory for dmah");
342 			return (NULL);
343 		}
344 		/* now, allocate dma buffer */
345 		dmabufva = cpqary3_alloc_phyctgs_mem(cpqary3p, bufsz,
346 		    &dmabufpa, dmah);
347 		if (dmabufva == NULL) {
348 			MEM_SFREE(cmddmah, sizeof (*cmddmah));
349 			cmn_err(CE_WARN, "cpqary3_synccmd_alloc: "
350 			    "no memory for dma buf");
351 			return (NULL);
352 		}
353 		bzero(dmabufva, bufsz);
354 
355 		/* attach dma buffer to command dma handle */
356 		cmddmah->sg = dmabufva;
357 		cmddmah->phyctgp = dmah;
358 	}
359 
360 	/* next, allocate a command packet */
361 	memp = cpqary3_cmdlist_occupy(cpqary3p);
362 	if (memp == NULL) {
363 		if (cmddmah != NULL) {
364 			cpqary3_free_phyctgs_mem(cmddmah->phyctgp,
365 			    CPQARY3_FREE_PHYCTG_MEM);
366 			MEM_SFREE(cmddmah, sizeof (*cmddmah));
367 		}
368 		cmn_err(CE_WARN, "cpqary3_synccmd_alloc: "
369 		    "cannot get free command");
370 		return (NULL);
371 	}
372 	memp->cmdpvt_flag = 0;
373 	memp->cmdlist_memaddr->Header.Tag.drvinfo_n_err =
374 	    CPQARY3_SYNCCMD_SUCCESS;
375 
376 	/* attach dma resources to command */
377 	memp->driverdata = cmddmah;
378 	memp->cmdlist_memaddr->SG[0].Addr = dmabufpa;
379 	memp->cmdlist_memaddr->SG[0].Len  = (uint32_t)bufsz;
380 
381 	/* done */
382 	return (memp);
383 }
384 
385 /*
386  * Function	:   cpqary3_synccmd_cleanup
387  * Description	:   This routine cleans up the command
388  * Called By	:   cpqary3_process_pkt(), cpqary3_synccmd_free()
389  * Parameters	:   per_command_memory
390  * Calls	:   cpqary3_free_phyctgs_mem(), cpqary3_cmdlist_release()
391  * Return Values:   none
392  */
393 void
394 cpqary3_synccmd_cleanup(cpqary3_cmdpvt_t *memp)
395 {
396 	/*
397 	 * ordinary users should not call this routine
398 	 * (use cpqary3_synccmd_free() instead).  this is
399 	 * for use ONLY by cpqary3_synccmd_free() and
400 	 * cpqary3_process_pkt().
401 	 */
402 
403 	if (memp->driverdata != NULL) {
404 		/* free dma resources */
405 		cpqary3_free_phyctgs_mem(memp->driverdata->phyctgp,
406 		    CPQARY3_FREE_PHYCTG_MEM);
407 		MEM_SFREE(memp->driverdata, sizeof (cpqary3_private_t));
408 		memp->driverdata = NULL;
409 	}
410 	/* release command */
411 	memp->cmdpvt_flag = 0;
412 	cpqary3_cmdlist_release(memp, CPQARY3_HOLD_SW_MUTEX);
413 }
414 
415 /*
416  * Function	:   	cpqary3_synccmd_free
417  * Description	:   	This routine frees the command and the
418  *			associated resources.
419  * Called By	:   	cpqary3_ioctl_send_bmiccmd(),
420  *			cpqary3_ioctl_send_scsicmd()
421  *			cpqary3_send_abortcmd(), cpqary3_flush_cache(),
422  *			cpqary3_probe4LVs(), cpqary3_probe4Tapes(),
423  *			cpqary3_detect_target_geometry()
424  * Parameters	:   	per_controller, per_command_memory
425  * Calls	:   	cpqary3_synccmd_cleanup()
426  * Return Values:   	NONE
427  */
428 void
429 cpqary3_synccmd_free(cpqary3_t *cpqary3p, cpqary3_cmdpvt_t *memp)
430 {
431 	/*
432 	 * so, the user is done with this command packet.
433 	 * we have three possible scenarios here:
434 	 *
435 	 * 1) the command was never submitted to the controller
436 	 *
437 	 * or
438 	 *
439 	 * 2) the command has completed at the controller and has
440 	 *    been fully processed by the interrupt processing
441 	 *    mechanism and is no longer on the submitted or
442 	 *    retrieve queues.
443 	 *
444 	 * or
445 	 *
446 	 * 3) the command is not yet complete at the controller,
447 	 *    and/or hasn't made it through cpqary3_process_pkt()
448 	 *    yet.
449 	 *
450 	 * For cases (1) and (2), we can go ahead and free the
451 	 * command and the associated resources.  For case (3), we
452 	 * must mark the command as no longer needed, and let
453 	 * cpqary3_process_pkt() clean it up instead.
454 	 */
455 
456 	mutex_enter(&(cpqary3p->sw_mutex));
457 	if (memp->cmdpvt_flag == CPQARY3_SYNC_SUBMITTED) {
458 		/*
459 		 * command is still pending (case #3 above).
460 		 * mark the command as abandoned and let
461 		 * cpqary3_process_pkt() clean it up.
462 		 */
463 		memp->cmdpvt_flag = CPQARY3_SYNC_TIMEOUT;
464 		mutex_exit(&(cpqary3p->sw_mutex));
465 		return;
466 	}
467 	memp->cmdpvt_flag = 0;
468 	mutex_exit(&(cpqary3p->sw_mutex));
469 
470 	/*
471 	 * command was either never submitted or has completed
472 	 * (cases #1 and #2 above).  so, clean it up.
473 	 */
474 	cpqary3_synccmd_cleanup(memp);
475 
476 	/* done */
477 	return;
478 
479 }  /* cpqary3_synccmd_free() */
480 
481 /*
482  * Function	:   	cpqary3_synccmd_send
483  * Description	:   	This routine sends the command to the controller
484  * Called By	:	cpqary3_ioctl_send_bmiccmd(),
485  * 			cpqary3_ioctl_send_scsicmd()
486  * 			cpqary3_send_abortcmd(), cpqary3_flush_cache(),
487  * 			cpqary3_probe4LVs(), cpqary3_probe4Tapes(),
488  * 			cpqary3_detect_target_geometry()
489  * Parameters	:   	per_controller, per_command_memory, timeout value,
490  * 			flag(wait for reply)
491  * Calls	:   	cpqary3_submit(), cpqary3_add2submitted_cmdq()
492  * Return Values:   	SUCCESS / FAILURE
493  */
494 int
495 cpqary3_synccmd_send(cpqary3_t *cpqary3p, cpqary3_cmdpvt_t *memp,
496     clock_t timeoutms, int flags)
497 {
498 	clock_t		absto = 0;  /* absolute timeout */
499 	int		waitsig = 0;
500 	int		rc = 0;
501 	kcondvar_t	*cv = 0;
502 
503 	/*  compute absolute timeout, if necessary  */
504 	if (timeoutms > 0)
505 		absto = ddi_get_lbolt() + drv_usectohz(timeoutms * 1000);
506 
507 	/*  heed signals during wait?  */
508 	if (flags & CPQARY3_SYNCCMD_SEND_WAITSIG)
509 		waitsig = 1;
510 
511 	/*  acquire the sw mutex for our wait  */
512 	mutex_enter(&(cpqary3p->sw_mutex));
513 
514 	/*  submit command to controller  */
515 	mutex_enter(&(cpqary3p->hw_mutex));
516 
517 	memp->cmdpvt_flag = CPQARY3_SYNC_SUBMITTED;
518 	memp->cmdlist_memaddr->Header.Tag.drvinfo_n_err =
519 	    CPQARY3_SYNCCMD_SUCCESS;
520 	if (EIO == cpqary3_submit(cpqary3p, memp->cmdlist_phyaddr)) {
521 		mutex_exit(&(cpqary3p->hw_mutex));
522 		mutex_exit(&(cpqary3p->sw_mutex));
523 		rc = -1;
524 		return (rc);
525 	}
526 	mutex_exit(&(cpqary3p->hw_mutex));
527 
528 	/*  wait for command completion, timeout, or signal  */
529 	while (memp->cmdpvt_flag == CPQARY3_SYNC_SUBMITTED) {
530 		kmutex_t *mt = &(cpqary3p->sw_mutex);
531 
532 		cv = &(cpqary3p->cv_ioctl_wait);
533 		/*  wait with the request behavior  */
534 		if (absto) {
535 			clock_t   crc;
536 			if (waitsig) {
537 				crc = cv_timedwait_sig(cv, mt, absto);
538 			} else {
539 				crc = cv_timedwait(cv, mt, absto);
540 			}
541 			if (crc > 0)
542 				rc = 0;
543 			else
544 				rc = (-1);
545 		} else {
546 			if (waitsig) {
547 				rc = cv_wait_sig(cv, mt);
548 				if (rc > 0)
549 					rc = 0;
550 				else
551 					rc = (-1);
552 			} else {
553 				cv_wait(cv, mt);
554 				rc = 0;
555 			}
556 		}
557 
558 
559 		/*
560 		 * if our wait was interrupted (timeout),
561 		 * then break here
562 		 */
563 		if (rc) {
564 			break;
565 		}
566 	}
567 
568 	/* our wait is done, so release the sw mutex */
569 	mutex_exit(&(cpqary3p->sw_mutex));
570 
571 	/* return the results */
572 	return (rc);
573 }
574 
575 /*
576  * Function	: 	cpqary3_detect_target_geometry
577  * Description	: 	This function determines the geometry for all
578  *			the existing targets for the controller.
579  * Called By	:	cpqary3_tgt_init()
580  * Parameters	:	per controller
581  * Calls	:	cpqary3_synccmd_alloc(), cpqary3_synccmd_send()
582  *			cpqary3_synccmd_free()
583  * Return Values: 	SUCCESS / FAILURE
584  *			[ Shall return failure only if Memory constraints exist
585  *			or controller does not respond ]
586  */
587 int8_t
588 cpqary3_detect_target_geometry(cpqary3_t *ctlr)
589 {
590 	int			i;
591 	int8_t			ld_count = 0;
592 	int8_t			loop_cnt = 0;
593 	IdLogDrive		*idlogdrive;
594 	CommandList_t		*cmdlistp;
595 	cpqary3_cmdpvt_t	*cpqary3_cmdpvtp;
596 
597 	RETURN_FAILURE_IF_NULL(ctlr);
598 
599 	/*
600 	 * Occupy a Command List
601 	 * Allocate Memory for return data
602 	 * If error, RETURN 0.
603 	 * get the Request Block from the CommandList
604 	 * Fill in the Request Packet with the corresponding values
605 	 * Submit the Command and Poll for its completion
606 	 * If success, continue else RETURN 0
607 	 */
608 
609 	/* Sync Changes */
610 	cpqary3_cmdpvtp = cpqary3_synccmd_alloc(ctlr, sizeof (IdLogDrive));
611 	if (cpqary3_cmdpvtp == NULL)
612 		return (CPQARY3_FAILURE);
613 
614 	cmdlistp = cpqary3_cmdpvtp->cmdlist_memaddr;
615 	idlogdrive = (IdLogDrive *)cpqary3_cmdpvtp->driverdata->sg;
616 	/* Sync Changes */
617 
618 
619 	/* Update Cmd Header */
620 	cmdlistp->Header.SGList = 1;
621 	cmdlistp->Header.SGTotal = 1;
622 	cmdlistp->Header.Tag.drvinfo_n_err = CPQARY3_SYNCCMD_SUCCESS;
623 
624 	/* Cmd Reques */
625 	cmdlistp->Request.CDBLen = CPQARY3_CDBLEN_16;
626 	cmdlistp->Request.CDB[0] = 0x26;
627 	cmdlistp->Request.CDB[6] = BMIC_IDENTIFY_LOGICAL_DRIVE;
628 	cmdlistp->Request.CDB[7] = (sizeof (IdLogDrive) >> 8) & 0xff;
629 	cmdlistp->Request.CDB[8] = sizeof (IdLogDrive) & 0xff;
630 	cmdlistp->Request.Type.Type = CISS_TYPE_CMD;
631 	cmdlistp->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
632 	cmdlistp->Request.Type.Direction = CISS_XFER_READ;
633 
634 	/*
635 	 * For all the Targets that exist, issue an IDENTIFY LOGICAL DRIVE.
636 	 * That returns values which includes the dsired Geometry also.
637 	 * Update the Geometry in the per-target structure.
638 	 * NOTE : When the loop is executed for i=controller's SCSI ID, just
639 	 * increament by one so that we are talking to the next logical
640 	 * drive in our per-target structure.
641 	 */
642 
643 	/*
644 	 * Depending upon the value of the variable legacy_mapping
645 	 * set in cpqary3_attach(),
646 	 * the target mapping algorithm to be used by the driver is decided.
647 	 */
648 
649 	if (ctlr->legacy_mapping == 1) {
650 		loop_cnt = ((ctlr->num_of_targets > CTLR_SCSI_ID) ?
651 		    (ctlr->num_of_targets + 1) : (ctlr->num_of_targets));
652 
653 		for (i = 0; i < loop_cnt; i++) {
654 			if (i == CTLR_SCSI_ID)	/* Go to Next logical target */
655 				i++;
656 
657 			bzero(idlogdrive, sizeof (IdLogDrive));
658 			cmdlistp->Request.CDB[1] =
659 			    ctlr->cpqary3_tgtp[i]->logical_id;
660 
661 			/* Always zero */
662 			cmdlistp->Header.LUN.PhysDev.TargetId = 0;
663 
664 			/*
665 			 * Logical volume Id numbering scheme is as follows
666 			 * 0x00000, 0x00001, ... - for Direct Attached
667 			 * 0x10000, 0x10001, ... - If 1st Port of HBA is
668 			 * connected to  MSA20 / MSA500
669 			 * 0x20000, 0x20001, ... - If 2nd Port of HBA is
670 			 * connected to MSA20 / MSA500
671 			 */
672 			cmdlistp->Header.LUN.PhysDev.Bus =
673 			    (ctlr->cpqary3_tgtp[i]->logical_id) >> 16;
674 			cmdlistp->Header.LUN.PhysDev.Mode =
675 			    (cmdlistp->Header.LUN.PhysDev.Bus > 0) ?
676 			    MASK_PERIPHERIAL_DEV_ADDR : PERIPHERIAL_DEV_ADDR;
677 
678 			/*
679 			 * Submit the command
680 			 * Poll for its completion
681 			 * If polling is not successful, something is wrong
682 			 * with the controler
683 			 * Return FAILURE (No point in continuing if h/w is
684 			 * faulty !!!)
685 			 */
686 
687 			/* PERF */
688 			cpqary3_cmdpvtp->complete = cpqary3_synccmd_complete;
689 			/* PERF */
690 
691 			/* Sync Changes */
692 			if (cpqary3_synccmd_send(ctlr, cpqary3_cmdpvtp, 90000,
693 			    CPQARY3_SYNCCMD_SEND_WAITSIG) != 0) {
694 				/* Timed out */
695 				cpqary3_synccmd_free(ctlr, cpqary3_cmdpvtp);
696 				return (CPQARY3_FAILURE);
697 			}
698 			if ((cpqary3_cmdpvtp->
699 			    cmdlist_memaddr->Header.Tag.drvinfo_n_err ==
700 			    CPQARY3_SYNCCMD_FAILURE) &&
701 			    (cpqary3_cmdpvtp->errorinfop->CommandStatus != 2)) {
702 				DTRACE_PROBE1(id_logdrv_fail,
703 				    ErrorInfo_t *, cpqary3_cmdpvtp->errorinfop);
704 				cpqary3_synccmd_free(ctlr, cpqary3_cmdpvtp);
705 				return (CPQARY3_FAILURE);
706 			}
707 			/* Sync Changes */
708 
709 			ctlr->cpqary3_tgtp[i]->properties.drive.heads =
710 			    idlogdrive->heads;
711 			ctlr->cpqary3_tgtp[i]->properties.drive.sectors =
712 			    idlogdrive->sectors;
713 
714 			DTRACE_PROBE2(tgt_geometry_detect,
715 			    int, i, IdLogDrive *, idlogdrive);
716 		}
717 	} else {
718 
719 		/*
720 		 * Fix for QXCR1000446657: Logical drives are re numbered
721 		 * after deleting a Logical drive.
722 		 * introduced, new variable ld_count, which gets
723 		 * incremented when the Target ID is found.
724 		 * And for i=controller's SCSI ID and LDs with holes are found,
725 		 * we continue talking to
726 		 * the next logical drive in the per-target structure
727 		 */
728 
729 		for (i = 0; ld_count < ctlr->num_of_targets; i++) {
730 			if (i == CTLR_SCSI_ID ||
731 			    ctlr->cpqary3_tgtp[i] == NULL)
732 			/*  Go to the Next logical target  */
733 			continue;
734 			bzero(idlogdrive, sizeof (IdLogDrive));
735 			cmdlistp->Request.CDB[1] =
736 			    ctlr->cpqary3_tgtp[i]->logical_id;
737 			/* Always zero */
738 			cmdlistp->Header.LUN.PhysDev.TargetId = 0;
739 			/*
740 			 * Logical volume Id numbering scheme is as follows
741 			 * 0x00000, 0x00001, ... - for Direct Attached
742 			 * 0x10000, 0x10001, ... - If 1st Port of HBA is
743 			 * connected to  MSA20 / MSA500
744 			 * 0x20000, 0x20001, ... - If 2nd Port of HBA is
745 			 * connected to MSA20 / MSA500
746 			 */
747 			cmdlistp->Header.LUN.PhysDev.Bus =
748 			    (ctlr->cpqary3_tgtp[i]->logical_id) >> 16;
749 			cmdlistp->Header.LUN.PhysDev.Mode =
750 			    (cmdlistp->Header.LUN.PhysDev.Bus > 0) ?
751 			    MASK_PERIPHERIAL_DEV_ADDR :	PERIPHERIAL_DEV_ADDR;
752 			/* PERF */
753 			cpqary3_cmdpvtp->complete = cpqary3_synccmd_complete;
754 			/* PERF */
755 
756 			/*
757 			 * Submit the command
758 			 * Poll for its completion
759 			 * If polling is not successful, something is wrong
760 			 * with the controler
761 			 * Return FAILURE (No point in continuing if h/w is
762 			 * faulty !!!)
763 			 */
764 
765 			/* Sync Changes */
766 			if (cpqary3_synccmd_send(ctlr, cpqary3_cmdpvtp, 90000,
767 			    CPQARY3_SYNCCMD_SEND_WAITSIG) != 0) {
768 				/* Timed out */
769 				cpqary3_synccmd_free(ctlr, cpqary3_cmdpvtp);
770 				return (CPQARY3_FAILURE);
771 			}
772 			if ((cpqary3_cmdpvtp->
773 			    cmdlist_memaddr->Header.Tag.drvinfo_n_err ==
774 			    CPQARY3_SYNCCMD_FAILURE) &&
775 			    (cpqary3_cmdpvtp->errorinfop->CommandStatus != 2)) {
776 				DTRACE_PROBE1(id_logdrv_fail,
777 				    ErrorInfo_t *, cpqary3_cmdpvtp->errorinfop);
778 				cpqary3_synccmd_free(ctlr, cpqary3_cmdpvtp);
779 				return (CPQARY3_FAILURE);
780 			}
781 			/* Sync Changes */
782 
783 			ctlr->cpqary3_tgtp[i]->properties.drive.heads =
784 			    idlogdrive->heads;
785 			ctlr->cpqary3_tgtp[i]->properties.drive.sectors =
786 			    idlogdrive->sectors;
787 
788 			DTRACE_PROBE2(tgt_geometry_detect,
789 			    int, i, IdLogDrive *, idlogdrive);
790 
791 			ld_count++;
792 		}
793 	}
794 
795 	/* Sync Changes */
796 	cpqary3_synccmd_free(ctlr, cpqary3_cmdpvtp);
797 	/* Sync Changes */
798 
799 	return (CPQARY3_SUCCESS);
800 }
801