Home | History | Annotate | Download | only in impl
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * Main Transport Routine for SCSA
     28  */
     29 #include <sys/scsi/scsi.h>
     30 #include <sys/thread.h>
     31 #include <sys/bitmap.h>
     32 
     33 #define	A_TO_TRAN(ap)	((ap)->a_hba_tran)
     34 #define	P_TO_TRAN(pkt)	((pkt)->pkt_address.a_hba_tran)
     35 #define	P_TO_ADDR(pkt)	(&((pkt)->pkt_address))
     36 
     37 #ifdef DEBUG
     38 #define	SCSI_POLL_STAT
     39 #endif
     40 
     41 #ifdef SCSI_POLL_STAT
     42 int	scsi_poll_user;
     43 int	scsi_poll_intr;
     44 #endif
     45 
     46 int			scsi_pkt_bad_alloc_msg = 1;
     47 extern	ulong_t		*scsi_pkt_bad_alloc_bitmap;
     48 extern	kmutex_t	scsi_flag_nointr_mutex;
     49 extern	kcondvar_t	scsi_flag_nointr_cv;
     50 
     51 extern int		do_polled_io;
     52 
     53 /*
     54  * we used to set the callback_done value to NULL after the callback
     55  * but this interfered with esp/fas drivers that also set the callback
     56  * to NULL to prevent callbacks during error recovery
     57  * to prevent confusion, create a truly unique value.
     58  * The scsi_callback_done() function is used to detect a packet
     59  * completion being called a second time.
     60  */
     61 /* ARGSUSED */
     62 void
     63 scsi_callback_done(struct scsi_pkt *pkt)
     64 {
     65 	cmn_err(CE_PANIC,
     66 	    "%s: duplicate scsi_callback_done() on same scsi_pkt(9s)",
     67 	    mod_containing_pc(caller()));
     68 }
     69 
     70 #define	CALLBACK_DONE (scsi_callback_done)
     71 
     72 static void
     73 scsi_flag_nointr_comp(struct scsi_pkt *pkt)
     74 {
     75 	mutex_enter(&scsi_flag_nointr_mutex);
     76 	pkt->pkt_comp = CALLBACK_DONE;
     77 	/*
     78 	 * We need cv_broadcast, because there can be more
     79 	 * than one thread sleeping on the cv. We
     80 	 * will wake all of them. The correct  one will
     81 	 * continue and the rest will again go to sleep.
     82 	 */
     83 	cv_broadcast(&scsi_flag_nointr_cv);
     84 	mutex_exit(&scsi_flag_nointr_mutex);
     85 }
     86 
     87 /*
     88  * A packet can have FLAG_NOINTR set because of target driver or
     89  * scsi_poll(). If FLAG_NOINTR is set and we are in user context,
     90  * we can avoid busy waiting in HBA by replacing the callback
     91  * function with our own function and resetting FLAG_NOINTR. We
     92  * can't do this in interrupt context because cv_wait will
     93  * sleep with CPU priority raised high and in case of some failure,
     94  * the CPU will be stuck in high priority.
     95  */
     96 
     97 int
     98 scsi_transport(struct scsi_pkt *pkt)
     99 {
    100 	struct scsi_address	*ap = P_TO_ADDR(pkt);
    101 	int			rval = TRAN_ACCEPT;
    102 	major_t			major;
    103 
    104 	/*
    105 	 * The DDI does not allow drivers to allocate their own scsi_pkt(9S),
    106 	 * a driver can't have *any* compiled in dependencies on the
    107 	 * "sizeof (struct scsi_pkt)". While this has been the case for years,
    108 	 * many drivers have still not been fixed (or have regressed - tempted
    109 	 * by kmem_cache_alloc()).  The correct way to allocate a scsi_pkt
    110 	 * is by calling scsi_hba_pkt_alloc(9F), or by implementing the
    111 	 * tran_setup_pkt(9E) interfaces.
    112 	 *
    113 	 * The code below will identify drivers that violate this rule, and
    114 	 * print a message. The message will identify broken drivers, and
    115 	 * encourage getting these drivers fixed - after which this code
    116 	 * can be removed. Getting HBA drivers fixed is important because
    117 	 * broken drivers are an impediment to SCSA enhancement.
    118 	 *
    119 	 * We use the scsi_pkt_allocated_correctly() to determine if the
    120 	 * scsi_pkt we are about to start was correctly allocated. The
    121 	 * scsi_pkt_bad_alloc_bitmap is used to limit messages to one per
    122 	 * driver per reboot, and with non-debug code we only check the
    123 	 * first scsi_pkt.
    124 	 */
    125 	if (scsi_pkt_bad_alloc_msg) {
    126 		major = ddi_driver_major(P_TO_TRAN(pkt)->tran_hba_dip);
    127 		if (!BT_TEST(scsi_pkt_bad_alloc_bitmap, major) &&
    128 		    !scsi_pkt_allocated_correctly(pkt)) {
    129 			BT_SET(scsi_pkt_bad_alloc_bitmap, major);
    130 			cmn_err(CE_WARN, "%s: violates DDI scsi_pkt(9S) "
    131 			    "allocation rules",
    132 			    ddi_driver_name(P_TO_TRAN(pkt)->tran_hba_dip));
    133 		}
    134 #ifndef	DEBUG
    135 		/* On non-debug kernel, only check the first packet */
    136 		BT_SET(scsi_pkt_bad_alloc_bitmap, major);
    137 #endif	/* DEBUG */
    138 	}
    139 
    140 	/* Some retryed packets come with this flag not cleared */
    141 	pkt->pkt_flags &= ~FLAG_PKT_COMP_CALLED;
    142 
    143 	/*
    144 	 * Check if we are required to do polled I/O. We can
    145 	 * get scsi_pkts that don't have the FLAG_NOINTR bit
    146 	 * set in the pkt_flags. When do_polled_io is set
    147 	 * we will probably be at a high IPL and not get any
    148 	 * command completion interrupts. We force polled I/Os
    149 	 * for such packets and do a callback of the completion
    150 	 * routine ourselves.
    151 	 */
    152 	if (!do_polled_io && ((pkt->pkt_flags & FLAG_NOINTR) == 0)) {
    153 		return (*A_TO_TRAN(ap)->tran_start)(ap, pkt);
    154 	} else if ((curthread->t_flag & T_INTR_THREAD) || do_polled_io) {
    155 #ifdef SCSI_POLL_STAT
    156 		mutex_enter(&scsi_flag_nointr_mutex);
    157 		scsi_poll_intr++;
    158 		mutex_exit(&scsi_flag_nointr_mutex);
    159 #endif
    160 		/*
    161 		 * If its an interrupt thread or we already have the
    162 		 * the FLAG_NOINTR flag set, we go ahead and call the
    163 		 * the hba's start routine directly. We force polling
    164 		 * only if we have do_polled_io set and FLAG_NOINTR
    165 		 * not set.
    166 		 */
    167 		if (!do_polled_io || (pkt->pkt_flags & FLAG_NOINTR)) {
    168 			return ((*A_TO_TRAN(ap)->tran_start)(ap, pkt));
    169 		} else {
    170 			uint_t		savef;
    171 			void		(*savec)();
    172 			/*
    173 			 * save the completion routine and pkt_flags
    174 			 */
    175 			savef = pkt->pkt_flags;
    176 			savec = pkt->pkt_comp;
    177 			pkt->pkt_flags |= FLAG_NOINTR;
    178 			pkt->pkt_comp = 0;
    179 
    180 			rval = (*A_TO_TRAN(ap)->tran_start)(ap, pkt);
    181 
    182 			/* only continue of transport accepted request */
    183 			if (rval == TRAN_ACCEPT) {
    184 				/*
    185 				 * Restore the pkt_completion routine
    186 				 * and pkt flags and call the completion
    187 				 * routine.
    188 				 */
    189 				pkt->pkt_comp = savec;
    190 				pkt->pkt_flags = savef;
    191 				scsi_hba_pkt_comp(pkt);
    192 				return (rval);
    193 			}
    194 
    195 			/*
    196 			 * rval was not TRAN_ACCEPT -- don't want command
    197 			 * to be retried
    198 			 */
    199 			return (TRAN_FATAL_ERROR);
    200 		}
    201 	} else {
    202 		uint_t	savef;
    203 		void	(*savec)();
    204 
    205 #ifdef SCSI_POLL_STAT
    206 		mutex_enter(&scsi_flag_nointr_mutex);
    207 		scsi_poll_user++;
    208 		mutex_exit(&scsi_flag_nointr_mutex);
    209 #endif
    210 		savef = pkt->pkt_flags;
    211 		savec = pkt->pkt_comp;
    212 
    213 		pkt->pkt_comp = scsi_flag_nointr_comp;
    214 		pkt->pkt_flags &= ~FLAG_NOINTR;
    215 		pkt->pkt_flags |= FLAG_IMMEDIATE_CB;
    216 
    217 		if ((rval = (*A_TO_TRAN(ap)->tran_start)(ap, pkt)) ==
    218 		    TRAN_ACCEPT) {
    219 			mutex_enter(&scsi_flag_nointr_mutex);
    220 			while (pkt->pkt_comp != CALLBACK_DONE) {
    221 				cv_wait(&scsi_flag_nointr_cv,
    222 				    &scsi_flag_nointr_mutex);
    223 			}
    224 			mutex_exit(&scsi_flag_nointr_mutex);
    225 		}
    226 
    227 		pkt->pkt_flags = savef;
    228 		pkt->pkt_comp = savec;
    229 		return (rval);
    230 	}
    231 }
    232