1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
|
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Main Transport Routine for SCSA
*/
#include <sys/scsi/scsi.h>
#include <sys/thread.h>
#include <sys/bitmap.h>
#define A_TO_TRAN(ap) ((ap)->a_hba_tran)
#define P_TO_TRAN(pkt) ((pkt)->pkt_address.a_hba_tran)
#define P_TO_ADDR(pkt) (&((pkt)->pkt_address))
#ifdef DEBUG
#define SCSI_POLL_STAT
#endif
#ifdef SCSI_POLL_STAT
int scsi_poll_user;
int scsi_poll_intr;
#endif
int scsi_pkt_bad_alloc_msg = 1;
extern ulong_t *scsi_pkt_bad_alloc_bitmap;
extern kmutex_t scsi_flag_nointr_mutex;
extern kcondvar_t scsi_flag_nointr_cv;
extern int do_polled_io;
extern int scsi_pkt_allow_naca;
extern uchar_t scsi_cdb_size[];
#define NACA_IS_SET(cdb) \
(((cdb)[scsi_cdb_size[GETGROUP((union scsi_cdb *)(cdb))] - 1] \
& CDB_FLAG_NACA) ? 1 : 0)
/*
* we used to set the callback_done value to NULL after the callback
* but this interfered with esp/fas drivers that also set the callback
* to NULL to prevent callbacks during error recovery
* to prevent confusion, create a truly unique value.
* The scsi_callback_done() function is used to detect a packet
* completion being called a second time.
*/
/* ARGSUSED */
void
scsi_callback_done(struct scsi_pkt *pkt)
{
cmn_err(CE_PANIC,
"%s: duplicate scsi_callback_done() on same scsi_pkt(9s)",
mod_containing_pc(caller()));
}
#define CALLBACK_DONE (scsi_callback_done)
static void
scsi_flag_nointr_comp(struct scsi_pkt *pkt)
{
mutex_enter(&scsi_flag_nointr_mutex);
pkt->pkt_comp = CALLBACK_DONE;
/*
* We need cv_broadcast, because there can be more
* than one thread sleeping on the cv. We
* will wake all of them. The correct one will
* continue and the rest will again go to sleep.
*/
cv_broadcast(&scsi_flag_nointr_cv);
mutex_exit(&scsi_flag_nointr_mutex);
}
/*
* A packet can have FLAG_NOINTR set because of target driver or
* scsi_poll(). If FLAG_NOINTR is set and we are in user context,
* we can avoid busy waiting in HBA by replacing the callback
* function with our own function and resetting FLAG_NOINTR. We
* can't do this in interrupt context because cv_wait will
* sleep with CPU priority raised high and in case of some failure,
* the CPU will be stuck in high priority.
*/
int
scsi_transport(struct scsi_pkt *pkt)
{
struct scsi_address *ap = P_TO_ADDR(pkt);
int rval = TRAN_ACCEPT;
major_t major;
/*
* Add an assertion check for debugging as use of the NACA flag
* can cause problems. If an initiator sets it but does not clear
* it, other initiators would end up waiting indefinitely for the
* first to clear ACA.
*/
if (!scsi_pkt_allow_naca) {
ASSERT(!NACA_IS_SET(pkt->pkt_cdbp));
}
/*
* The DDI does not allow drivers to allocate their own scsi_pkt(9S),
* a driver can't have *any* compiled in dependencies on the
* "sizeof (struct scsi_pkt)". While this has been the case for years,
* many drivers have still not been fixed (or have regressed - tempted
* by kmem_cache_alloc()). The correct way to allocate a scsi_pkt
* is by calling scsi_hba_pkt_alloc(9F), or by implementing the
* tran_setup_pkt(9E) interfaces.
*
* The code below will identify drivers that violate this rule, and
* print a message. The message will identify broken drivers, and
* encourage getting these drivers fixed - after which this code
* can be removed. Getting HBA drivers fixed is important because
* broken drivers are an impediment to SCSA enhancement.
*
* We use the scsi_pkt_allocated_correctly() to determine if the
* scsi_pkt we are about to start was correctly allocated. The
* scsi_pkt_bad_alloc_bitmap is used to limit messages to one per
* driver per reboot, and with non-debug code we only check the
* first scsi_pkt.
*/
if (scsi_pkt_bad_alloc_msg) {
major = ddi_driver_major(P_TO_TRAN(pkt)->tran_hba_dip);
if (!BT_TEST(scsi_pkt_bad_alloc_bitmap, major) &&
!scsi_pkt_allocated_correctly(pkt)) {
BT_SET(scsi_pkt_bad_alloc_bitmap, major);
cmn_err(CE_WARN, "%s: violates DDI scsi_pkt(9S) "
"allocation rules",
ddi_driver_name(P_TO_TRAN(pkt)->tran_hba_dip));
}
#ifndef DEBUG
/* On non-debug kernel, only check the first packet */
BT_SET(scsi_pkt_bad_alloc_bitmap, major);
#endif /* DEBUG */
}
/* Some retryed packets come with this flag not cleared */
pkt->pkt_flags &= ~FLAG_PKT_COMP_CALLED;
/*
* Check if we are required to do polled I/O. We can
* get scsi_pkts that don't have the FLAG_NOINTR bit
* set in the pkt_flags. When do_polled_io is set
* we will probably be at a high IPL and not get any
* command completion interrupts. We force polled I/Os
* for such packets and do a callback of the completion
* routine ourselves.
*/
if (!do_polled_io && ((pkt->pkt_flags & FLAG_NOINTR) == 0)) {
return (*A_TO_TRAN(ap)->tran_start)(ap, pkt);
} else if ((curthread->t_flag & T_INTR_THREAD) || do_polled_io) {
#ifdef SCSI_POLL_STAT
mutex_enter(&scsi_flag_nointr_mutex);
scsi_poll_intr++;
mutex_exit(&scsi_flag_nointr_mutex);
#endif
/*
* If its an interrupt thread or we already have the
* the FLAG_NOINTR flag set, we go ahead and call the
* the hba's start routine directly. We force polling
* only if we have do_polled_io set and FLAG_NOINTR
* not set.
*/
if (!do_polled_io || (pkt->pkt_flags & FLAG_NOINTR)) {
return ((*A_TO_TRAN(ap)->tran_start)(ap, pkt));
} else {
uint_t savef;
void (*savec)();
/*
* save the completion routine and pkt_flags
*/
savef = pkt->pkt_flags;
savec = pkt->pkt_comp;
pkt->pkt_flags |= FLAG_NOINTR;
pkt->pkt_comp = 0;
rval = (*A_TO_TRAN(ap)->tran_start)(ap, pkt);
/* only continue of transport accepted request */
if (rval == TRAN_ACCEPT) {
/*
* Restore the pkt_completion routine
* and pkt flags and call the completion
* routine.
*/
pkt->pkt_comp = savec;
pkt->pkt_flags = savef;
scsi_hba_pkt_comp(pkt);
return (rval);
}
/*
* rval was not TRAN_ACCEPT -- don't want command
* to be retried
*/
return (TRAN_FATAL_ERROR);
}
} else {
uint_t savef;
void (*savec)();
#ifdef SCSI_POLL_STAT
mutex_enter(&scsi_flag_nointr_mutex);
scsi_poll_user++;
mutex_exit(&scsi_flag_nointr_mutex);
#endif
savef = pkt->pkt_flags;
savec = pkt->pkt_comp;
pkt->pkt_comp = scsi_flag_nointr_comp;
pkt->pkt_flags &= ~FLAG_NOINTR;
pkt->pkt_flags |= FLAG_IMMEDIATE_CB;
if ((rval = (*A_TO_TRAN(ap)->tran_start)(ap, pkt)) ==
TRAN_ACCEPT) {
mutex_enter(&scsi_flag_nointr_mutex);
while (pkt->pkt_comp != CALLBACK_DONE) {
cv_wait(&scsi_flag_nointr_cv,
&scsi_flag_nointr_mutex);
}
mutex_exit(&scsi_flag_nointr_mutex);
}
pkt->pkt_flags = savef;
pkt->pkt_comp = savec;
return (rval);
}
}
|