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
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
pppt_msg_rx(stmf_ic_msg_t * msg)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
pppt_msg_tx_status(stmf_ic_msg_t * orig_msg,stmf_status_t status)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
pppt_msg_tgt_register(stmf_ic_msg_t * msg)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 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
pppt_msg_tgt_deregister(stmf_ic_msg_t * msg)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
pppt_msg_session_destroy(stmf_ic_msg_t * msg)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_rport);
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
pppt_msg_scsi_cmd(stmf_ic_msg_t * msg)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_rport,
281 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 = 1; /* Don't allow parallel xfers */
353 task->task_cmd_seq_no = msg->icm_msgid;
354 task->task_expected_xfer_length =
355 scmd->icsc_task_expected_xfer_length;
356
357 if (scmd->icsc_task_cdb_length) {
358 bcopy(scmd->icsc_task_cdb, task->task_cdb,
359 scmd->icsc_task_cdb_length);
360 }
361 bcopy(scmd->icsc_lun_id, ptask->pt_lun_id, 16);
362
363 if (scmd->icsc_immed_data_len) {
364 pbuf = ptask->pt_immed_data;
365 pbuf->pbuf_immed_msg = msg;
366 pbuf->pbuf_stmf_buf->db_data_size = scmd->icsc_immed_data_len;
367 pbuf->pbuf_stmf_buf->db_buf_size = scmd->icsc_immed_data_len;
368 pbuf->pbuf_stmf_buf->db_relative_offset = 0;
369 pbuf->pbuf_stmf_buf->db_sglist[0].seg_length =
370 scmd->icsc_immed_data_len;
371 pbuf->pbuf_stmf_buf->db_sglist[0].seg_addr =
372 scmd->icsc_immed_data;
373
374 stmf_post_task(task, pbuf->pbuf_stmf_buf);
375 } else {
376 stmf_post_task(task, NULL);
377 stmf_ic_msg_free(msg);
378 }
379 }
380
381 static void
pppt_msg_data_xfer_done(stmf_ic_msg_t * msg)382 pppt_msg_data_xfer_done(stmf_ic_msg_t *msg)
383 {
384 pppt_task_t *pppt_task;
385 stmf_ic_scsi_data_xfer_done_msg_t *data_xfer_done;
386
387 data_xfer_done = msg->icm_msg;
388
389 /*
390 * Find task
391 */
392 pppt_task = pppt_task_lookup(data_xfer_done->icsx_task_msgid);
393
394 /* If we found one, complete the transfer */
395 if (pppt_task != NULL) {
396 pppt_xfer_read_complete(pppt_task, data_xfer_done->icsx_status);
397 }
398
399 stmf_ic_msg_free(msg);
400 }
401
402 static void
pppt_msg_handle_status(stmf_ic_msg_t * msg)403 pppt_msg_handle_status(stmf_ic_msg_t *msg)
404 {
405 /* Don't care for now */
406 stmf_ic_msg_free(msg);
407 }
408