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