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 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
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
43 #include "pppt.h"
44
45 static void pppt_msg_tgt_register(stmf_ic_msg_t *reg_port);
46
47 static void pppt_msg_tgt_deregister(stmf_ic_msg_t *msg);
48
49 static void pppt_msg_session_destroy(stmf_ic_msg_t *msg);
50
51 static void pppt_msg_scsi_cmd(stmf_ic_msg_t *msg);
52
53 static void pppt_msg_data_xfer_done(stmf_ic_msg_t *msg);
54
55 static void pppt_msg_handle_status(stmf_ic_msg_t *msg);
56
57 void
pppt_msg_rx(stmf_ic_msg_t * msg)58 pppt_msg_rx(stmf_ic_msg_t *msg)
59 {
60 switch (msg->icm_msg_type) {
61 case STMF_ICM_REGISTER_PROXY_PORT:
62 pppt_msg_tgt_register(msg);
63 break;
64 case STMF_ICM_DEREGISTER_PROXY_PORT:
65 pppt_msg_tgt_deregister(msg);
66 break;
67 case STMF_ICM_SESSION_CREATE:
68 pppt_msg_tx_status(msg, STMF_NOT_SUPPORTED);
69 stmf_ic_msg_free(msg);
70 break;
71 case STMF_ICM_SESSION_DESTROY:
72 pppt_msg_session_destroy(msg);
73 break;
74 case STMF_ICM_SCSI_CMD:
75 pppt_msg_scsi_cmd(msg);
76 break;
77 case STMF_ICM_SCSI_DATA_XFER_DONE:
78 pppt_msg_data_xfer_done(msg);
79 break;
80 case STMF_ICM_SCSI_DATA:
81 /* Ignore, all proxy data will be immediate for now */
82 pppt_msg_tx_status(msg, STMF_NOT_SUPPORTED);
83 stmf_ic_msg_free(msg);
84 break;
85 case STMF_ICM_STATUS:
86 pppt_msg_handle_status(msg);
87 break;
88 default:
89 /* Other message types are not allowed */
90 ASSERT(0);
91 break;
92 }
93 }
94
95 void
pppt_msg_tx_status(stmf_ic_msg_t * orig_msg,stmf_status_t status)96 pppt_msg_tx_status(stmf_ic_msg_t *orig_msg, stmf_status_t status)
97 {
98 stmf_ic_msg_t *msg;
99
100 /*
101 * If TX of status fails it should be treated the same as a loss of
102 * connection. We expect the remote node to handle it.
103 */
104 msg = stmf_ic_status_msg_alloc(status, orig_msg->icm_msg_type,
105 orig_msg->icm_msgid);
106
107 if (msg != NULL) {
108 (void) stmf_ic_tx_msg(msg);
109 }
110 }
111
112 static void
pppt_msg_tgt_register(stmf_ic_msg_t * msg)113 pppt_msg_tgt_register(stmf_ic_msg_t *msg)
114 {
115 stmf_ic_reg_port_msg_t *reg_port;
116 pppt_tgt_t *result;
117 stmf_status_t stmf_status;
118
119 reg_port = msg->icm_msg;
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
pppt_msg_tgt_deregister(stmf_ic_msg_t * msg)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
pppt_msg_session_destroy(stmf_ic_msg_t * msg)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_rport);
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
pppt_msg_scsi_cmd(stmf_ic_msg_t * msg)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_rport,
282 scmd->icsc_session_id, &stmf_status);
283 if (pppt_sess == NULL) {
284 pppt_task_free(ptask);
285 pppt_msg_tx_status(msg, stmf_status);
286 stmf_ic_msg_free(msg);
287 PPPT_INC_STAT(es_scmd_sess_create_fail);
288 return;
289 }
290
291 ptask->pt_sess = pppt_sess;
292 ptask->pt_task_id = scmd->icsc_task_msgid;
293 stmf_sess = pppt_sess->ps_stmf_sess;
294 lport = stmf_sess->ss_lport;
295
296 /*
297 * Add task to our internal task set.
298 */
299 pppt_status = pppt_task_start(ptask);
300
301 if (pppt_status != 0) {
302 /* Release hold from pppt_sess_lookup_create() */
303 PPPT_LOG(CE_WARN, "Duplicate taskid from remote node 0x%llx",
304 (longlong_t)scmd->icsc_task_msgid);
305 pppt_task_free(ptask);
306 pppt_sess_rele(pppt_sess);
307 pppt_msg_tx_status(msg, STMF_ALREADY);
308 stmf_ic_msg_free(msg);
309 PPPT_INC_STAT(es_scmd_dup_task_count);
310 return;
311 }
312
313 /*
314 * Allocate STMF task context
315 */
316 ptask->pt_stmf_task = stmf_task_alloc(lport, stmf_sess,
317 scmd->icsc_task_lun_no,
318 scmd->icsc_task_cdb_length, 0);
319 if (ptask->pt_stmf_task == NULL) {
320 /* NOTE: pppt_task_done() will free ptask. */
321 (void) pppt_task_done(ptask);
322 pppt_sess_rele(pppt_sess);
323 pppt_msg_tx_status(msg, STMF_ALLOC_FAILURE);
324 stmf_ic_msg_free(msg);
325 PPPT_INC_STAT(es_scmd_stask_alloc_fail);
326 return;
327 }
328
329 task = ptask->pt_stmf_task;
330 /* task_port_private reference is a real reference. */
331 (void) pppt_task_hold(ptask);
332 task->task_port_private = ptask;
333 task->task_flags = scmd->icsc_task_flags;
334 task->task_additional_flags = TASK_AF_PPPT_TASK;
335 task->task_priority = 0;
336
337 /*
338 * Set task->task_mgmt_function to TM_NONE for a normal SCSI task
339 * or one of these values for a task management command:
340 *
341 * TM_ABORT_TASK ***
342 * TM_ABORT_TASK_SET
343 * TM_CLEAR_ACA
344 * TM_CLEAR_TASK_SET
345 * TM_LUN_RESET
346 * TM_TARGET_WARM_RESET
347 * TM_TARGET_COLD_RESET
348 *
349 * *** Note that STMF does not currently support TM_ABORT_TASK so
350 * port providers must implement this command on their own
351 * (e.g. lookup the desired task and call stmf_abort).
352 */
353 task->task_mgmt_function = scmd->icsc_task_mgmt_function;
354
355 task->task_max_nbufs = 1; /* Don't allow parallel xfers */
356 task->task_cmd_seq_no = msg->icm_msgid;
357 task->task_expected_xfer_length =
358 scmd->icsc_task_expected_xfer_length;
359
360 if (scmd->icsc_task_cdb_length) {
361 bcopy(scmd->icsc_task_cdb, task->task_cdb,
362 scmd->icsc_task_cdb_length);
363 }
364 bcopy(scmd->icsc_lun_id, ptask->pt_lun_id, 16);
365
366 if (scmd->icsc_immed_data_len) {
367 pbuf = ptask->pt_immed_data;
368 pbuf->pbuf_immed_msg = msg;
369 pbuf->pbuf_stmf_buf->db_data_size = scmd->icsc_immed_data_len;
370 pbuf->pbuf_stmf_buf->db_buf_size = scmd->icsc_immed_data_len;
371 pbuf->pbuf_stmf_buf->db_relative_offset = 0;
372 pbuf->pbuf_stmf_buf->db_sglist[0].seg_length =
373 scmd->icsc_immed_data_len;
374 pbuf->pbuf_stmf_buf->db_sglist[0].seg_addr =
375 scmd->icsc_immed_data;
376
377 stmf_post_task(task, pbuf->pbuf_stmf_buf);
378 } else {
379 stmf_post_task(task, NULL);
380 stmf_ic_msg_free(msg);
381 }
382 }
383
384 static void
pppt_msg_data_xfer_done(stmf_ic_msg_t * msg)385 pppt_msg_data_xfer_done(stmf_ic_msg_t *msg)
386 {
387 pppt_task_t *pppt_task;
388 stmf_ic_scsi_data_xfer_done_msg_t *data_xfer_done;
389
390 data_xfer_done = msg->icm_msg;
391
392 /*
393 * Find task
394 */
395 pppt_task = pppt_task_lookup(data_xfer_done->icsx_task_msgid);
396
397 /* If we found one, complete the transfer */
398 if (pppt_task != NULL) {
399 pppt_xfer_read_complete(pppt_task, data_xfer_done->icsx_status);
400 }
401
402 stmf_ic_msg_free(msg);
403 }
404
405 static void
pppt_msg_handle_status(stmf_ic_msg_t * msg)406 pppt_msg_handle_status(stmf_ic_msg_t *msg)
407 {
408 /* Don't care for now */
409 stmf_ic_msg_free(msg);
410 }
411