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