xref: /illumos-gate/usr/src/uts/common/io/1394/s1394_fcp.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * s1394_fcp.c
31  *    1394 Services Layer FCP Support Routines
32  */
33 
34 #include <sys/conf.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/cmn_err.h>
38 #include <sys/types.h>
39 #include <sys/kmem.h>
40 #include <sys/tnf_probe.h>
41 
42 #include <sys/1394/t1394.h>
43 #include <sys/1394/s1394.h>
44 #include <sys/1394/h1394.h>
45 
46 static int s1394_fcp_register_common(s1394_target_t *target,
47     t1394_fcp_evts_t *evts, s1394_fa_type_t type, s1394_fa_descr_t *descr);
48 static int s1394_fcp_unregister_common(s1394_target_t *target,
49     s1394_fa_type_t type);
50 static void s1394_fcp_resp_recv_write_request(cmd1394_cmd_t *req);
51 static void s1394_fcp_cmd_recv_write_request(cmd1394_cmd_t *req);
52 static void s1394_fcp_recv_write_request(cmd1394_cmd_t *req,
53     s1394_fa_type_t type);
54 static void s1394_fcp_recv_write_unclaimed(s1394_hal_t *hal,
55     cmd1394_cmd_t *req);
56 
57 
58 /*
59  * number of retries to notify registered targets in case target list
60  * changes while the list rwlock is dropped for the time of callback
61  */
62 uint_t s1394_fcp_notify_retry_cnt = 3;
63 
64 s1394_fa_descr_t s1394_fcp_ctl_descr = {
65 	IEC61883_FCP_RESP_ADDR,
66 	IEC61883_FCP_RESP_SIZE,
67 	T1394_ADDR_WRENBL,
68 	{ NULL, s1394_fcp_resp_recv_write_request, NULL },
69 	IEC61883_FCP_CMD_ADDR
70 };
71 
72 s1394_fa_descr_t s1394_fcp_tgt_descr = {
73 	IEC61883_FCP_CMD_ADDR,
74 	IEC61883_FCP_CMD_SIZE,
75 	T1394_ADDR_WRENBL,
76 	{ NULL, s1394_fcp_cmd_recv_write_request, NULL },
77 	IEC61883_FCP_RESP_ADDR
78 };
79 
80 
81 int
82 s1394_fcp_hal_init(s1394_hal_t *hal)
83 {
84 	int	ret = DDI_SUCCESS;
85 
86 	TNF_PROBE_0_DEBUG(s1394_fcp_hal_init_enter, S1394_TNF_SL_FCP_STACK, "");
87 
88 	if ((ddi_prop_exists(DDI_DEV_T_ANY, hal->halinfo.dip, DDI_PROP_DONTPASS,
89 	    "h1394-fcp-claim-on-demand")) == 0) {
90 		/* if not on-demand, claim addresses now */
91 		ret = s1394_fa_claim_addr(hal, S1394_FA_TYPE_FCP_CTL,
92 					&s1394_fcp_ctl_descr);
93 		if (ret == DDI_SUCCESS) {
94 			ret = s1394_fa_claim_addr(hal, S1394_FA_TYPE_FCP_TGT,
95 						&s1394_fcp_tgt_descr);
96 			if (ret != DDI_SUCCESS) {
97 				s1394_fa_free_addr(hal, S1394_FA_TYPE_FCP_CTL);
98 			}
99 		}
100 	}
101 
102 	TNF_PROBE_0_DEBUG(s1394_fcp_hal_init_exit, S1394_TNF_SL_FCP_STACK, "");
103 	return (ret);
104 }
105 
106 int
107 s1394_fcp_register_ctl(s1394_target_t *target, t1394_fcp_evts_t *evts)
108 {
109 	return (s1394_fcp_register_common(target, evts, S1394_FA_TYPE_FCP_CTL,
110 			&s1394_fcp_ctl_descr));
111 }
112 
113 int
114 s1394_fcp_register_tgt(s1394_target_t *target, t1394_fcp_evts_t *evts)
115 {
116 	return (s1394_fcp_register_common(target, evts, S1394_FA_TYPE_FCP_TGT,
117 			&s1394_fcp_tgt_descr));
118 }
119 
120 int
121 s1394_fcp_unregister_ctl(s1394_target_t *target)
122 {
123 	return (s1394_fcp_unregister_common(target, S1394_FA_TYPE_FCP_CTL));
124 }
125 
126 int
127 s1394_fcp_unregister_tgt(s1394_target_t *target)
128 {
129 	return (s1394_fcp_unregister_common(target, S1394_FA_TYPE_FCP_TGT));
130 }
131 
132 
133 static int
134 s1394_fcp_register_common(s1394_target_t *target, t1394_fcp_evts_t *evts,
135     s1394_fa_type_t type, s1394_fa_descr_t *descr)
136 {
137 	s1394_hal_t	*hal = target->on_hal;
138 	s1394_fcp_target_t *fcp;
139 
140 	rw_enter(&hal->target_list_rwlock, RW_WRITER);
141 
142 	if (s1394_fa_list_is_empty(hal, type)) {
143 		if (s1394_fa_claim_addr(hal, type, descr) != DDI_SUCCESS) {
144 			rw_exit(&hal->target_list_rwlock);
145 			return (DDI_FAILURE);
146 		}
147 	}
148 
149 	/* Add on the target list */
150 	s1394_fa_list_add(hal, target, type);
151 
152 	fcp = &target->target_fa[type].fat_u.fcp;
153 	fcp->fc_evts = *evts;
154 
155 	rw_exit(&hal->target_list_rwlock);
156 
157 	return (DDI_SUCCESS);
158 }
159 
160 static int
161 s1394_fcp_unregister_common(s1394_target_t *target, s1394_fa_type_t type)
162 {
163 	s1394_hal_t	*hal = target->on_hal;
164 	int		result;
165 
166 	rw_enter(&hal->target_list_rwlock, RW_WRITER);
167 
168 	result = s1394_fa_list_remove(hal, target, type);
169 	if (result == DDI_SUCCESS) {
170 		if (s1394_fa_list_is_empty(hal, type)) {
171 			s1394_fa_free_addr(hal, type);
172 		}
173 	} else {
174 		TNF_PROBE_0(s1394_fcp_unregister_common_error_list,
175 		    S1394_TNF_SL_FCP_ERROR, "");
176 	}
177 
178 	rw_exit(&hal->target_list_rwlock);
179 
180 	return (result);
181 }
182 
183 /*
184  * s1394_fcp_write_check_cmd()
185  *    Check if an FCP command is formed correctly;
186  *    set cmd_result and return DDI_FAILURE if not.
187  */
188 int
189 s1394_fcp_write_check_cmd(cmd1394_cmd_t *cmd)
190 {
191 	int	len;
192 
193 	/* 4-byte writes must be quadlet writes */
194 	if (cmd->cmd_type == CMD1394_ASYNCH_WR_BLOCK) {
195 		len = cmd->cmd_u.b.blk_length;
196 		if (len == 4) {
197 			cmd->cmd_result = CMD1394_ETYPE_ERROR;
198 			TNF_PROBE_0(t1394_write_error_type,
199 			    S1394_TNF_SL_FCP_ERROR, "");
200 			return (DDI_FAILURE);
201 		}
202 	} else {
203 		len = 4;
204 	}
205 
206 	/*
207 	 * request must be within FCP range. we avoid extra checks by
208 	 * using the fact that command and response are of the same size
209 	 */
210 	if ((cmd->cmd_addr & IEEE1394_ADDR_OFFSET_MASK) + len >
211 	    IEC61883_FCP_CMD_SIZE) {
212 		cmd->cmd_result = CMD1394_EADDRESS_ERROR;
213 		TNF_PROBE_0(t1394_write_error_addr, S1394_TNF_SL_FCP_ERROR, "");
214 		return (DDI_FAILURE);
215 	}
216 
217 	/* some options don't make sense for FCP commands */
218 	if (cmd->cmd_options & CMD1394_OVERRIDE_ADDR) {
219 		cmd->cmd_result = CMD1394_EINVALID_COMMAND;
220 		TNF_PROBE_0(t1394_write_error_opt, S1394_TNF_SL_FCP_ERROR, "");
221 		return (DDI_FAILURE);
222 	}
223 
224 	return (DDI_SUCCESS);
225 }
226 
227 static void
228 s1394_fcp_resp_recv_write_request(cmd1394_cmd_t *req)
229 {
230 	s1394_fcp_recv_write_request(req, S1394_FA_TYPE_FCP_CTL);
231 }
232 
233 static void
234 s1394_fcp_cmd_recv_write_request(cmd1394_cmd_t *req)
235 {
236 	s1394_fcp_recv_write_request(req, S1394_FA_TYPE_FCP_TGT);
237 }
238 
239 /*
240  * s1394_fcp_recv_write_request()
241  *    Common write request handler
242  */
243 static void
244 s1394_fcp_recv_write_request(cmd1394_cmd_t *req, s1394_fa_type_t type)
245 {
246 	s1394_hal_t	*hal = (s1394_hal_t *)req->cmd_callback_arg;
247 	s1394_target_t	*target;
248 	s1394_fa_target_t *fat;
249 	uint_t		saved_gen;
250 	int		num_retries = 0;
251 	int		(*cb)(cmd1394_cmd_t *req);
252 	boolean_t	restored = B_FALSE;
253 	int		ret = T1394_REQ_UNCLAIMED;
254 
255 	TNF_PROBE_0_DEBUG(s1394_fcp_recv_write_request_enter,
256 	    S1394_TNF_SL_FCP_STACK, "");
257 
258 	rw_enter(&hal->target_list_rwlock, RW_READER);
259 
260 start:
261 	target = hal->hal_fa[type].fal_head;
262 
263 	if (target) {
264 		s1394_fa_restore_cmd(hal, req);
265 		restored = B_TRUE;
266 
267 		/* Find a target that claims the request */
268 		do {
269 			fat = &target->target_fa[type];
270 
271 			cb = fat->fat_u.fcp.fc_evts.fcp_write_request;
272 			if (cb == NULL) {
273 				continue;
274 			}
275 			req->cmd_callback_arg = fat->fat_u.fcp.fc_evts.fcp_arg;
276 
277 			saved_gen = s1394_fa_list_gen(hal, type);
278 
279 			rw_exit(&hal->target_list_rwlock);
280 			ret = cb(req);
281 			rw_enter(&hal->target_list_rwlock, RW_READER);
282 
283 			if (ret == T1394_REQ_CLAIMED) {
284 				break;
285 			}
286 
287 			/*
288 			 * List could change while we dropped the lock. In such
289 			 * case, start all over again, because missing a write
290 			 * request can have more serious consequences for a
291 			 * target than receiving same request more than once
292 			 */
293 			if (saved_gen != s1394_fa_list_gen(hal, type)) {
294 				TNF_PROBE_2(s1394_fcp_recv_write_request_error,
295 				    S1394_TNF_SL_FCP_ERROR, "",
296 				    tnf_string, msg, "list gen changed",
297 				    tnf_opaque, num_retries, num_retries);
298 				num_retries++;
299 				if (num_retries <= s1394_fcp_notify_retry_cnt) {
300 					goto start;
301 				} else {
302 					break;
303 				}
304 			}
305 
306 			target = fat->fat_next;
307 		} while (target != NULL);
308 	}
309 
310 	rw_exit(&hal->target_list_rwlock);
311 
312 	if (ret != T1394_REQ_CLAIMED) {
313 		TNF_PROBE_0(s1394_fcp_recv_write_request_error_unclaimed,
314 		    S1394_TNF_SL_FCP_ERROR, "");
315 		if (restored) {
316 			s1394_fa_convert_cmd(hal, req);
317 		}
318 		s1394_fcp_recv_write_unclaimed(hal, req);
319 	}
320 
321 	TNF_PROBE_0_DEBUG(s1394_fcp_recv_write_request_exit,
322 	    S1394_TNF_SL_FCP_STACK, "");
323 }
324 
325 /*
326  * none of the targets claimed the request - send an appropriate response
327  */
328 static void
329 s1394_fcp_recv_write_unclaimed(s1394_hal_t *hal, cmd1394_cmd_t *req)
330 {
331 	req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
332 	(void) s1394_send_response(hal, req);
333 }
334