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