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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Main Transport Routine for SCSA
30  */
31 #include <sys/scsi/scsi.h>
32 #include <sys/thread.h>
33 
34 #define	A_TO_TRAN(ap)	((ap)->a_hba_tran)
35 #define	P_TO_TRAN(pkt)	((pkt)->pkt_address.a_hba_tran)
36 #define	P_TO_ADDR(pkt)	(&((pkt)->pkt_address))
37 
38 #ifdef DEBUG
39 #define	SCSI_POLL_STAT
40 #endif
41 
42 #ifdef SCSI_POLL_STAT
43 int	scsi_poll_user;
44 int	scsi_poll_intr;
45 #endif
46 
47 extern	kmutex_t	scsi_flag_nointr_mutex;
48 extern	kcondvar_t	scsi_flag_nointr_cv;
49 
50 /*
51  * we used to set the callback_done value to NULL after the callback
52  * but this interfered with esp/fas drivers that also set the callback
53  * to NULL to prevent callbacks during error recovery
54  * to prevent confusion, create a truly unique value
55  */
56 static int scsi_callback_done;
57 #define	CALLBACK_DONE ((void (*)(struct scsi_pkt *))(&scsi_callback_done))
58 
59 static void
60 scsi_flag_nointr_comp(struct scsi_pkt *pkt)
61 {
62 	mutex_enter(&scsi_flag_nointr_mutex);
63 	pkt->pkt_comp = CALLBACK_DONE;
64 	/*
65 	 * We need cv_broadcast, because there can be more
66 	 * than one thread sleeping on the cv. We
67 	 * will wake all of them. The correct  one will
68 	 * continue and the rest will again go to sleep.
69 	 */
70 	cv_broadcast(&scsi_flag_nointr_cv);
71 	mutex_exit(&scsi_flag_nointr_mutex);
72 }
73 
74 static void
75 scsi_consistent_comp(struct scsi_pkt *pkt)
76 {
77 	struct scsi_pkt_cache_wrapper *pcw =
78 		(struct scsi_pkt_cache_wrapper *)pkt;
79 
80 	pkt->pkt_comp = pcw->pcw_orig_comp;
81 	scsi_sync_pkt(pkt);
82 	if (pkt->pkt_comp)
83 		(*pkt->pkt_comp)(pkt);
84 }
85 
86 /*
87  * A packet can have FLAG_NOINTR set because of target driver or
88  * scsi_poll(). If FLAG_NOINTR is set and we are in user context,
89  * we can avoid busy waiting in HBA by replacing the callback
90  * function with our own function and resetting FLAG_NOINTR. We
91  * can't do this in interrupt context because cv_wait will
92  * sleep with CPU priority raised high and in case of some failure,
93  * the CPU will be stuck in high priority.
94  */
95 
96 int
97 scsi_transport(struct scsi_pkt *pkt)
98 {
99 	struct scsi_address	*ap = P_TO_ADDR(pkt);
100 	extern int do_polled_io;
101 	int rval = TRAN_ACCEPT;
102 
103 	/* determine if we need to sync the data on the HBA's behalf */
104 	if ((pkt->pkt_dma_flags & DDI_DMA_CONSISTENT) &&
105 	    ((P_TO_TRAN(pkt)->tran_setup_pkt) != NULL)) {
106 		struct scsi_pkt_cache_wrapper *pcw =
107 			(struct scsi_pkt_cache_wrapper *)pkt;
108 
109 		_NOTE(SCHEME_PROTECTS_DATA("unique per pkt", \
110 			scsi_pkt_cache_wrapper::pcw_orig_comp));
111 
112 		pcw->pcw_orig_comp = pkt->pkt_comp;
113 		pkt->pkt_comp = scsi_consistent_comp;
114 	}
115 	/*
116 	 * Check if we are required to do polled I/O. We can
117 	 * get scsi_pkts that don't have the FLAG_NOINTR bit
118 	 * set in the pkt_flags. When do_polled_io is set
119 	 * we will probably be at a high IPL and not get any
120 	 * command completion interrupts. We force polled I/Os
121 	 * for such packets and do a callback of the completion
122 	 * routine ourselves.
123 	 */
124 	if (!do_polled_io && ((pkt->pkt_flags & FLAG_NOINTR) == 0)) {
125 		return (*A_TO_TRAN(ap)->tran_start)(ap, pkt);
126 	} else if ((curthread->t_flag & T_INTR_THREAD) || do_polled_io) {
127 #ifdef SCSI_POLL_STAT
128 		mutex_enter(&scsi_flag_nointr_mutex);
129 		scsi_poll_intr++;
130 		mutex_exit(&scsi_flag_nointr_mutex);
131 #endif
132 		/*
133 		 * If its an interrupt thread or we already have the
134 		 * the FLAG_NOINTR flag set, we go ahead and call the
135 		 * the hba's start routine directly. We force polling
136 		 * only if we have do_polled_io set and FLAG_NOINTR
137 		 * not set.
138 		 */
139 		if (!do_polled_io || (pkt->pkt_flags & FLAG_NOINTR)) {
140 			return ((*A_TO_TRAN(ap)->tran_start)(ap, pkt));
141 		} else {
142 			uint_t		savef;
143 			void		(*savec)();
144 			/*
145 			 * save the completion routine and pkt_flags
146 			 */
147 			savef = pkt->pkt_flags;
148 			savec = pkt->pkt_comp;
149 			pkt->pkt_flags |= FLAG_NOINTR;
150 			pkt->pkt_comp = 0;
151 
152 			rval = (*A_TO_TRAN(ap)->tran_start)(ap, pkt);
153 
154 			/* only continue of transport accepted request */
155 			if (rval == TRAN_ACCEPT) {
156 				/*
157 				 * Restore the pkt_completion routine
158 				 * and pkt flags and call the completion
159 				 * routine.
160 				 */
161 				pkt->pkt_comp = savec;
162 				pkt->pkt_flags = savef;
163 
164 				if (pkt->pkt_comp != NULL) {
165 					(*pkt->pkt_comp)(pkt);
166 				}
167 
168 				return (rval);
169 			}
170 
171 			/*
172 			 * rval was not TRAN_ACCEPT -- don't want command
173 			 * to be retried
174 			 */
175 			return (TRAN_FATAL_ERROR);
176 		}
177 	} else {
178 		uint_t	savef;
179 		void	(*savec)();
180 		int	status;
181 
182 #ifdef SCSI_POLL_STAT
183 		mutex_enter(&scsi_flag_nointr_mutex);
184 		scsi_poll_user++;
185 		mutex_exit(&scsi_flag_nointr_mutex);
186 #endif
187 		savef = pkt->pkt_flags;
188 		savec = pkt->pkt_comp;
189 
190 		pkt->pkt_comp = scsi_flag_nointr_comp;
191 		pkt->pkt_flags &= ~FLAG_NOINTR;
192 		pkt->pkt_flags |= FLAG_IMMEDIATE_CB;
193 
194 		if ((status = (*A_TO_TRAN(ap)->tran_start)(ap, pkt)) ==
195 			TRAN_ACCEPT) {
196 			mutex_enter(&scsi_flag_nointr_mutex);
197 			while (pkt->pkt_comp != CALLBACK_DONE) {
198 				cv_wait(&scsi_flag_nointr_cv,
199 					&scsi_flag_nointr_mutex);
200 			}
201 			mutex_exit(&scsi_flag_nointr_mutex);
202 		}
203 
204 		pkt->pkt_flags = savef;
205 		pkt->pkt_comp = savec;
206 		return (status);
207 	}
208 }
209