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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/types.h>
30 #include <sys/sunddi.h>
31 #include <sys/modctl.h>
32 #include <sys/byteorder.h>
35 #include <sys/ib/clients/iser/iser.h>
42 iser_xfer_hello_msg(iser_chan_t
*chan
)
48 iser_hello_hdr_t
*hdr
;
53 hca
= (iser_hca_t
*)chan
->ic_hca
;
55 ISER_LOG(CE_NOTE
, "iser_xfer_hello_msg: no hca handle found");
56 return (ISER_STATUS_FAIL
);
59 msg
= iser_msg_get(hca
, 1, NULL
);
62 ISER_LOG(CE_NOTE
, "iser_xfer_hello_msg: iser message cache "
64 return (ISER_STATUS_FAIL
);
67 /* Send iSER Hello Message to declare iSER parameters to the target */
68 hdr
= (iser_hello_hdr_t
*)(uintptr_t)msg
->msg_ds
.ds_va
;
70 hdr
->opcode
= ISER_OPCODE_HELLO_MSG
;
74 hdr
->iser_ird
= htons(ISER_IB_DEFAULT_IRD
);
78 /* Allocate an iSER WR handle and tuck this msg into it */
79 iser_wr
= iser_wr_get();
80 if (iser_wr
== NULL
) {
81 ISER_LOG(CE_NOTE
, "iser_xfer_hello_msg: unable to allocate "
84 return (ISER_STATUS_FAIL
);
86 iser_wr
->iw_msg
= msg
;
87 iser_wr
->iw_type
= ISER_WR_SEND
;
89 /* Use the address of our generic iser_wr handle as our WRID */
90 wr
.wr_id
= (ibt_wrid_t
)(uintptr_t)iser_wr
;
92 /* Populate the rest of the work request */
93 wr
.wr_trans
= IBT_RC_SRV
;
94 wr
.wr_opcode
= IBT_WRC_SEND
;
96 wr
.wr_sgl
= &msg
->msg_ds
;
99 * Avoid race condition by incrementing this channel's
100 * SQ posted count prior to calling ibt_post_send
102 mutex_enter(&chan
->ic_sq_post_lock
);
103 chan
->ic_sq_post_count
++;
104 if (chan
->ic_sq_post_count
> chan
->ic_sq_max_post_count
)
105 chan
->ic_sq_max_post_count
= chan
->ic_sq_post_count
;
106 mutex_exit(&chan
->ic_sq_post_lock
);
108 status
= ibt_post_send(chan
->ic_chanhdl
, &wr
, 1, NULL
);
109 if (status
!= IBT_SUCCESS
) {
110 ISER_LOG(CE_NOTE
, "iser_xfer_hello_msg: ibt_post_send "
111 "failure (%d)", status
);
112 mutex_enter(&chan
->ic_sq_post_lock
);
113 chan
->ic_sq_post_count
--;
114 mutex_exit(&chan
->ic_sq_post_lock
);
116 iser_wr_free(iser_wr
);
117 return (ISER_STATUS_FAIL
);
120 ISER_LOG(CE_NOTE
, "Posting iSER Hello message: chan (0x%p): "
121 "IP [%x to %x]", (void *)chan
, chan
->ic_localip
.un
.ip4addr
,
122 chan
->ic_remoteip
.un
.ip4addr
);
124 return (ISER_STATUS_SUCCESS
);
128 iser_xfer_helloreply_msg(iser_chan_t
*chan
)
134 iser_helloreply_hdr_t
*hdr
;
137 ASSERT(chan
!= NULL
);
139 hca
= (iser_hca_t
*)chan
->ic_hca
;
141 ISER_LOG(CE_NOTE
, "iser_xfer_helloreply_msg: no hca handle "
143 return (ISER_STATUS_FAIL
);
146 msg
= iser_msg_get(hca
, 1, NULL
);
149 ISER_LOG(CE_NOTE
, "iser_xfer_helloreply_msg: iser message "
150 "cache alloc failed");
151 return (ISER_STATUS_FAIL
);
154 /* Use the iSER Hello Reply Message */
155 hdr
= (iser_helloreply_hdr_t
*)(uintptr_t)msg
->msg_ds
.ds_va
;
157 hdr
->opcode
= ISER_OPCODE_HELLOREPLY_MSG
;
162 hdr
->iser_ord
= htons(ISER_IB_DEFAULT_ORD
);
166 /* Allocate an iSER WR handle and tuck this msg into it */
167 iser_wr
= iser_wr_get();
168 if (iser_wr
== NULL
) {
169 ISER_LOG(CE_NOTE
, "iser_xfer_helloreply_msg: unable to "
170 "allocate iser wr handle");
172 return (ISER_STATUS_FAIL
);
174 iser_wr
->iw_msg
= msg
;
175 iser_wr
->iw_type
= ISER_WR_SEND
;
177 /* Use the address of our generic iser_wr handle as our WRID */
178 wr
.wr_id
= (ibt_wrid_t
)(uintptr_t)iser_wr
;
180 /* Populate the rest of the work request */
181 wr
.wr_trans
= IBT_RC_SRV
;
182 wr
.wr_opcode
= IBT_WRC_SEND
;
184 wr
.wr_sgl
= &msg
->msg_ds
;
186 mutex_enter(&chan
->ic_sq_post_lock
);
187 chan
->ic_sq_post_count
++;
188 if (chan
->ic_sq_post_count
> chan
->ic_sq_max_post_count
)
189 chan
->ic_sq_max_post_count
= chan
->ic_sq_post_count
;
191 mutex_exit(&chan
->ic_sq_post_lock
);
193 status
= ibt_post_send(chan
->ic_chanhdl
, &wr
, 1, NULL
);
194 if (status
!= IBT_SUCCESS
) {
195 ISER_LOG(CE_NOTE
, "iser_xfer_helloreply_msg: ibt_post_send "
196 "failure (%d)", status
);
197 mutex_enter(&chan
->ic_sq_post_lock
);
198 chan
->ic_sq_post_count
--;
199 mutex_exit(&chan
->ic_sq_post_lock
);
201 iser_wr_free(iser_wr
);
202 return (ISER_STATUS_FAIL
);
204 ISER_LOG(CE_NOTE
, "Posting iSER HelloReply message: chan (0x%p): "
205 "IP [%x to %x]", (void *)chan
, chan
->ic_localip
.un
.ip4addr
,
206 chan
->ic_remoteip
.un
.ip4addr
);
208 return (ISER_STATUS_SUCCESS
);
214 * This is iSER's implementation of the 'Send_control' operational primitive.
215 * This iSER layer uses the Send Message type of RCaP to transfer the iSCSI
216 * Control-type PDU. A special case is that the transfer of SCSI Data-out PDUs
217 * carrying unsolicited data are also treated as iSCSI Control-Type PDUs. The
218 * message payload contains an iSER header followed by the iSCSI Control-type
219 * the iSCSI Control-type PDU.
220 * This function is invoked by an initiator iSCSI layer requesting the transfer
221 * of a iSCSI command PDU or a target iSCSI layer requesting the transfer of a
222 * iSCSI response PDU.
225 iser_xfer_ctrlpdu(iser_chan_t
*chan
, idm_pdu_t
*pdu
)
228 iser_ctrl_hdr_t
*hdr
;
234 iscsi_data_hdr_t
*bhs
;
236 idm_task_t
*idt
= NULL
;
239 ASSERT(chan
!= NULL
);
241 mutex_enter(&chan
->ic_conn
->ic_lock
);
242 /* Bail out if the connection is closed */
243 if ((chan
->ic_conn
->ic_stage
== ISER_CONN_STAGE_CLOSING
) ||
244 (chan
->ic_conn
->ic_stage
== ISER_CONN_STAGE_CLOSED
)) {
245 mutex_exit(&chan
->ic_conn
->ic_lock
);
246 return (ISER_STATUS_FAIL
);
249 ic
= chan
->ic_conn
->ic_idmc
;
251 /* Pull the BHS out of the PDU handle */
252 bhs
= (iscsi_data_hdr_t
*)pdu
->isp_hdr
;
255 * All SCSI command PDU (except SCSI Read and SCSI Write) and the SCSI
256 * Response PDU are sent to the remote end using the SendSE Message.
258 * The StatSN may need to be sent (and possibly advanced) at this time
259 * for some PDUs, identified by the IDM_PDU_SET_STATSN flag.
261 if (pdu
->isp_flags
& IDM_PDU_SET_STATSN
) {
262 (ic
->ic_conn_ops
.icb_update_statsn
)(NULL
, pdu
);
265 * Setup a Send Message for carrying the iSCSI control-type PDU
266 * preceeded by an iSER header.
268 hca
= (iser_hca_t
*)chan
->ic_hca
;
270 ISER_LOG(CE_NOTE
, "iser_xfer_ctrlpdu: no hca handle found");
271 mutex_exit(&chan
->ic_conn
->ic_lock
);
272 return (ISER_STATUS_FAIL
);
275 msg
= iser_msg_get(hca
, 1, NULL
);
277 ISER_LOG(CE_NOTE
, "iser_xfer_ctrlpdu: iser message cache "
279 mutex_exit(&chan
->ic_conn
->ic_lock
);
280 return (ISER_STATUS_FAIL
);
283 hdr
= (iser_ctrl_hdr_t
*)(uintptr_t)msg
->msg_ds
.ds_va
;
286 * Initialize header assuming no transfers
288 bzero(hdr
, sizeof (*hdr
));
289 hdr
->opcode
= ISER_OPCODE_CTRL_TYPE_PDU
;
292 * On the initiator side, the task buffers will be used to identify
293 * if there are any buffers to be advertised
295 if ((ic
->ic_conn_type
== CONN_TYPE_INI
) &&
296 ((bhs
->opcode
& ISCSI_OPCODE_MASK
) == ISCSI_OP_SCSI_CMD
) &&
297 ((idt
= idm_task_find(ic
, bhs
->itt
, bhs
->ttt
)) != NULL
)) {
299 if (!list_is_empty(&idt
->idt_inbufv
)) {
300 buf
= idm_buf_find(&idt
->idt_inbufv
, 0);
303 mr
= (iser_mr_t
*)buf
->idb_reg_private
;
307 hdr
->rstag
= htonl(mr
->is_mrrkey
);
308 BE_OUT64(&hdr
->rva
, mr
->is_mrva
);
311 if (!list_is_empty(&idt
->idt_outbufv
)) {
312 buf
= idm_buf_find(&idt
->idt_outbufv
, 0);
315 mr
= (iser_mr_t
*)buf
->idb_reg_private
;
319 hdr
->wstag
= htonl(mr
->is_mrrkey
);
320 BE_OUT64(&hdr
->wva
, mr
->is_mrva
);
323 /* Release our reference on the task */
327 /* Copy the BHS after the iSER header */
329 (uint8_t *)(uintptr_t)msg
->msg_ds
.ds_va
+ ISER_HEADER_LENGTH
,
332 if (pdu
->isp_datalen
> 0) {
333 /* Copy the isp_data after the PDU header */
335 (uint8_t *)(uintptr_t)msg
->msg_ds
.ds_va
+
336 ISER_HEADER_LENGTH
+ pdu
->isp_hdrlen
,
339 /* Set the SGE's ds_len */
340 msg
->msg_ds
.ds_len
= ISER_HEADER_LENGTH
+ pdu
->isp_hdrlen
+
343 /* No data, so set the SGE's ds_len to the headers length */
344 msg
->msg_ds
.ds_len
= ISER_HEADER_LENGTH
+ pdu
->isp_hdrlen
;
348 * Build Work Request to be posted on the Send Queue.
350 bzero(&wr
, sizeof (wr
));
352 /* Allocate an iSER WR handle and tuck the msg and pdu into it */
353 iser_wr
= iser_wr_get();
354 if (iser_wr
== NULL
) {
355 ISER_LOG(CE_NOTE
, "iser_xfer_ctrlpdu: unable to allocate "
358 mutex_exit(&chan
->ic_conn
->ic_lock
);
359 return (ISER_STATUS_FAIL
);
361 iser_wr
->iw_pdu
= pdu
;
362 iser_wr
->iw_msg
= msg
;
363 iser_wr
->iw_type
= ISER_WR_SEND
;
366 * Use the address of our generic iser_wr handle as our WRID
367 * and populate the rest of the work request
369 wr
.wr_id
= (ibt_wrid_t
)(uintptr_t)iser_wr
;
370 wr
.wr_trans
= IBT_RC_SRV
;
371 wr
.wr_opcode
= IBT_WRC_SEND
;
373 wr
.wr_sgl
= &msg
->msg_ds
;
375 /* Increment this channel's SQ posted count */
376 mutex_enter(&chan
->ic_sq_post_lock
);
377 chan
->ic_sq_post_count
++;
378 if (chan
->ic_sq_post_count
> chan
->ic_sq_max_post_count
)
379 chan
->ic_sq_max_post_count
= chan
->ic_sq_post_count
;
380 mutex_exit(&chan
->ic_sq_post_lock
);
382 /* Post Send Work Request on the specified channel */
383 status
= ibt_post_send(chan
->ic_chanhdl
, &wr
, 1, NULL
);
384 if (status
!= IBT_SUCCESS
) {
385 ISER_LOG(CE_NOTE
, "iser_xfer_ctrlpdu: ibt_post_send "
386 "failure (%d)", status
);
388 iser_wr_free(iser_wr
);
389 mutex_enter(&chan
->ic_sq_post_lock
);
390 chan
->ic_sq_post_count
--;
391 mutex_exit(&chan
->ic_sq_post_lock
);
392 mutex_exit(&chan
->ic_conn
->ic_lock
);
393 return (ISER_STATUS_FAIL
);
396 mutex_exit(&chan
->ic_conn
->ic_lock
);
397 return (ISER_STATUS_SUCCESS
);
401 * iser_xfer_buf_to_ini
402 * This is iSER's implementation of the 'Put_Data' operational primitive.
403 * The iSCSI layer at the target invokes this function when it is ready to
404 * return the SCSI Read Data to the initiator. This function generates and
405 * sends an RDMA Write Message containing the read data to the initiator.
408 iser_xfer_buf_to_ini(idm_task_t
*idt
, idm_buf_t
*buf
)
410 iser_conn_t
*iser_conn
;
411 iser_chan_t
*iser_chan
;
412 iser_buf_t
*iser_buf
;
414 iser_ctrl_hdr_t
*iser_hdr
;
420 /* Grab the iSER resources from the task and buf handles */
421 iser_conn
= (iser_conn_t
*)idt
->idt_ic
->ic_transport_private
;
422 iser_chan
= iser_conn
->ic_chan
;
424 mutex_enter(&iser_chan
->ic_conn
->ic_lock
);
425 /* Bail out if the connection is closed */
426 if ((iser_chan
->ic_conn
->ic_stage
== ISER_CONN_STAGE_CLOSING
) ||
427 (iser_chan
->ic_conn
->ic_stage
== ISER_CONN_STAGE_CLOSED
)) {
428 mutex_exit(&iser_chan
->ic_conn
->ic_lock
);
429 return (ISER_STATUS_FAIL
);
432 iser_buf
= (iser_buf_t
*)buf
->idb_buf_private
;
433 iser_hdr
= (iser_ctrl_hdr_t
*)idt
->idt_transport_hdr
;
435 /* Pull the Read STag data out of the iSER header in the task hdl */
436 reg_raddr
= BE_IN64(&iser_hdr
->rva
);
437 reg_rkey
= (ntohl(iser_hdr
->rstag
));
439 /* Set up the WR raddr and rkey based upon the Read iSER STag */
440 bzero(&wr
, sizeof (ibt_send_wr_t
));
441 wr
.wr
.rc
.rcwr
.rdma
.rdma_raddr
= reg_raddr
+ buf
->idb_bufoffset
;
442 wr
.wr
.rc
.rcwr
.rdma
.rdma_rkey
= reg_rkey
;
444 /* Set the transfer length from the IDM buf handle */
445 iser_buf
->buf_ds
.ds_len
= buf
->idb_xfer_len
;
447 /* Allocate an iSER WR handle and tuck the IDM buf handle into it */
448 iser_wr
= iser_wr_get();
449 if (iser_wr
== NULL
) {
450 ISER_LOG(CE_NOTE
, "iser_xfer_buf_to_ini: unable to allocate "
452 mutex_exit(&iser_chan
->ic_conn
->ic_lock
);
453 return (ISER_STATUS_FAIL
);
455 iser_wr
->iw_buf
= buf
;
456 iser_wr
->iw_type
= ISER_WR_RDMAW
;
458 /* Use the address of our generic iser_wr handle as our WRID */
459 wr
.wr_id
= (ibt_wrid_t
)(uintptr_t)iser_wr
;
461 /* Populate the rest of the work request */
462 wr
.wr_flags
= IBT_WR_SEND_SIGNAL
;
463 wr
.wr_trans
= IBT_RC_SRV
;
464 wr
.wr_opcode
= IBT_WRC_RDMAW
;
466 wr
.wr_sgl
= &iser_buf
->buf_ds
;
469 bcopy(&wr
, &iser_buf
->buf_wr
, sizeof (ibt_send_wr_t
));
472 DTRACE_ISCSI_8(xfer__start
, idm_conn_t
*, idt
->idt_ic
,
473 uintptr_t, buf
->idb_buf
, uint32_t, buf
->idb_bufoffset
,
474 uint64_t, reg_raddr
, uint32_t, buf
->idb_bufoffset
,
476 uint32_t, buf
->idb_xfer_len
, int, XFER_BUF_TX_TO_INI
);
478 /* Increment this channel's SQ posted count */
479 mutex_enter(&iser_chan
->ic_sq_post_lock
);
480 iser_chan
->ic_sq_post_count
++;
481 if (iser_chan
->ic_sq_post_count
> iser_chan
->ic_sq_max_post_count
)
482 iser_chan
->ic_sq_max_post_count
= iser_chan
->ic_sq_post_count
;
483 mutex_exit(&iser_chan
->ic_sq_post_lock
);
485 status
= ibt_post_send(iser_chan
->ic_chanhdl
, &wr
, 1, NULL
);
486 if (status
!= IBT_SUCCESS
) {
487 ISER_LOG(CE_NOTE
, "iser_xfer_buf_to_ini: ibt_post_send "
488 "failure (%d)", status
);
489 iser_wr_free(iser_wr
);
490 mutex_enter(&iser_chan
->ic_sq_post_lock
);
491 iser_chan
->ic_sq_post_count
--;
492 mutex_exit(&iser_chan
->ic_sq_post_lock
);
493 mutex_exit(&iser_chan
->ic_conn
->ic_lock
);
494 return (ISER_STATUS_FAIL
);
497 mutex_exit(&iser_chan
->ic_conn
->ic_lock
);
498 return (ISER_STATUS_SUCCESS
);
502 * iser_xfer_buf_from_ini
503 * This is iSER's implementation of the 'Get_Data' operational primitive.
504 * The iSCSI layer at the target invokes this function when it is ready to
505 * receive the SCSI Write Data from the initiator. This function generates and
506 * sends an RDMA Read Message to get the data from the initiator. No R2T PDUs
510 iser_xfer_buf_from_ini(idm_task_t
*idt
, idm_buf_t
*buf
)
512 iser_conn_t
*iser_conn
;
513 iser_chan_t
*iser_chan
;
514 iser_buf_t
*iser_buf
;
516 iser_ctrl_hdr_t
*iser_hdr
;
522 /* Grab the iSER resources from the task and buf handles */
523 iser_conn
= (iser_conn_t
*)idt
->idt_ic
->ic_transport_private
;
524 iser_chan
= iser_conn
->ic_chan
;
526 mutex_enter(&iser_chan
->ic_conn
->ic_lock
);
527 /* Bail out if the connection is closed */
528 if ((iser_chan
->ic_conn
->ic_stage
== ISER_CONN_STAGE_CLOSING
) ||
529 (iser_chan
->ic_conn
->ic_stage
== ISER_CONN_STAGE_CLOSED
)) {
530 mutex_exit(&iser_chan
->ic_conn
->ic_lock
);
531 return (ISER_STATUS_FAIL
);
534 iser_buf
= (iser_buf_t
*)buf
->idb_buf_private
;
535 iser_hdr
= (iser_ctrl_hdr_t
*)idt
->idt_transport_hdr
;
537 /* Pull the Write STag data out of the iSER header in the task hdl */
538 reg_raddr
= BE_IN64(&iser_hdr
->wva
);
539 reg_rkey
= (ntohl(iser_hdr
->wstag
));
541 /* Set up the WR raddr and rkey based upon the iSER Write STag */
542 bzero(&wr
, sizeof (ibt_send_wr_t
));
543 wr
.wr
.rc
.rcwr
.rdma
.rdma_raddr
= reg_raddr
+ buf
->idb_bufoffset
;
544 wr
.wr
.rc
.rcwr
.rdma
.rdma_rkey
= reg_rkey
;
546 /* Set the transfer length from the IDM buf handle */
547 iser_buf
->buf_ds
.ds_len
= buf
->idb_xfer_len
;
549 /* Allocate an iSER WR handle and tuck the IDM buf handle into it */
550 iser_wr
= iser_wr_get();
551 if (iser_wr
== NULL
) {
552 ISER_LOG(CE_NOTE
, "iser_xfer_buf_from_ini: unable to allocate "
554 mutex_exit(&iser_chan
->ic_conn
->ic_lock
);
555 return (ISER_STATUS_FAIL
);
557 iser_wr
->iw_buf
= buf
;
558 iser_wr
->iw_type
= ISER_WR_RDMAR
;
560 /* Use the address of our generic iser_wr handle as our WRID */
561 wr
.wr_id
= (ibt_wrid_t
)(uintptr_t)iser_wr
;
563 /* Populate the rest of the work request */
564 wr
.wr_flags
= IBT_WR_SEND_SIGNAL
;
565 wr
.wr_trans
= IBT_RC_SRV
;
566 wr
.wr_opcode
= IBT_WRC_RDMAR
;
568 wr
.wr_sgl
= &iser_buf
->buf_ds
;
571 bcopy(&wr
, &iser_buf
->buf_wr
, sizeof (ibt_send_wr_t
));
574 DTRACE_ISCSI_8(xfer__start
, idm_conn_t
*, idt
->idt_ic
,
575 uintptr_t, buf
->idb_buf
, uint32_t, buf
->idb_bufoffset
,
576 uint64_t, reg_raddr
, uint32_t, buf
->idb_bufoffset
,
578 uint32_t, buf
->idb_xfer_len
, int, XFER_BUF_RX_FROM_INI
);
580 /* Increment this channel's SQ posted count */
581 mutex_enter(&iser_chan
->ic_sq_post_lock
);
582 iser_chan
->ic_sq_post_count
++;
583 if (iser_chan
->ic_sq_post_count
> iser_chan
->ic_sq_max_post_count
)
584 iser_chan
->ic_sq_max_post_count
= iser_chan
->ic_sq_post_count
;
585 mutex_exit(&iser_chan
->ic_sq_post_lock
);
587 status
= ibt_post_send(iser_chan
->ic_chanhdl
, &wr
, 1, NULL
);
588 if (status
!= IBT_SUCCESS
) {
589 ISER_LOG(CE_NOTE
, "iser_xfer_buf_from_ini: ibt_post_send "
590 "failure (%d)", status
);
591 iser_wr_free(iser_wr
);
592 mutex_enter(&iser_chan
->ic_sq_post_lock
);
593 iser_chan
->ic_sq_post_count
--;
594 mutex_exit(&iser_chan
->ic_sq_post_lock
);
595 mutex_exit(&iser_chan
->ic_conn
->ic_lock
);
596 return (ISER_STATUS_FAIL
);
599 mutex_exit(&iser_chan
->ic_conn
->ic_lock
);
600 return (ISER_STATUS_SUCCESS
);