summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun/io/dada/impl/dcd_transport.c
blob: 75a7a427acf7b438f34599b76fd9ee1a392c3b78 (plain)
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
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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) 1996,1999-2000 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Main Transport Routine for DADA.
 *
 */

#include <sys/dada/dada.h>
#include <sys/thread.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))


extern	kmutex_t	dcd_flag_nointr_mutex;
extern	kcondvar_t	dcd_flag_nointr_cv;

static void
dcd_flag_nointr_comp(struct dcd_pkt *pkt)
{

	mutex_enter(&dcd_flag_nointr_mutex);

	pkt->pkt_comp = NULL;

	/*
	 * 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 reset will again go to
	 * sleep.
	 */
	cv_broadcast(&dcd_flag_nointr_cv);
	mutex_exit(&dcd_flag_nointr_mutex);
}


int
dcd_transport(struct dcd_pkt *pkt)
{

	struct dcd_address *ap = P_TO_ADDR(pkt);
	extern int do_polled_io;
	int rval;

	/*
	 * Check if we are required to do polled I/O. We can
	 * get dcd_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) ||
			(pkt->pkt_flags & FLAG_FORCENOINTR)) {

		if (pkt->pkt_flags & FLAG_FORCENOINTR) {
			/*
			 * FLAG_FORCENOINTR means we do not want to rely on
			 * device interrupts. Set the FLAG_NOINTR
			 * so the command gets completed in polled mode.
			 */
			pkt->pkt_flags &= ~FLAG_FORCENOINTR;
			pkt->pkt_flags |= FLAG_NOINTR;
		}

		/*
		 * 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);

			/*
			 * Restore the pkt_completion routine
			 * and pkt flags and call the completion
			 * routine.
			 */
			pkt->pkt_comp = savec;
			pkt->pkt_flags = savef;
			(*pkt->pkt_comp)(pkt);
			return (rval);
		}
	} else {
		uint_t	savef;
		void	(*savec)();
		int	status;

		savef = pkt->pkt_flags;
		savec = pkt->pkt_comp;

		pkt->pkt_comp = dcd_flag_nointr_comp;
		pkt->pkt_flags &= ~FLAG_NOINTR;
		pkt->pkt_flags |= FLAG_IMMEDIATE_CB;

		if ((status = (*A_TO_TRAN(ap)->tran_start)(ap, pkt)) ==
			TRAN_ACCEPT) {
			mutex_enter(& dcd_flag_nointr_mutex);
			while (pkt->pkt_comp != NULL) {
				cv_wait(&dcd_flag_nointr_cv,
					&dcd_flag_nointr_mutex);
			}
			mutex_exit(&dcd_flag_nointr_mutex);
		}
		pkt->pkt_flags = savef;
		pkt->pkt_comp = savec;
		return (status);
	}
}