xref: /illumos-gate/usr/src/uts/common/io/comstar/port/pppt/pppt_msg.c (revision 134379c07d59b848341b71d3c4819af39ad347cc)
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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <sys/cpuvar.h>
26 #include <sys/types.h>
27 #include <sys/conf.h>
28 #include <sys/file.h>
29 #include <sys/ddi.h>
30 #include <sys/sunddi.h>
31 #include <sys/modctl.h>
32 #include <sys/sysmacros.h>
33 
34 #include <sys/socket.h>
35 #include <sys/strsubr.h>
36 #include <sys/door.h>
37 
38 #include <sys/stmf.h>
39 #include <sys/stmf_ioctl.h>
40 #include <sys/portif.h>
41 #include <pppt.h>
42 
43 static void pppt_msg_tgt_register(stmf_ic_msg_t *reg_port);
44 
45 static void pppt_msg_tgt_deregister(stmf_ic_msg_t *msg);
46 
47 static void pppt_msg_session_destroy(stmf_ic_msg_t *msg);
48 
49 static void pppt_msg_scsi_cmd(stmf_ic_msg_t *msg);
50 
51 static void pppt_msg_data_xfer_done(stmf_ic_msg_t *msg);
52 
53 static void pppt_msg_handle_status(stmf_ic_msg_t *msg);
54 
55 void
56 pppt_msg_rx(stmf_ic_msg_t *msg)
57 {
58 	switch (msg->icm_msg_type) {
59 	case STMF_ICM_REGISTER_PROXY_PORT:
60 		pppt_msg_tgt_register(msg);
61 		break;
62 	case STMF_ICM_DEREGISTER_PROXY_PORT:
63 		pppt_msg_tgt_deregister(msg);
64 		break;
65 	case STMF_ICM_SESSION_CREATE:
66 		pppt_msg_tx_status(msg, STMF_NOT_SUPPORTED);
67 		stmf_ic_msg_free(msg);
68 		break;
69 	case STMF_ICM_SESSION_DESTROY:
70 		pppt_msg_session_destroy(msg);
71 		break;
72 	case STMF_ICM_SCSI_CMD:
73 		pppt_msg_scsi_cmd(msg);
74 		break;
75 	case STMF_ICM_SCSI_DATA_XFER_DONE:
76 		pppt_msg_data_xfer_done(msg);
77 		break;
78 	case STMF_ICM_SCSI_DATA:
79 		/* Ignore, all proxy data will be immediate for now */
80 		pppt_msg_tx_status(msg, STMF_NOT_SUPPORTED);
81 		stmf_ic_msg_free(msg);
82 		break;
83 	case STMF_ICM_STATUS:
84 		pppt_msg_handle_status(msg);
85 		break;
86 	default:
87 		/* Other message types are not allowed */
88 		ASSERT(0);
89 		break;
90 	}
91 }
92 
93 void
94 pppt_msg_tx_status(stmf_ic_msg_t *orig_msg, stmf_status_t status)
95 {
96 	stmf_ic_msg_t	*msg;
97 
98 	/*
99 	 * If TX of status fails it should be treated the same as a loss of
100 	 * connection.  We expect the remote node to handle it.
101 	 */
102 	msg = stmf_ic_status_msg_alloc(status, orig_msg->icm_msg_type,
103 	    orig_msg->icm_msgid);
104 
105 	if (msg != NULL) {
106 		(void) stmf_ic_tx_msg(msg);
107 	}
108 }
109 
110 static void
111 pppt_msg_tgt_register(stmf_ic_msg_t *msg)
112 {
113 	stmf_ic_reg_port_msg_t	*reg_port;
114 	pppt_tgt_t		*result;
115 	stmf_status_t		stmf_status;
116 
117 	reg_port = msg->icm_msg;
118 
119 
120 	PPPT_GLOBAL_LOCK();
121 	if (pppt_global.global_svc_state != PSS_ENABLED) {
122 		stmf_status = STMF_FAILURE;
123 		PPPT_INC_STAT(es_tgt_reg_svc_disabled);
124 		goto pppt_register_tgt_done;
125 	}
126 
127 	/*
128 	 * For now we assume that the marshall/unmarshall code is responsible
129 	 * for validating the message length and ensuring the resulting
130 	 * request structure is self consistent.  Make sure this
131 	 * target doesn't already exist.
132 	 */
133 	if ((result = pppt_tgt_lookup_locked(reg_port->icrp_port_id)) != NULL) {
134 		stmf_status = STMF_ALREADY;
135 		PPPT_INC_STAT(es_tgt_reg_duplicate);
136 		goto pppt_register_tgt_done;
137 	}
138 
139 	result = pppt_tgt_create(reg_port, &stmf_status);
140 
141 	if (result == NULL) {
142 		stmf_status = STMF_TARGET_FAILURE;
143 		PPPT_INC_STAT(es_tgt_reg_create_fail);
144 		goto pppt_register_tgt_done;
145 	}
146 
147 	avl_add(&pppt_global.global_target_list, result);
148 
149 	stmf_status = STMF_SUCCESS;
150 
151 pppt_register_tgt_done:
152 	PPPT_GLOBAL_UNLOCK();
153 	pppt_msg_tx_status(msg, stmf_status);
154 	stmf_ic_msg_free(msg);
155 }
156 
157 static void
158 pppt_msg_tgt_deregister(stmf_ic_msg_t *msg)
159 {
160 	stmf_ic_dereg_port_msg_t	*dereg_port;
161 	stmf_status_t			stmf_status;
162 	pppt_tgt_t			*tgt;
163 
164 	PPPT_GLOBAL_LOCK();
165 	if (pppt_global.global_svc_state != PSS_ENABLED) {
166 		PPPT_GLOBAL_UNLOCK();
167 		stmf_status = STMF_FAILURE;
168 		PPPT_INC_STAT(es_tgt_dereg_svc_disabled);
169 		goto pppt_deregister_tgt_done;
170 	}
171 
172 	dereg_port = msg->icm_msg;
173 
174 	/* Lookup target */
175 	if ((tgt = pppt_tgt_lookup_locked(dereg_port->icdp_port_id)) == NULL) {
176 		PPPT_GLOBAL_UNLOCK();
177 		stmf_status = STMF_NOT_FOUND;
178 		PPPT_INC_STAT(es_tgt_dereg_not_found);
179 		goto pppt_deregister_tgt_done;
180 	}
181 	avl_remove(&pppt_global.global_target_list, tgt);
182 	pppt_tgt_async_delete(tgt);
183 
184 	PPPT_GLOBAL_UNLOCK();
185 
186 	/* Wait for delete to complete */
187 	mutex_enter(&tgt->target_mutex);
188 	while ((tgt->target_refcount > 0) ||
189 	    (tgt->target_state != TS_DELETING)) {
190 		cv_wait(&tgt->target_cv, &tgt->target_mutex);
191 	}
192 	mutex_exit(&tgt->target_mutex);
193 
194 	pppt_tgt_destroy(tgt);
195 	stmf_status = STMF_SUCCESS;
196 
197 pppt_deregister_tgt_done:
198 	pppt_msg_tx_status(msg, stmf_status);
199 	stmf_ic_msg_free(msg);
200 }
201 
202 static void
203 pppt_msg_session_destroy(stmf_ic_msg_t *msg)
204 {
205 	stmf_ic_session_create_destroy_msg_t	*sess_destroy;
206 	pppt_tgt_t				*tgt;
207 	pppt_sess_t				*ps;
208 
209 	sess_destroy = msg->icm_msg;
210 
211 	PPPT_GLOBAL_LOCK();
212 
213 	/*
214 	 * Look for existing session for this ID
215 	 */
216 	ps = pppt_sess_lookup_locked(sess_destroy->icscd_session_id,
217 	    sess_destroy->icscd_tgt_devid, sess_destroy->icscd_ini_devid);
218 
219 	if (ps == NULL) {
220 		PPPT_GLOBAL_UNLOCK();
221 		stmf_ic_msg_free(msg);
222 		PPPT_INC_STAT(es_sess_destroy_no_session);
223 		return;
224 	}
225 
226 	tgt = ps->ps_target;
227 
228 	mutex_enter(&tgt->target_mutex);
229 	mutex_enter(&ps->ps_mutex);
230 
231 	/* Release the reference from the lookup */
232 	pppt_sess_rele_locked(ps);
233 
234 	/* Make sure another thread is not already closing the session */
235 	if (!ps->ps_closed) {
236 		/* Found matching open session, quiesce... */
237 		pppt_sess_close_locked(ps);
238 	}
239 	mutex_exit(&ps->ps_mutex);
240 	mutex_exit(&tgt->target_mutex);
241 	PPPT_GLOBAL_UNLOCK();
242 
243 	stmf_ic_msg_free(msg);
244 }
245 
246 static void
247 pppt_msg_scsi_cmd(stmf_ic_msg_t *msg)
248 {
249 	pppt_sess_t			*pppt_sess;
250 	pppt_buf_t			*pbuf;
251 	stmf_ic_scsi_cmd_msg_t		*scmd;
252 	pppt_task_t			*ptask;
253 	scsi_task_t			*task;
254 	pppt_status_t			pppt_status;
255 	stmf_local_port_t		*lport;
256 	stmf_scsi_session_t		*stmf_sess;
257 	stmf_status_t			stmf_status;
258 
259 	/*
260 	 * Get a task context
261 	 */
262 	ptask = pppt_task_alloc();
263 	if (ptask == NULL) {
264 		/*
265 		 * We must be very low on memory.  Just free the message
266 		 * and let the command timeout.
267 		 */
268 		stmf_ic_msg_free(msg);
269 		PPPT_INC_STAT(es_scmd_ptask_alloc_fail);
270 		return;
271 	}
272 
273 	scmd = msg->icm_msg;
274 
275 	/*
276 	 * Session are created implicitly on the first use of an
277 	 * IT nexus
278 	 */
279 	pppt_sess = pppt_sess_lookup_create(scmd->icsc_tgt_devid,
280 	    scmd->icsc_ini_devid, scmd->icsc_session_id, &stmf_status);
281 	if (pppt_sess == NULL) {
282 		pppt_task_free(ptask);
283 		pppt_msg_tx_status(msg, stmf_status);
284 		stmf_ic_msg_free(msg);
285 		PPPT_INC_STAT(es_scmd_sess_create_fail);
286 		return;
287 	}
288 
289 	ptask->pt_sess = pppt_sess;
290 	ptask->pt_task_id = scmd->icsc_task_msgid;
291 	stmf_sess = pppt_sess->ps_stmf_sess;
292 	lport = stmf_sess->ss_lport;
293 
294 	/*
295 	 * Add task to our internal task set.
296 	 */
297 	pppt_status = pppt_task_start(ptask);
298 
299 	if (pppt_status != 0) {
300 		/* Release hold from pppt_sess_lookup_create() */
301 		PPPT_LOG(CE_WARN, "Duplicate taskid from remote node 0x%llx",
302 		    (longlong_t)scmd->icsc_task_msgid);
303 		pppt_task_free(ptask);
304 		pppt_sess_rele(pppt_sess);
305 		pppt_msg_tx_status(msg, STMF_ALREADY);
306 		stmf_ic_msg_free(msg);
307 		PPPT_INC_STAT(es_scmd_dup_task_count);
308 		return;
309 	}
310 
311 	/*
312 	 * Allocate STMF task context
313 	 */
314 	ptask->pt_stmf_task = stmf_task_alloc(lport, stmf_sess,
315 	    scmd->icsc_task_lun_no,
316 	    scmd->icsc_task_cdb_length, 0);
317 	if (ptask->pt_stmf_task == NULL) {
318 		(void) pppt_task_done(ptask);
319 		pppt_task_free(ptask);
320 		pppt_sess_rele(pppt_sess);
321 		pppt_msg_tx_status(msg, STMF_ALLOC_FAILURE);
322 		stmf_ic_msg_free(msg);
323 		PPPT_INC_STAT(es_scmd_stask_alloc_fail);
324 		return;
325 	}
326 
327 	task = ptask->pt_stmf_task;
328 	task->task_port_private = ptask;
329 	task->task_flags = scmd->icsc_task_flags;
330 	task->task_additional_flags = 0;
331 	task->task_priority = 0;
332 
333 	/*
334 	 * Set task->task_mgmt_function to TM_NONE for a normal SCSI task
335 	 * or one of these values for a task management command:
336 	 *
337 	 * TM_ABORT_TASK ***
338 	 * TM_ABORT_TASK_SET
339 	 * TM_CLEAR_ACA
340 	 * TM_CLEAR_TASK_SET
341 	 * TM_LUN_RESET
342 	 * TM_TARGET_WARM_RESET
343 	 * TM_TARGET_COLD_RESET
344 	 *
345 	 * *** Note that STMF does not currently support TM_ABORT_TASK so
346 	 * port providers must implement this command on their own
347 	 * (e.g. lookup the desired task and call stmf_abort).
348 	 */
349 	task->task_mgmt_function = scmd->icsc_task_mgmt_function;
350 
351 	task->task_max_nbufs = 1; /* Don't allow parallel xfers */
352 	task->task_cmd_seq_no = msg->icm_msgid;
353 	task->task_expected_xfer_length =
354 	    scmd->icsc_task_expected_xfer_length;
355 
356 	bcopy(scmd->icsc_task_cdb, task->task_cdb,
357 	    scmd->icsc_task_cdb_length);
358 	bcopy(scmd->icsc_lun_id, ptask->pt_lun_id, 16);
359 
360 	if (scmd->icsc_immed_data_len) {
361 		pbuf = ptask->pt_immed_data;
362 		pbuf->pbuf_immed_msg = msg;
363 		pbuf->pbuf_stmf_buf->db_data_size = scmd->icsc_immed_data_len;
364 		pbuf->pbuf_stmf_buf->db_buf_size = scmd->icsc_immed_data_len;
365 		pbuf->pbuf_stmf_buf->db_relative_offset = 0;
366 		pbuf->pbuf_stmf_buf->db_sglist[0].seg_length =
367 		    scmd->icsc_immed_data_len;
368 		pbuf->pbuf_stmf_buf->db_sglist[0].seg_addr =
369 		    scmd->icsc_immed_data;
370 
371 		stmf_post_task(task, pbuf->pbuf_stmf_buf);
372 	} else {
373 		stmf_post_task(task, NULL);
374 		stmf_ic_msg_free(msg);
375 	}
376 }
377 
378 static void
379 pppt_msg_data_xfer_done(stmf_ic_msg_t *msg)
380 {
381 	pppt_task_t				*pppt_task;
382 	stmf_ic_scsi_data_xfer_done_msg_t	*data_xfer_done;
383 
384 	data_xfer_done = msg->icm_msg;
385 
386 	/*
387 	 * Find task
388 	 */
389 	pppt_task = pppt_task_lookup(data_xfer_done->icsx_task_msgid);
390 
391 	/* If we found one, complete the transfer */
392 	if (pppt_task != NULL) {
393 		pppt_xfer_read_complete(pppt_task, data_xfer_done->icsx_status);
394 	}
395 
396 	stmf_ic_msg_free(msg);
397 }
398 
399 static void
400 pppt_msg_handle_status(stmf_ic_msg_t *msg)
401 {
402 	/* Don't care for now */
403 	stmf_ic_msg_free(msg);
404 }
405