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
;
98 status
= ibt_post_send(chan
->ic_chanhdl
, &wr
, 1, NULL
);
99 if (status
!= IBT_SUCCESS
) {
100 ISER_LOG(CE_NOTE
, "iser_xfer_hello_msg: ibt_post_send "
101 "failure (%d)", status
);
103 iser_wr_free(iser_wr
);
104 return (ISER_STATUS_FAIL
);
106 /* Increment this channel's SQ posted count */
107 mutex_enter(&chan
->ic_sq_post_lock
);
108 chan
->ic_sq_post_count
++;
109 if (chan
->ic_sq_post_count
> chan
->ic_sq_max_post_count
)
110 chan
->ic_sq_max_post_count
= chan
->ic_sq_post_count
;
111 mutex_exit(&chan
->ic_sq_post_lock
);
113 ISER_LOG(CE_NOTE
, "Posting iSER Hello message: chan (0x%p): "
114 "IP [%x to %x]", (void *)chan
, chan
->ic_localip
.un
.ip4addr
,
115 chan
->ic_remoteip
.un
.ip4addr
);
117 return (ISER_STATUS_SUCCESS
);
121 iser_xfer_helloreply_msg(iser_chan_t
*chan
)
127 iser_helloreply_hdr_t
*hdr
;
130 ASSERT(chan
!= NULL
);
132 hca
= (iser_hca_t
*)chan
->ic_hca
;
134 ISER_LOG(CE_NOTE
, "iser_xfer_helloreply_msg: no hca handle "
136 return (ISER_STATUS_FAIL
);
139 msg
= iser_msg_get(hca
, 1, NULL
);
142 ISER_LOG(CE_NOTE
, "iser_xfer_helloreply_msg: iser message "
143 "cache alloc failed");
144 return (ISER_STATUS_FAIL
);
147 /* Use the iSER Hello Reply Message */
148 hdr
= (iser_helloreply_hdr_t
*)(uintptr_t)msg
->msg_ds
.ds_va
;
150 hdr
->opcode
= ISER_OPCODE_HELLOREPLY_MSG
;
155 hdr
->iser_ord
= htons(ISER_IB_DEFAULT_ORD
);
159 /* Allocate an iSER WR handle and tuck this msg into it */
160 iser_wr
= iser_wr_get();
161 if (iser_wr
== NULL
) {
162 ISER_LOG(CE_NOTE
, "iser_xfer_helloreply_msg: unable to "
163 "allocate iser wr handle");
165 return (ISER_STATUS_FAIL
);
167 iser_wr
->iw_msg
= msg
;
168 iser_wr
->iw_type
= ISER_WR_SEND
;
170 /* Use the address of our generic iser_wr handle as our WRID */
171 wr
.wr_id
= (ibt_wrid_t
)(uintptr_t)iser_wr
;
173 /* Populate the rest of the work request */
174 wr
.wr_trans
= IBT_RC_SRV
;
175 wr
.wr_opcode
= IBT_WRC_SEND
;
177 wr
.wr_sgl
= &msg
->msg_ds
;
179 status
= ibt_post_send(chan
->ic_chanhdl
, &wr
, 1, NULL
);
180 if (status
!= IBT_SUCCESS
) {
181 ISER_LOG(CE_NOTE
, "iser_xfer_helloreply_msg: ibt_post_send "
182 "failure (%d)", status
);
184 iser_wr_free(iser_wr
);
185 return (ISER_STATUS_FAIL
);
187 /* Increment this channel's SQ posted count */
188 mutex_enter(&chan
->ic_sq_post_lock
);
189 chan
->ic_sq_post_count
++;
190 if (chan
->ic_sq_post_count
> chan
->ic_sq_max_post_count
)
191 chan
->ic_sq_max_post_count
= chan
->ic_sq_post_count
;
192 mutex_exit(&chan
->ic_sq_post_lock
);
194 ISER_LOG(CE_NOTE
, "Posting iSER HelloReply message: chan (0x%p): "
195 "IP [%x to %x]", (void *)chan
, chan
->ic_localip
.un
.ip4addr
,
196 chan
->ic_remoteip
.un
.ip4addr
);
198 return (ISER_STATUS_SUCCESS
);
204 * This is iSER's implementation of the 'Send_control' operational primitive.
205 * This iSER layer uses the Send Message type of RCaP to transfer the iSCSI
206 * Control-type PDU. A special case is that the transfer of SCSI Data-out PDUs
207 * carrying unsolicited data are also treated as iSCSI Control-Type PDUs. The
208 * message payload contains an iSER header followed by the iSCSI Control-type
209 * the iSCSI Control-type PDU.
210 * This function is invoked by an initiator iSCSI layer requesting the transfer
211 * of a iSCSI command PDU or a target iSCSI layer requesting the transfer of a
212 * iSCSI response PDU.
215 iser_xfer_ctrlpdu(iser_chan_t
*chan
, idm_pdu_t
*pdu
)
218 iser_ctrl_hdr_t
*hdr
;
224 iscsi_data_hdr_t
*bhs
;
226 idm_task_t
*idt
= NULL
;
229 ASSERT(chan
!= NULL
);
232 * All SCSI command PDU (except SCSI Read and SCSI Write) and the SCSI
233 * Response PDU are sent to the remote end using the SendSE Message.
235 * Setup a Send Message for carrying the iSCSI control-type PDU
236 * preceeded by an iSER header.
238 hca
= (iser_hca_t
*)chan
->ic_hca
;
240 ISER_LOG(CE_NOTE
, "iser_xfer_ctrlpdu: no hca handle found");
241 return (ISER_STATUS_FAIL
);
244 msg
= iser_msg_get(hca
, 1, NULL
);
246 ISER_LOG(CE_NOTE
, "iser_xfer_ctrlpdu: iser message cache "
248 return (ISER_STATUS_FAIL
);
251 /* Pull the BHS out of the PDU handle */
252 bhs
= (iscsi_data_hdr_t
*)pdu
->isp_hdr
;
254 ASSERT(chan
->ic_conn
!= NULL
&& chan
->ic_conn
->ic_idmc
!= NULL
);
255 ic
= chan
->ic_conn
->ic_idmc
;
258 hdr
= (iser_ctrl_hdr_t
*)(uintptr_t)msg
->msg_ds
.ds_va
;
261 * Initialize header assuming no transfers
263 bzero(hdr
, sizeof (*hdr
));
264 hdr
->opcode
= ISER_OPCODE_CTRL_TYPE_PDU
;
267 * On the initiator side, the task buffers will be used to identify
268 * if there are any buffers to be advertised
270 if ((ic
->ic_conn_type
== CONN_TYPE_INI
) &&
271 ((bhs
->opcode
& ISCSI_OPCODE_MASK
) == ISCSI_OP_SCSI_CMD
) &&
272 ((idt
= idm_task_find(ic
, bhs
->itt
, bhs
->ttt
)) != NULL
)) {
274 if (!list_is_empty(&idt
->idt_inbufv
)) {
275 buf
= idm_buf_find(&idt
->idt_inbufv
, 0);
278 mr
= (iser_mr_t
*)buf
->idb_reg_private
;
282 hdr
->rstag
= htonl(mr
->is_mrrkey
);
283 BE_OUT64(&hdr
->rva
, mr
->is_mrva
);
286 if (!list_is_empty(&idt
->idt_outbufv
)) {
287 buf
= idm_buf_find(&idt
->idt_outbufv
, 0);
290 mr
= (iser_mr_t
*)buf
->idb_reg_private
;
294 hdr
->wstag
= htonl(mr
->is_mrrkey
);
295 BE_OUT64(&hdr
->wva
, mr
->is_mrva
);
298 /* Release our reference on the task */
302 /* Copy the BHS after the iSER header */
304 (uint8_t *)(uintptr_t)msg
->msg_ds
.ds_va
+ ISER_HEADER_LENGTH
,
307 if (pdu
->isp_datalen
> 0) {
308 /* Copy the isp_data after the PDU header */
310 (uint8_t *)(uintptr_t)msg
->msg_ds
.ds_va
+
311 ISER_HEADER_LENGTH
+ pdu
->isp_hdrlen
,
314 /* Set the SGE's ds_len */
315 msg
->msg_ds
.ds_len
= ISER_HEADER_LENGTH
+ pdu
->isp_hdrlen
+
318 /* No data, so set the SGE's ds_len to the headers length */
319 msg
->msg_ds
.ds_len
= ISER_HEADER_LENGTH
+ pdu
->isp_hdrlen
;
323 * Build Work Request to be posted on the Send Queue.
325 bzero(&wr
, sizeof (wr
));
327 /* Allocate an iSER WR handle and tuck the msg and pdu into it */
328 iser_wr
= iser_wr_get();
329 if (iser_wr
== NULL
) {
330 ISER_LOG(CE_NOTE
, "iser_xfer_ctrlpdu: unable to allocate "
333 return (ISER_STATUS_FAIL
);
335 iser_wr
->iw_pdu
= pdu
;
336 iser_wr
->iw_msg
= msg
;
337 iser_wr
->iw_type
= ISER_WR_SEND
;
340 * Use the address of our generic iser_wr handle as our WRID
341 * and populate the rest of the work request
343 wr
.wr_id
= (ibt_wrid_t
)(uintptr_t)iser_wr
;
344 wr
.wr_trans
= IBT_RC_SRV
;
345 wr
.wr_opcode
= IBT_WRC_SEND
;
347 wr
.wr_sgl
= &msg
->msg_ds
;
349 /* Post Send Work Request on the specified channel */
350 status
= ibt_post_send(chan
->ic_chanhdl
, &wr
, 1, NULL
);
351 if (status
!= IBT_SUCCESS
) {
352 ISER_LOG(CE_NOTE
, "iser_xfer_ctrlpdu: ibt_post_send "
353 "failure (%d)", status
);
355 iser_wr_free(iser_wr
);
356 return (ISER_STATUS_FAIL
);
358 /* Increment this channel's SQ posted count */
359 mutex_enter(&chan
->ic_sq_post_lock
);
360 chan
->ic_sq_post_count
++;
361 if (chan
->ic_sq_post_count
> chan
->ic_sq_max_post_count
)
362 chan
->ic_sq_max_post_count
= chan
->ic_sq_post_count
;
363 mutex_exit(&chan
->ic_sq_post_lock
);
365 return (ISER_STATUS_SUCCESS
);
369 * iser_xfer_buf_to_ini
370 * This is iSER's implementation of the 'Put_Data' operational primitive.
371 * The iSCSI layer at the target invokes this function when it is ready to
372 * return the SCSI Read Data to the initiator. This function generates and
373 * sends an RDMA Write Message containing the read data to the initiator.
376 iser_xfer_buf_to_ini(idm_task_t
*idt
, idm_buf_t
*buf
)
378 iser_conn_t
*iser_conn
;
379 iser_chan_t
*iser_chan
;
380 iser_buf_t
*iser_buf
;
382 iser_ctrl_hdr_t
*iser_hdr
;
388 /* Grab the iSER resources from the task and buf handles */
389 iser_conn
= (iser_conn_t
*)idt
->idt_ic
->ic_transport_private
;
390 iser_chan
= iser_conn
->ic_chan
;
391 iser_buf
= (iser_buf_t
*)buf
->idb_buf_private
;
392 iser_hdr
= (iser_ctrl_hdr_t
*)idt
->idt_transport_hdr
;
394 /* Pull the Read STag data out of the iSER header in the task hdl */
395 reg_raddr
= BE_IN64(&iser_hdr
->rva
);
396 reg_rkey
= (ntohl(iser_hdr
->rstag
));
398 /* Set up the WR raddr and rkey based upon the Read iSER STag */
399 bzero(&wr
, sizeof (ibt_send_wr_t
));
400 wr
.wr
.rc
.rcwr
.rdma
.rdma_raddr
= reg_raddr
+ buf
->idb_bufoffset
;
401 wr
.wr
.rc
.rcwr
.rdma
.rdma_rkey
= reg_rkey
;
403 /* Set the transfer length from the IDM buf handle */
404 iser_buf
->buf_ds
.ds_len
= buf
->idb_xfer_len
;
406 /* Allocate an iSER WR handle and tuck the IDM buf handle into it */
407 iser_wr
= iser_wr_get();
408 if (iser_wr
== NULL
) {
409 ISER_LOG(CE_NOTE
, "iser_xfer_buf_to_ini: unable to allocate "
411 return (ISER_STATUS_FAIL
);
413 iser_wr
->iw_buf
= buf
;
414 iser_wr
->iw_type
= ISER_WR_RDMAW
;
416 /* Use the address of our generic iser_wr handle as our WRID */
417 wr
.wr_id
= (ibt_wrid_t
)(uintptr_t)iser_wr
;
419 /* Populate the rest of the work request */
420 wr
.wr_flags
= IBT_WR_SEND_SIGNAL
;
421 wr
.wr_trans
= IBT_RC_SRV
;
422 wr
.wr_opcode
= IBT_WRC_RDMAW
;
424 wr
.wr_sgl
= &iser_buf
->buf_ds
;
427 bcopy(&wr
, &iser_buf
->buf_wr
, sizeof (ibt_send_wr_t
));
430 DTRACE_ISCSI_8(xfer__start
, idm_conn_t
*, idt
->idt_ic
,
431 uintptr_t, buf
->idb_buf
, uint32_t, buf
->idb_bufoffset
,
432 uint64_t, reg_raddr
, uint32_t, buf
->idb_bufoffset
,
434 uint32_t, buf
->idb_xfer_len
, int, XFER_BUF_TX_TO_INI
);
436 status
= ibt_post_send(iser_chan
->ic_chanhdl
, &wr
, 1, NULL
);
437 if (status
!= IBT_SUCCESS
) {
438 ISER_LOG(CE_NOTE
, "iser_xfer_buf_to_ini: ibt_post_send "
439 "failure (%d)", status
);
440 iser_wr_free(iser_wr
);
441 return (ISER_STATUS_FAIL
);
443 /* Increment this channel's SQ posted count */
444 mutex_enter(&iser_chan
->ic_sq_post_lock
);
445 iser_chan
->ic_sq_post_count
++;
446 if (iser_chan
->ic_sq_post_count
> iser_chan
->ic_sq_max_post_count
)
447 iser_chan
->ic_sq_max_post_count
= iser_chan
->ic_sq_post_count
;
448 mutex_exit(&iser_chan
->ic_sq_post_lock
);
450 return (ISER_STATUS_SUCCESS
);
454 * iser_xfer_buf_from_ini
455 * This is iSER's implementation of the 'Get_Data' operational primitive.
456 * The iSCSI layer at the target invokes this function when it is ready to
457 * receive the SCSI Write Data from the initiator. This function generates and
458 * sends an RDMA Read Message to get the data from the initiator. No R2T PDUs
462 iser_xfer_buf_from_ini(idm_task_t
*idt
, idm_buf_t
*buf
)
464 iser_conn_t
*iser_conn
;
465 iser_chan_t
*iser_chan
;
466 iser_buf_t
*iser_buf
;
468 iser_ctrl_hdr_t
*iser_hdr
;
474 /* Grab the iSER resources from the task and buf handles */
475 iser_conn
= (iser_conn_t
*)idt
->idt_ic
->ic_transport_private
;
476 iser_chan
= iser_conn
->ic_chan
;
477 iser_buf
= (iser_buf_t
*)buf
->idb_buf_private
;
478 iser_hdr
= (iser_ctrl_hdr_t
*)idt
->idt_transport_hdr
;
480 /* Pull the Write STag data out of the iSER header in the task hdl */
481 reg_raddr
= BE_IN64(&iser_hdr
->wva
);
482 reg_rkey
= (ntohl(iser_hdr
->wstag
));
484 /* Set up the WR raddr and rkey based upon the iSER Write STag */
485 bzero(&wr
, sizeof (ibt_send_wr_t
));
486 wr
.wr
.rc
.rcwr
.rdma
.rdma_raddr
= reg_raddr
+ buf
->idb_bufoffset
;
487 wr
.wr
.rc
.rcwr
.rdma
.rdma_rkey
= reg_rkey
;
489 /* Set the transfer length from the IDM buf handle */
490 iser_buf
->buf_ds
.ds_len
= buf
->idb_xfer_len
;
492 /* Allocate an iSER WR handle and tuck the IDM buf handle into it */
493 iser_wr
= iser_wr_get();
494 if (iser_wr
== NULL
) {
495 ISER_LOG(CE_NOTE
, "iser_xfer_buf_from_ini: unable to allocate "
497 return (ISER_STATUS_FAIL
);
499 iser_wr
->iw_buf
= buf
;
500 iser_wr
->iw_type
= ISER_WR_RDMAR
;
502 /* Use the address of our generic iser_wr handle as our WRID */
503 wr
.wr_id
= (ibt_wrid_t
)(uintptr_t)iser_wr
;
505 /* Populate the rest of the work request */
506 wr
.wr_flags
= IBT_WR_SEND_SIGNAL
;
507 wr
.wr_trans
= IBT_RC_SRV
;
508 wr
.wr_opcode
= IBT_WRC_RDMAR
;
510 wr
.wr_sgl
= &iser_buf
->buf_ds
;
513 bcopy(&wr
, &iser_buf
->buf_wr
, sizeof (ibt_send_wr_t
));
516 DTRACE_ISCSI_8(xfer__start
, idm_conn_t
*, idt
->idt_ic
,
517 uintptr_t, buf
->idb_buf
, uint32_t, buf
->idb_bufoffset
,
518 uint64_t, reg_raddr
, uint32_t, buf
->idb_bufoffset
,
520 uint32_t, buf
->idb_xfer_len
, int, XFER_BUF_RX_FROM_INI
);
522 status
= ibt_post_send(iser_chan
->ic_chanhdl
, &wr
, 1, NULL
);
523 if (status
!= IBT_SUCCESS
) {
524 ISER_LOG(CE_NOTE
, "iser_xfer_buf_from_ini: ibt_post_send "
525 "failure (%d)", status
);
526 iser_wr_free(iser_wr
);
527 return (ISER_STATUS_FAIL
);
529 /* Increment this channel's SQ posted count */
530 mutex_enter(&iser_chan
->ic_sq_post_lock
);
531 iser_chan
->ic_sq_post_count
++;
532 if (iser_chan
->ic_sq_post_count
> iser_chan
->ic_sq_max_post_count
)
533 iser_chan
->ic_sq_max_post_count
= iser_chan
->ic_sq_post_count
;
534 mutex_exit(&iser_chan
->ic_sq_post_lock
);
536 return (ISER_STATUS_SUCCESS
);