xref: /illumos-gate/usr/src/uts/common/io/cpqary3/cpqary3_noe.c (revision 6e6545bfaed3bab9ce836ee82d1abd8f2edba89a)
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 /*
17  * This  File  has  Modules  that  handle  the NOE  functionality  for
18  *	this driver.
19  *	It  builds and  submits  the NOE  command to  the adapter.  It also
20  *	processes a completed NOE command.
21  *	A study of the FirmWare specifications would be neccessary to relate
22  *	coding in this module to the hardware functionality.
23  */
24 
25 #include "cpqary3.h"
26 
27 /*
28  * Local Functions Definitions
29  */
30 
31 uint8_t cpqary3_disable_NOE_command(cpqary3_t *);
32 
33 /*
34  * Last reason a drive at this position was failed by the
35  * controller firmware (saved in the RIS).
36  */
37 
38 #define	MAX_KNOWN_FAILURE_REASON	31
39 
40 char *ascii_failure_reason[] = {
41 	"NONE",
42 	"TOO_SMALL_IN_LOAD_CONFIG",
43 	"ERROR_ERASING_RIS",
44 	"ERROR_SAVING_RIS",
45 	"FAIL_DRIVE_COMMAND",
46 	"MARK_BAD_FAILED",
47 	"MARK_BAD_FAILED_IN_FINISH_REMAP",
48 	"TIMEOUT",
49 	"AUTOSENSE_FAILED",
50 	"MEDIUM_ERROR_1",
51 	"MEDIUM_ERROR_2",
52 	"NOT_READY_BAD_SENSE",
53 	"NOT_READY",
54 	"HARDWARE_ERROR",
55 	"ABORTED_COMMAND",
56 	"WRITE_PROTECTED",
57 	"SPIN_UP_FAILURE_IN_RECOVER",
58 	"REBUILD_WRITE_ERROR",
59 	"TOO_SMALL_IN_HOT_PLUG",
60 	"RESET_RECOVERY_ABORT",
61 	"REMOVED_IN_HOT_PLUG",
62 	"INIT_REQUEST_SENSE_FAILED",
63 	"INIT_START_UNIT_FAILED",
64 	"GDP_INQUIRY_FAILED",
65 	"GDP_NON_DISK_DEVICE",
66 	"GDP_READ_CAPACITY_FAILED",
67 	"GDP_INVALID_BLOCK_SIZE",
68 	"HOTP_REQUEST_SENSE_FAILED",
69 	"HOTP_START_UNIT_FAILED",
70 	"WRITE_ERROR_AFTER_REMAP",
71 	"INIT_RESET_RECOVERY_ABORTED"
72 };
73 
74 /*
75  * All Possible Logical Volume Status
76  */
77 
78 char *log_vol_status[] = {
79 	"OK",
80 	"Failed",
81 	"Not Configured",
82 	"Regenerating",
83 	"Needs Rebuild Permission",
84 	"Rebuilding",
85 	"Wrong Drive Replaced",
86 	"Bad Drive Connection",
87 	"Box Overheating",
88 	"Box Overheated",
89 	"Volume Expanding",
90 	"Not Yet Available",
91 	"Volume Needs to Expand",
92 	"Unknown"
93 };
94 
95 /*
96  * Function	: 	cpqary3_send_NOE_command
97  * Description	: 	This routine builds and submits the NOE Command
98  *  			to the Controller.
99  * Called By	:   	cpqary3_attach(), cpqary3_NOE_handler()
100  * Parameters	: 	per-controller, per-command,
101  *  			Flag to signify first time or otherwise
102  * Calls	:   	cpqary3_alloc_phyctgs_mem(), cpqary3_cmdlist_occupy(),
103  *			cpqary3_submit(), cpqary3_add2submitted_cmdq(),
104  *			cpqary3_free_phyctgs_mem()
105  * Return Values: 	SUCCESS / FAILURE
106  *			[Shall fail only if memory allocation issues exist]
107  */
108 uint8_t
109 cpqary3_send_NOE_command(cpqary3_t *ctlr, cpqary3_cmdpvt_t *memp, uint8_t flag)
110 {
111 	uint32_t		phys_addr = 0;
112 	NoeBuffer 		*databuf;
113 	CommandList_t		*cmdlist;
114 	cpqary3_phyctg_t	*phys_handle;
115 	int			rv;
116 
117 	/*
118 	 * NOTE : DO NOT perform this operation for memp. Shall result in a
119 	 * failure of submission of the NOE command as it shall be NULL for
120 	 * the very first time
121 	 */
122 	RETURN_FAILURE_IF_NULL(ctlr);
123 
124 	/*
125 	 * Allocate Memory for Return data
126 	 * if failure, RETURN.
127 	 * Allocate Memory for CommandList
128 	 * If error, RETURN.
129 	 * get the Request Block from the CommandList
130 	 * Fill in the Request Packet with the corresponding values
131 	 * Special Information can be filled in the "bno" field of
132 	 * the request structure.
133 	 * Here, the "bno" field is filled for Asynchronous Mode.
134 	 * Submit the Command.
135 	 * If Failure, WARN and RETURN.
136 	 */
137 	if (CPQARY3_NOE_RESUBMIT == flag) {
138 		if ((NULL == memp) || (NULL == memp->cmdlist_memaddr)) {
139 			cmn_err(CE_WARN, " CPQary3 : _send_NOE_command : "
140 			    "Re-Use Not possible; CommandList NULL");
141 			return (CPQARY3_FAILURE);
142 		}
143 
144 		bzero(MEM2DRVPVT(memp)->sg, sizeof (NoeBuffer));
145 		memp->cmdlist_memaddr->Header.Tag.drvinfo_n_err =
146 		    CPQARY3_NOECMD_SUCCESS;
147 	} else if (CPQARY3_NOE_INIT == flag) {
148 		phys_handle =
149 		    (cpqary3_phyctg_t *)MEM_ZALLOC(sizeof (cpqary3_phyctg_t));
150 		if (!phys_handle)
151 			return (CPQARY3_FAILURE);
152 
153 		databuf = (NoeBuffer *)cpqary3_alloc_phyctgs_mem(ctlr,
154 		    sizeof (NoeBuffer), &phys_addr, phys_handle);
155 		if (!databuf) {
156 			return (CPQARY3_FAILURE);
157 		}
158 		bzero(databuf, sizeof (NoeBuffer));
159 
160 		if (NULL == (memp = cpqary3_cmdlist_occupy(ctlr))) {
161 			cpqary3_free_phyctgs_mem(phys_handle,
162 			    CPQARY3_FREE_PHYCTG_MEM);
163 			return (CPQARY3_FAILURE);
164 		}
165 
166 		memp->driverdata = (cpqary3_private_t *)
167 		    MEM_ZALLOC(sizeof (cpqary3_private_t));
168 		if (NULL == memp->driverdata) {
169 			cpqary3_free_phyctgs_mem(phys_handle,
170 			    CPQARY3_FREE_PHYCTG_MEM);
171 			cpqary3_cmdlist_release(memp, CPQARY3_HOLD_SW_MUTEX);
172 			return (CPQARY3_FAILURE);
173 		}
174 		memp->driverdata->sg = databuf;
175 		memp->driverdata->phyctgp = phys_handle;
176 
177 		cmdlist = memp->cmdlist_memaddr;
178 		cmdlist->Header.SGTotal = 1;
179 		cmdlist->Header.SGList = 1;
180 		cmdlist->Header.Tag.drvinfo_n_err = CPQARY3_NOECMD_SUCCESS;
181 		cmdlist->Header.LUN.PhysDev.Mode = PERIPHERIAL_DEV_ADDR;
182 
183 		cmdlist->Request.CDBLen = CISS_NOE_CDB_LEN;
184 		cmdlist->Request.Timeout = 0;
185 		cmdlist->Request.Type.Type = CISS_TYPE_CMD;
186 		cmdlist->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
187 		cmdlist->Request.Type.Direction = CISS_XFER_READ;
188 		cmdlist->Request.CDB[0] = CISS_NEW_READ;
189 		cmdlist->Request.CDB[1] = BMIC_NOTIFY_ON_EVENT;
190 		cmdlist->Request.CDB[10] = (NOE_BUFFER_LENGTH >> 8) & 0xff;
191 		cmdlist->Request.CDB[11] = NOE_BUFFER_LENGTH & 0xff;
192 
193 		cmdlist->SG[0].Addr = phys_addr;
194 		cmdlist->SG[0].Len = NOE_BUFFER_LENGTH;
195 	}
196 
197 	/* PERF */
198 
199 	memp->complete = cpqary3_noe_complete;
200 
201 	mutex_enter(&ctlr->hw_mutex);
202 	rv = cpqary3_submit(ctlr, memp->cmdlist_phyaddr);
203 	mutex_exit(&ctlr->hw_mutex);
204 
205 	if (rv != 0)
206 		return (CPQARY3_FAILURE);
207 
208 	/* PERF */
209 	return (CPQARY3_SUCCESS);
210 }
211 
212 /*
213  * Function	: 	cpqary3_disable_NOE_command
214  * Description	: 	This routine disables the Event Notifier
215  *			for the specified Controller.
216  * Called By	: 	cpqary3_cleanup()
217  * Parameters	: 	Per Controller Structure
218  * Calls	:   	cpqary3_cmdlist_occupy(), cpqary3_submit(),
219  *			cpqary3_add2submitted_cmdq()
220  * Return Values: 	SUCCESS / FAILURE
221  *			[Shall fail only if Memory Constraints exist]
222  */
223 uint8_t
224 cpqary3_disable_NOE_command(cpqary3_t *ctlr)
225 {
226 	CommandList_t		*cmdlist;
227 	cpqary3_cmdpvt_t	*memp;
228 	int			rv;
229 
230 	RETURN_FAILURE_IF_NULL(ctlr);
231 
232 	/*
233 	 * Allocate Memory for CommandList
234 	 * If error, RETURN.
235 	 * get the Request Block from the CommandList
236 	 * Fill in the Request Packet with the corresponding values
237 	 * Submit the Command.
238 	 * If Failure, WARN and RETURN.
239 	 */
240 
241 	if (NULL == (memp = cpqary3_cmdlist_occupy(ctlr))) {
242 		cmn_err(CE_WARN, "CPQary3 : _disable_NOE_command : Failed");
243 		return (CPQARY3_FAILURE);
244 	}
245 
246 	cmdlist = memp->cmdlist_memaddr;
247 	cmdlist->Header.Tag.drvinfo_n_err = CPQARY3_NOECMD_SUCCESS;
248 	cmdlist->Header.LUN.PhysDev.Mode = PERIPHERIAL_DEV_ADDR;
249 
250 	cmdlist->Request.CDBLen = CISS_CANCEL_NOE_CDB_LEN;
251 	cmdlist->Request.Timeout = 0;
252 	cmdlist->Request.Type.Type = CISS_TYPE_CMD;
253 	cmdlist->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
254 	cmdlist->Request.Type.Direction = CISS_XFER_NONE;
255 	cmdlist->Request.CDB[0] = ARRAY_WRITE;	/* 0x27 */
256 	cmdlist->Request.CDB[6] = BMIC_CANCEL_NOTIFY_ON_EVENT;
257 
258 	/* PERF */
259 
260 	memp->complete = cpqary3_noe_complete;
261 
262 	mutex_enter(&ctlr->hw_mutex);
263 	rv = cpqary3_submit(ctlr, memp->cmdlist_phyaddr);
264 	mutex_exit(&ctlr->hw_mutex);
265 
266 	if (rv != 0)
267 		return (CPQARY3_FAILURE);
268 
269 	/* PERF */
270 	return (CPQARY3_SUCCESS);
271 }
272 
273 /*
274  * Function	: 	cpqary3_NOE_handler
275  * Description	: 	This routine handles all those NOEs tabulated at the
276  *			begining of this code.
277  * Called By	: 	cpqary3_process_pkt()
278  * Parameters	: 	Pointer to the Command List
279  * Calls	:   	cpqary3_send_NOE_command(),
280  *			cpqary3_display_spare_status()
281  *			cpqary3_free_phyctgs_mem(), cpqary3_cmdlist_release()
282  * Return Values: 	None
283  */
284 void
285 cpqary3_NOE_handler(cpqary3_cmdpvt_t *memp)
286 {
287 	uint16_t		drive = 0;
288 	NoeBuffer 		*evt;
289 	cpqary3_t		*ctlr;
290 	cpqary3_phyctg_t	*phys_handle;
291 	uint8_t			driveId = 0;
292 
293 	/*
294 	 * This should never happen....
295 	 * If the pointer passed as argument is NULL, Panic the System.
296 	 */
297 	VERIFY(memp != NULL);
298 
299 	evt = (NoeBuffer *)MEM2DRVPVT(memp)->sg;
300 	ctlr = (cpqary3_t *)memp->ctlr;
301 	phys_handle = (cpqary3_phyctg_t *)MEM2DRVPVT(memp)->phyctgp;
302 
303 	/* Don't display more than 79 characters */
304 	evt->ascii_message[79] = 0;
305 
306 
307 	switch (evt->event_class_code) {
308 	case CLASS_PROTOCOL:
309 		/*
310 		 * the following cases are not handled:
311 		 * 000 	: This is for Synchronous NOE.
312 		 *	  CPQary3 follows asynchronous NOE.
313 		 * 002	: Asynchronous NOE time out.
314 		 *	  CPQary3 does not implement time
315 		 *	  outs for NOE. It shall always reside in the HBA.
316 		 */
317 
318 		cmn_err(CE_NOTE, " %s", ctlr->hba_name);
319 		if ((evt->event_subclass_code == SUB_CLASS_NON_EVENT) &&
320 		    (evt->event_detail_code == DETAIL_DISABLED)) {
321 			cmn_err(CE_CONT, " %s", ctlr->hba_name);
322 			cmn_err(CE_CONT,
323 			    "CPQary3 : Event Notifier Disabled \n");
324 			MEM_SFREE(memp->driverdata, sizeof (cpqary3_private_t));
325 			cpqary3_free_phyctgs_mem(phys_handle,
326 			    CPQARY3_FREE_PHYCTG_MEM);
327 			cpqary3_cmdlist_release(memp, CPQARY3_NO_MUTEX);
328 			return;
329 		} else if ((evt->event_subclass_code ==
330 		    SUB_CLASS_PROTOCOL_ERR) &&
331 		    (evt->event_detail_code == DETAIL_EVENT_Q_OVERFLOW)) {
332 			cmn_err(CE_CONT, " %s\n", evt->ascii_message);
333 		}
334 		cmn_err(CE_CONT, "\n");
335 		break;
336 
337 	case CLASS_HOT_PLUG:
338 		if (evt->event_subclass_code == SUB_CLASS_HP_CHANGE) {
339 			cmn_err(CE_NOTE, " %s", ctlr->hba_name);
340 			cmn_err(CE_CONT, " %s\n", evt->ascii_message);
341 
342 			/*
343 			 * Fix for QUIX 1000440284: Display the Physical
344 			 * Drive Num info only for CISS Controllers
345 			 */
346 
347 			if (!(ctlr->bddef->bd_flags & SA_BD_SAS)) {
348 				driveId =
349 				    /* LINTED: alignment */
350 				    *(uint16_t *)(&evt->event_specific_data[0]);
351 				if (driveId & 0x80) {
352 					driveId -= 0x80;
353 					cmn_err(CE_CONT, " Physical Drive Num "
354 					    "....... SCSI Port %u, "
355 					    "Drive Id %u\n",
356 					    (driveId / 16) + 1,
357 					    (driveId % 16));
358 				} else {
359 					cmn_err(CE_CONT, " Physical Drive Num "
360 					    "....... SCSI Port %u, "
361 					    "Drive Id %u\n",
362 					    (driveId / 16) + 1, (driveId % 16));
363 				}
364 			}
365 
366 			cmn_err(CE_CONT, " Configured Drive ? ....... %s\n",
367 			    evt->event_specific_data[2] ? "YES" : "NO");
368 			if (evt->event_specific_data[3]) {
369 				cmn_err(CE_CONT, " Spare Drive? "
370 				    "............. %s\n",
371 				    evt->event_specific_data[3] ? "YES" : "NO");
372 			}
373 		} else if (evt->event_subclass_code == SUB_CLASS_SB_HP_CHANGE) {
374 			if (evt->event_detail_code == DETAIL_PATH_REMOVED) {
375 				cmn_err(CE_WARN, " %s", ctlr->hba_name);
376 				cmn_err(CE_CONT,
377 				    " Storage Enclosure cable or %s\n",
378 				    evt->ascii_message);
379 			} else if (evt->event_detail_code ==
380 			    DETAIL_PATH_REPAIRED) {
381 				cmn_err(CE_NOTE, " %s", ctlr->hba_name);
382 				cmn_err(CE_CONT,
383 				    " Storage Enclosure Cable or %s\n",
384 				    evt->ascii_message);
385 			} else {
386 				cmn_err(CE_NOTE, " %s", ctlr->hba_name);
387 				cmn_err(CE_CONT, " %s\n", evt->ascii_message);
388 			}
389 		} else {
390 			cmn_err(CE_NOTE, " %s", ctlr->hba_name);
391 			cmn_err(CE_CONT, " %s\n", evt->ascii_message);
392 		}
393 
394 		cmn_err(CE_CONT, "\n");
395 		break;
396 
397 	case CLASS_HARDWARE:
398 	case CLASS_ENVIRONMENT:
399 		cmn_err(CE_NOTE, " %s", ctlr->hba_name);
400 		cmn_err(CE_CONT, " %s\n", evt->ascii_message);
401 		cmn_err(CE_CONT, "\n");
402 		break;
403 
404 	case CLASS_PHYSICAL_DRIVE:
405 		cmn_err(CE_WARN, " %s", ctlr->hba_name);
406 		cmn_err(CE_CONT, " %s\n", evt->ascii_message);
407 
408 		/*
409 		 * Fix for QUIX 1000440284: Display the Physical Drive
410 		 * Num info only for CISS Controllers
411 		 */
412 
413 		if (!(ctlr->bddef->bd_flags & SA_BD_SAS)) {
414 			/* LINTED: alignment */
415 			driveId = *(uint16_t *)(&evt->event_specific_data[0]);
416 			if (driveId & 0x80) {
417 				driveId -= 0x80;
418 				cmn_err(CE_CONT, " Physical Drive Num ....... "
419 				    "SCSI Port %u, Drive Id %u\n",
420 				    (driveId / 16) + 1, (driveId % 16));
421 			} else {
422 				cmn_err(CE_CONT, " Physical Drive Num ....... "
423 				    "SCSI Port %u, Drive Id %u\n",
424 				    (driveId / 16) + 1, (driveId % 16));
425 			}
426 		}
427 
428 		if (evt->event_specific_data[2] < MAX_KNOWN_FAILURE_REASON) {
429 			cmn_err(CE_CONT, " Failure Reason............ %s\n",
430 			    ascii_failure_reason[evt->event_specific_data[2]]);
431 		} else {
432 			cmn_err(CE_CONT,
433 			    " Failure Reason............ UNKNOWN \n");
434 		}
435 
436 		cmn_err(CE_CONT, "\n");
437 		break;
438 
439 	case CLASS_LOGICAL_DRIVE:
440 		cmn_err(CE_NOTE, " %s", ctlr->hba_name);
441 
442 		/*
443 		 * Fix for QXCR1000717274 - We are appending the logical
444 		 * voulme number by one to be in sync with logical volume
445 		 * details given by HPQacucli
446 		 */
447 
448 		if ((evt->event_subclass_code == SUB_CLASS_STATUS) &&
449 		    (evt->event_detail_code == DETAIL_CHANGE)) {
450 			cmn_err(CE_CONT, " State change, logical drive %u\n",
451 			    /* LINTED: alignment */
452 			    (*(uint16_t *)(&evt->event_specific_data[0]) + 1));
453 			cmn_err(CE_CONT, " New Logical Drive State... %s\n",
454 			    log_vol_status[evt->event_specific_data[3]]);
455 
456 			/*
457 			 * If the Logical drive has FAILED or it was
458 			 * NOT CONFIGURED, in the corresponding target
459 			 * structure, set flag as NONE to suggest that no
460 			 * target exists at this id.
461 			 */
462 
463 			if ((evt->event_specific_data[3] == 1) ||
464 			    (evt->event_specific_data[3] == 2)) {
465 				/* LINTED: alignment */
466 				drive =	*(uint16_t *)
467 				    (&evt->event_specific_data[0]);
468 				drive = ((drive < CTLR_SCSI_ID)
469 				    ? drive : drive + CPQARY3_TGT_ALIGNMENT);
470 				if (ctlr && ctlr->cpqary3_tgtp[drive]) {
471 					ctlr->cpqary3_tgtp[drive]->type =
472 					    CPQARY3_TARGET_NONE;
473 				}
474 			}
475 
476 			if (evt->event_specific_data[4] & SPARE_REBUILDING) {
477 				cmn_err(CE_CONT, " Logical Drive %d: "
478 				    "Data is rebuilding on spare drive\n",
479 				    /* LINTED: alignment */
480 				    (*(uint16_t *)
481 				    (&evt->event_specific_data[0]) + 1));
482 			}
483 
484 			if (evt->event_specific_data[4] & SPARE_REBUILT) {
485 				cmn_err(CE_CONT,
486 				    " Logical Drive %d: Rebuild complete. "
487 				    "Spare is now active\n",
488 				    /* LINTED: alignment */
489 				    (*(uint16_t *)
490 				    (&evt->event_specific_data[0]) + 1));
491 			}
492 		} else if ((evt->event_subclass_code == SUB_CLASS_STATUS) &&
493 		    (evt->event_detail_code == MEDIA_EXCHANGE)) {
494 			cmn_err(CE_CONT, " Media exchange detected, "
495 			    "logical drive %u\n",
496 			    /* LINTED: alignment */
497 			    (*(uint16_t *)
498 			    (&evt->event_specific_data[0]) + 1));
499 		} else {
500 			cmn_err(CE_CONT, " %s\n", evt->ascii_message);
501 		}
502 
503 		cmn_err(CE_CONT, "\n");
504 		break;
505 
506 	default:
507 		cmn_err(CE_NOTE, "%s", ctlr->hba_name);
508 		cmn_err(CE_CONT, " %s\n", evt->ascii_message);
509 		cmn_err(CE_CONT, "\n");
510 		break;
511 	}
512 
513 	/*
514 	 * Here, we reuse this command block to resubmit the NOE
515 	 * command.
516 	 * Ideally speaking, the resubmit should never fail
517 	 */
518 	if (CPQARY3_FAILURE ==
519 	    cpqary3_send_NOE_command(ctlr, memp, CPQARY3_NOE_RESUBMIT)) {
520 		cmn_err(CE_WARN, "CPQary3: Failed to ReInitialize "
521 		    "NOTIFY OF EVENT");
522 		cpqary3_free_phyctgs_mem(MEM2DRVPVT(memp)->phyctgp,
523 		    CPQARY3_FREE_PHYCTG_MEM);
524 		cpqary3_cmdlist_release(memp, CPQARY3_NO_MUTEX);
525 	}
526 }
527 
528 /* PERF */
529 /*
530  * Function	:      	cpqary3_noe_complete
531  * Description	:      	This routine processes the completed
532  *			NOE commands and
533  *			initiates any callback that is needed.
534  * Called By	:      	cpqary3_send_NOE_command,
535  *			cpqary3_disable_NOE_command
536  * Parameters	:      	per-command
537  * Calls	:      	cpqary3_NOE_handler, cpqary3_cmdlist_release
538  * Return Values:      	None
539  */
540 void
541 cpqary3_noe_complete(cpqary3_cmdpvt_t *cpqary3_cmdpvtp)
542 {
543 	ASSERT(cpqary3_cmdpvtp != NULL);
544 
545 	if (CPQARY3_TIMEOUT == cpqary3_cmdpvtp->cmdpvt_flag) {
546 		cpqary3_cmdlist_release(cpqary3_cmdpvtp, CPQARY3_NO_MUTEX);
547 		return;
548 	}
549 
550 	if (cpqary3_cmdpvtp->cmdlist_memaddr->Request.CDB[6] ==
551 	    BMIC_CANCEL_NOTIFY_ON_EVENT) {
552 		cv_signal(&cpqary3_cmdpvtp->ctlr->cv_noe_wait);
553 		cpqary3_cmdlist_release(cpqary3_cmdpvtp, CPQARY3_NO_MUTEX);
554 	} else {
555 		cpqary3_NOE_handler(cpqary3_cmdpvtp);
556 	}
557 }
558 
559 /* PERF */
560