xref: /illumos-gate/usr/src/uts/common/io/ib/clients/iser/iser_xfer.c (revision a82ec3cf2b07d0f2ebb2e60f41370b7c39a5e71e)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/conf.h>
29 #include <sys/ddi.h>
30 #include <sys/sunddi.h>
31 #include <sys/modctl.h>
32 #include <sys/byteorder.h>
33 #include <sys/sdt.h>
34 
35 #include <sys/ib/clients/iser/iser.h>
36 
37 /*
38  * iser_xfer.c
39  */
40 
41 int
42 iser_xfer_hello_msg(iser_chan_t *chan)
43 {
44 	iser_hca_t		*hca;
45 	iser_wr_t		*iser_wr;
46 	iser_msg_t		*msg;
47 	ibt_send_wr_t		wr;
48 	iser_hello_hdr_t	*hdr;
49 	int			status;
50 
51 	ASSERT(chan != NULL);
52 
53 	hca = (iser_hca_t *)chan->ic_hca;
54 	if (hca == NULL) {
55 		ISER_LOG(CE_NOTE, "iser_xfer_hello_msg: no hca handle found");
56 		return (ISER_STATUS_FAIL);
57 	}
58 
59 	msg = iser_msg_get(hca, 1, NULL);
60 
61 	if (msg == NULL) {
62 		ISER_LOG(CE_NOTE, "iser_xfer_hello_msg: iser message cache "
63 		    "alloc failed");
64 		return (ISER_STATUS_FAIL);
65 	}
66 
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;
69 
70 	hdr->opcode	= ISER_OPCODE_HELLO_MSG;
71 	hdr->rsvd1	= 0;
72 	hdr->maxver 	= 1;
73 	hdr->minver 	= 1;
74 	hdr->iser_ird 	= htons(ISER_IB_DEFAULT_IRD);
75 	hdr->rsvd2[0] 	= 0;
76 	hdr->rsvd2[1] 	= 0;
77 
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 "
82 		    "iser wr handle");
83 		iser_msg_free(msg);
84 		return (ISER_STATUS_FAIL);
85 	}
86 	iser_wr->iw_msg = msg;
87 	iser_wr->iw_type = ISER_WR_SEND;
88 
89 	/* Use the address of our generic iser_wr handle as our WRID */
90 	wr.wr_id	= (ibt_wrid_t)(uintptr_t)iser_wr;
91 
92 	/* Populate the rest of the work request */
93 	wr.wr_trans	= IBT_RC_SRV;
94 	wr.wr_opcode	= IBT_WRC_SEND;
95 	wr.wr_nds	= 1;
96 	wr.wr_sgl	= &msg->msg_ds;
97 
98 	/*
99 	 * Avoid race condition by incrementing this channel's
100 	 * SQ posted count prior to calling ibt_post_send
101 	 */
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);
107 
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);
115 		iser_msg_free(msg);
116 		iser_wr_free(iser_wr);
117 		return (ISER_STATUS_FAIL);
118 	}
119 
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);
123 
124 	return (ISER_STATUS_SUCCESS);
125 }
126 
127 int
128 iser_xfer_helloreply_msg(iser_chan_t *chan)
129 {
130 	iser_hca_t		*hca;
131 	iser_wr_t		*iser_wr;
132 	ibt_send_wr_t   	wr;
133 	iser_msg_t		*msg;
134 	iser_helloreply_hdr_t	*hdr;
135 	int			status;
136 
137 	ASSERT(chan != NULL);
138 
139 	hca = (iser_hca_t *)chan->ic_hca;
140 	if (hca == NULL) {
141 		ISER_LOG(CE_NOTE, "iser_xfer_helloreply_msg: no hca handle "
142 		    "found");
143 		return (ISER_STATUS_FAIL);
144 	}
145 
146 	msg = iser_msg_get(hca, 1, NULL);
147 
148 	if (msg == NULL) {
149 		ISER_LOG(CE_NOTE, "iser_xfer_helloreply_msg: iser message "
150 		    "cache alloc failed");
151 		return (ISER_STATUS_FAIL);
152 	}
153 
154 	/* Use the iSER Hello Reply Message */
155 	hdr = (iser_helloreply_hdr_t *)(uintptr_t)msg->msg_ds.ds_va;
156 
157 	hdr->opcode	= ISER_OPCODE_HELLOREPLY_MSG;
158 	hdr->rsvd1	= 0;
159 	hdr->flag	= 0;
160 	hdr->maxver	= 1;
161 	hdr->curver	= 1;
162 	hdr->iser_ord	= htons(ISER_IB_DEFAULT_ORD);
163 	hdr->rsvd2[0]	= 0;
164 	hdr->rsvd2[1]	= 0;
165 
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");
171 		iser_msg_free(msg);
172 		return (ISER_STATUS_FAIL);
173 	}
174 	iser_wr->iw_msg = msg;
175 	iser_wr->iw_type = ISER_WR_SEND;
176 
177 	/* Use the address of our generic iser_wr handle as our WRID */
178 	wr.wr_id	= (ibt_wrid_t)(uintptr_t)iser_wr;
179 
180 	/* Populate the rest of the work request */
181 	wr.wr_trans	= IBT_RC_SRV;
182 	wr.wr_opcode	= IBT_WRC_SEND;
183 	wr.wr_nds	= 1;
184 	wr.wr_sgl	= &msg->msg_ds;
185 
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;
190 
191 	mutex_exit(&chan->ic_sq_post_lock);
192 
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);
200 		iser_msg_free(msg);
201 		iser_wr_free(iser_wr);
202 		return (ISER_STATUS_FAIL);
203 	}
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);
207 
208 	return (ISER_STATUS_SUCCESS);
209 }
210 
211 /*
212  * iser_xfer_ctrlpdu
213  *
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.
223  */
224 int
225 iser_xfer_ctrlpdu(iser_chan_t *chan, idm_pdu_t *pdu)
226 {
227 	iser_hca_t	*hca;
228 	iser_ctrl_hdr_t	*hdr;
229 	iser_msg_t	*msg;
230 	iser_wr_t	*iser_wr;
231 	ibt_send_wr_t   wr;
232 	int		status;
233 	iser_mr_t	*mr;
234 	iscsi_data_hdr_t	*bhs;
235 	idm_conn_t	*ic;
236 	idm_task_t	*idt = NULL;
237 	idm_buf_t	*buf;
238 
239 	ASSERT(chan != NULL);
240 
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);
247 	}
248 
249 	/*
250 	 * All SCSI command PDU (except SCSI Read and SCSI Write) and the SCSI
251 	 * Response PDU are sent to the remote end using the SendSE Message.
252 	 *
253 	 * Setup a Send Message for carrying the iSCSI control-type PDU
254 	 * preceeded by an iSER header.
255 	 */
256 	hca = (iser_hca_t *)chan->ic_hca;
257 	if (hca == NULL) {
258 		ISER_LOG(CE_NOTE, "iser_xfer_ctrlpdu: no hca handle found");
259 		mutex_exit(&chan->ic_conn->ic_lock);
260 		return (ISER_STATUS_FAIL);
261 	}
262 
263 	msg = iser_msg_get(hca, 1, NULL);
264 	if (msg == NULL) {
265 		ISER_LOG(CE_NOTE, "iser_xfer_ctrlpdu: iser message cache "
266 		    "alloc failed");
267 		mutex_exit(&chan->ic_conn->ic_lock);
268 		return (ISER_STATUS_FAIL);
269 	}
270 
271 	/* Pull the BHS out of the PDU handle */
272 	bhs = (iscsi_data_hdr_t *)pdu->isp_hdr;
273 
274 	ic = chan->ic_conn->ic_idmc;
275 
276 	hdr = (iser_ctrl_hdr_t *)(uintptr_t)msg->msg_ds.ds_va;
277 
278 	/*
279 	 * Initialize header assuming no transfers
280 	 */
281 	bzero(hdr, sizeof (*hdr));
282 	hdr->opcode	= ISER_OPCODE_CTRL_TYPE_PDU;
283 
284 	/*
285 	 * On the initiator side, the task buffers will be used to identify
286 	 * if there are any buffers to be advertised
287 	 */
288 	if ((ic->ic_conn_type == CONN_TYPE_INI) &&
289 	    ((bhs->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_SCSI_CMD) &&
290 	    ((idt = idm_task_find(ic, bhs->itt, bhs->ttt)) != NULL)) {
291 
292 		if (!list_is_empty(&idt->idt_inbufv)) {
293 			buf = idm_buf_find(&idt->idt_inbufv, 0);
294 			ASSERT(buf != NULL);
295 
296 			mr = (iser_mr_t *)buf->idb_reg_private;
297 			ASSERT(mr != NULL);
298 
299 			hdr->rsv_flag = 1;
300 			hdr->rstag = htonl(mr->is_mrrkey);
301 			BE_OUT64(&hdr->rva, mr->is_mrva);
302 		}
303 
304 		if (!list_is_empty(&idt->idt_outbufv)) {
305 			buf = idm_buf_find(&idt->idt_outbufv, 0);
306 			ASSERT(buf != NULL);
307 
308 			mr = (iser_mr_t *)buf->idb_reg_private;
309 			ASSERT(mr != NULL);
310 
311 			hdr->wsv_flag = 1;
312 			hdr->wstag = htonl(mr->is_mrrkey);
313 			BE_OUT64(&hdr->wva, mr->is_mrva);
314 		}
315 
316 		/* Release our reference on the task */
317 		idm_task_rele(idt);
318 	}
319 
320 	/* Copy the BHS after the iSER header */
321 	bcopy(pdu->isp_hdr,
322 	    (uint8_t *)(uintptr_t)msg->msg_ds.ds_va + ISER_HEADER_LENGTH,
323 	    pdu->isp_hdrlen);
324 
325 	if (pdu->isp_datalen > 0) {
326 		/* Copy the isp_data after the PDU header */
327 		bcopy(pdu->isp_data,
328 		    (uint8_t *)(uintptr_t)msg->msg_ds.ds_va +
329 		    ISER_HEADER_LENGTH + pdu->isp_hdrlen,
330 		    pdu->isp_datalen);
331 
332 		/* Set the SGE's ds_len */
333 		msg->msg_ds.ds_len = ISER_HEADER_LENGTH + pdu->isp_hdrlen +
334 		    pdu->isp_datalen;
335 	} else {
336 		/* No data, so set the SGE's ds_len to the headers length */
337 		msg->msg_ds.ds_len = ISER_HEADER_LENGTH + pdu->isp_hdrlen;
338 	}
339 
340 	/*
341 	 * Build Work Request to be posted on the Send Queue.
342 	 */
343 	bzero(&wr, sizeof (wr));
344 
345 	/* Allocate an iSER WR handle and tuck the msg and pdu into it */
346 	iser_wr = iser_wr_get();
347 	if (iser_wr == NULL) {
348 		ISER_LOG(CE_NOTE, "iser_xfer_ctrlpdu: unable to allocate "
349 		    "iser wr handle");
350 		iser_msg_free(msg);
351 		mutex_exit(&chan->ic_conn->ic_lock);
352 		return (ISER_STATUS_FAIL);
353 	}
354 	iser_wr->iw_pdu = pdu;
355 	iser_wr->iw_msg = msg;
356 	iser_wr->iw_type = ISER_WR_SEND;
357 
358 	/*
359 	 * Use the address of our generic iser_wr handle as our WRID
360 	 * and populate the rest of the work request
361 	 */
362 	wr.wr_id	= (ibt_wrid_t)(uintptr_t)iser_wr;
363 	wr.wr_trans	= IBT_RC_SRV;
364 	wr.wr_opcode	= IBT_WRC_SEND;
365 	wr.wr_nds	= 1;
366 	wr.wr_sgl	= &msg->msg_ds;
367 
368 	/* Increment this channel's SQ posted count */
369 	mutex_enter(&chan->ic_sq_post_lock);
370 	chan->ic_sq_post_count++;
371 	if (chan->ic_sq_post_count > chan->ic_sq_max_post_count)
372 		chan->ic_sq_max_post_count = chan->ic_sq_post_count;
373 	mutex_exit(&chan->ic_sq_post_lock);
374 
375 	/* Post Send Work Request on the specified channel */
376 	status = ibt_post_send(chan->ic_chanhdl, &wr, 1, NULL);
377 	if (status != IBT_SUCCESS) {
378 		ISER_LOG(CE_NOTE, "iser_xfer_ctrlpdu: ibt_post_send "
379 		    "failure (%d)", status);
380 		iser_msg_free(msg);
381 		iser_wr_free(iser_wr);
382 		mutex_enter(&chan->ic_sq_post_lock);
383 		chan->ic_sq_post_count--;
384 		mutex_exit(&chan->ic_sq_post_lock);
385 		mutex_exit(&chan->ic_conn->ic_lock);
386 		return (ISER_STATUS_FAIL);
387 	}
388 
389 	mutex_exit(&chan->ic_conn->ic_lock);
390 	return (ISER_STATUS_SUCCESS);
391 }
392 
393 /*
394  * iser_xfer_buf_to_ini
395  * This is iSER's implementation of the 'Put_Data' operational primitive.
396  * The iSCSI layer at the target invokes this function when it is ready to
397  * return the SCSI Read Data to the initiator. This function generates and
398  * sends an RDMA Write Message containing the read data to the initiator.
399  */
400 int
401 iser_xfer_buf_to_ini(idm_task_t *idt, idm_buf_t *buf)
402 {
403 	iser_conn_t	*iser_conn;
404 	iser_chan_t	*iser_chan;
405 	iser_buf_t	*iser_buf;
406 	iser_wr_t	*iser_wr;
407 	iser_ctrl_hdr_t	*iser_hdr;
408 	ibt_send_wr_t	wr;
409 	uint64_t	reg_raddr;
410 	uint32_t	reg_rkey;
411 	int		status;
412 
413 	/* Grab the iSER resources from the task and buf handles */
414 	iser_conn = (iser_conn_t *)idt->idt_ic->ic_transport_private;
415 	iser_chan = iser_conn->ic_chan;
416 
417 	mutex_enter(&iser_chan->ic_conn->ic_lock);
418 	/* Bail out if the connection is closed */
419 	if ((iser_chan->ic_conn->ic_stage == ISER_CONN_STAGE_CLOSING) ||
420 	    (iser_chan->ic_conn->ic_stage == ISER_CONN_STAGE_CLOSED)) {
421 		mutex_exit(&iser_chan->ic_conn->ic_lock);
422 		return (ISER_STATUS_FAIL);
423 	}
424 
425 	iser_buf  = (iser_buf_t *)buf->idb_buf_private;
426 	iser_hdr  = (iser_ctrl_hdr_t *)idt->idt_transport_hdr;
427 
428 	/* Pull the Read STag data out of the iSER header in the task hdl */
429 	reg_raddr = BE_IN64(&iser_hdr->rva);
430 	reg_rkey  = (ntohl(iser_hdr->rstag));
431 
432 	/* Set up the WR raddr and rkey based upon the Read iSER STag */
433 	bzero(&wr, sizeof (ibt_send_wr_t));
434 	wr.wr.rc.rcwr.rdma.rdma_raddr = reg_raddr + buf->idb_bufoffset;
435 	wr.wr.rc.rcwr.rdma.rdma_rkey  = reg_rkey;
436 
437 	/* Set the transfer length from the IDM buf handle */
438 	iser_buf->buf_ds.ds_len	= buf->idb_xfer_len;
439 
440 	/* Allocate an iSER WR handle and tuck the IDM buf handle into it */
441 	iser_wr = iser_wr_get();
442 	if (iser_wr == NULL) {
443 		ISER_LOG(CE_NOTE, "iser_xfer_buf_to_ini: unable to allocate "
444 		    "iser wr handle");
445 		mutex_exit(&iser_chan->ic_conn->ic_lock);
446 		return (ISER_STATUS_FAIL);
447 	}
448 	iser_wr->iw_buf = buf;
449 	iser_wr->iw_type = ISER_WR_RDMAW;
450 
451 	/* Use the address of our generic iser_wr handle as our WRID */
452 	wr.wr_id	= (ibt_wrid_t)(uintptr_t)iser_wr;
453 
454 	/* Populate the rest of the work request */
455 	wr.wr_flags	= IBT_WR_SEND_SIGNAL;
456 	wr.wr_trans	= IBT_RC_SRV;
457 	wr.wr_opcode	= IBT_WRC_RDMAW;
458 	wr.wr_nds	= 1;
459 	wr.wr_sgl	= &iser_buf->buf_ds;
460 
461 #ifdef DEBUG
462 	bcopy(&wr, &iser_buf->buf_wr, sizeof (ibt_send_wr_t));
463 #endif
464 
465 	DTRACE_ISCSI_8(xfer__start, idm_conn_t *, idt->idt_ic,
466 	    uintptr_t, buf->idb_buf, uint32_t, buf->idb_bufoffset,
467 	    uint64_t, reg_raddr, uint32_t, buf->idb_bufoffset,
468 	    uint32_t,  reg_rkey,
469 	    uint32_t, buf->idb_xfer_len, int, XFER_BUF_TX_TO_INI);
470 
471 	/* Increment this channel's SQ posted count */
472 	mutex_enter(&iser_chan->ic_sq_post_lock);
473 	iser_chan->ic_sq_post_count++;
474 	if (iser_chan->ic_sq_post_count > iser_chan->ic_sq_max_post_count)
475 		iser_chan->ic_sq_max_post_count = iser_chan->ic_sq_post_count;
476 	mutex_exit(&iser_chan->ic_sq_post_lock);
477 
478 	status = ibt_post_send(iser_chan->ic_chanhdl, &wr, 1, NULL);
479 	if (status != IBT_SUCCESS) {
480 		ISER_LOG(CE_NOTE, "iser_xfer_buf_to_ini: ibt_post_send "
481 		    "failure (%d)", status);
482 		iser_wr_free(iser_wr);
483 		mutex_enter(&iser_chan->ic_sq_post_lock);
484 		iser_chan->ic_sq_post_count--;
485 		mutex_exit(&iser_chan->ic_sq_post_lock);
486 		mutex_exit(&iser_chan->ic_conn->ic_lock);
487 		return (ISER_STATUS_FAIL);
488 	}
489 
490 	mutex_exit(&iser_chan->ic_conn->ic_lock);
491 	return (ISER_STATUS_SUCCESS);
492 }
493 
494 /*
495  * iser_xfer_buf_from_ini
496  * This is iSER's implementation of the 'Get_Data' operational primitive.
497  * The iSCSI layer at the target invokes this function when it is ready to
498  * receive the SCSI Write Data from the initiator. This function generates and
499  * sends an RDMA Read Message to get the data from the initiator. No R2T PDUs
500  * are generated.
501  */
502 int
503 iser_xfer_buf_from_ini(idm_task_t *idt, idm_buf_t *buf)
504 {
505 	iser_conn_t	*iser_conn;
506 	iser_chan_t	*iser_chan;
507 	iser_buf_t	*iser_buf;
508 	iser_wr_t	*iser_wr;
509 	iser_ctrl_hdr_t	*iser_hdr;
510 	ibt_send_wr_t	wr;
511 	uint64_t	reg_raddr;
512 	uint32_t	reg_rkey;
513 	int		status;
514 
515 	/* Grab the iSER resources from the task and buf handles */
516 	iser_conn = (iser_conn_t *)idt->idt_ic->ic_transport_private;
517 	iser_chan = iser_conn->ic_chan;
518 
519 	mutex_enter(&iser_chan->ic_conn->ic_lock);
520 	/* Bail out if the connection is closed */
521 	if ((iser_chan->ic_conn->ic_stage == ISER_CONN_STAGE_CLOSING) ||
522 	    (iser_chan->ic_conn->ic_stage == ISER_CONN_STAGE_CLOSED)) {
523 		mutex_exit(&iser_chan->ic_conn->ic_lock);
524 		return (ISER_STATUS_FAIL);
525 	}
526 
527 	iser_buf = (iser_buf_t *)buf->idb_buf_private;
528 	iser_hdr  = (iser_ctrl_hdr_t *)idt->idt_transport_hdr;
529 
530 	/* Pull the Write STag data out of the iSER header in the task hdl */
531 	reg_raddr = BE_IN64(&iser_hdr->wva);
532 	reg_rkey  = (ntohl(iser_hdr->wstag));
533 
534 	/* Set up the WR raddr and rkey based upon the iSER Write STag */
535 	bzero(&wr, sizeof (ibt_send_wr_t));
536 	wr.wr.rc.rcwr.rdma.rdma_raddr = reg_raddr + buf->idb_bufoffset;
537 	wr.wr.rc.rcwr.rdma.rdma_rkey  = reg_rkey;
538 
539 	/* Set the transfer length from the IDM buf handle */
540 	iser_buf->buf_ds.ds_len	= buf->idb_xfer_len;
541 
542 	/* Allocate an iSER WR handle and tuck the IDM buf handle into it */
543 	iser_wr = iser_wr_get();
544 	if (iser_wr == NULL) {
545 		ISER_LOG(CE_NOTE, "iser_xfer_buf_from_ini: unable to allocate "
546 		    "iser wr handle");
547 		mutex_exit(&iser_chan->ic_conn->ic_lock);
548 		return (ISER_STATUS_FAIL);
549 	}
550 	iser_wr->iw_buf = buf;
551 	iser_wr->iw_type = ISER_WR_RDMAR;
552 
553 	/* Use the address of our generic iser_wr handle as our WRID */
554 	wr.wr_id	= (ibt_wrid_t)(uintptr_t)iser_wr;
555 
556 	/* Populate the rest of the work request */
557 	wr.wr_flags	= IBT_WR_SEND_SIGNAL;
558 	wr.wr_trans	= IBT_RC_SRV;
559 	wr.wr_opcode	= IBT_WRC_RDMAR;
560 	wr.wr_nds	= 1;
561 	wr.wr_sgl	= &iser_buf->buf_ds;
562 
563 #ifdef DEBUG
564 	bcopy(&wr, &iser_buf->buf_wr, sizeof (ibt_send_wr_t));
565 #endif
566 
567 	DTRACE_ISCSI_8(xfer__start, idm_conn_t *, idt->idt_ic,
568 	    uintptr_t, buf->idb_buf, uint32_t, buf->idb_bufoffset,
569 	    uint64_t, reg_raddr, uint32_t, buf->idb_bufoffset,
570 	    uint32_t,  reg_rkey,
571 	    uint32_t, buf->idb_xfer_len, int, XFER_BUF_RX_FROM_INI);
572 
573 	/* Increment this channel's SQ posted count */
574 	mutex_enter(&iser_chan->ic_sq_post_lock);
575 	iser_chan->ic_sq_post_count++;
576 	if (iser_chan->ic_sq_post_count > iser_chan->ic_sq_max_post_count)
577 		iser_chan->ic_sq_max_post_count = iser_chan->ic_sq_post_count;
578 	mutex_exit(&iser_chan->ic_sq_post_lock);
579 
580 	status = ibt_post_send(iser_chan->ic_chanhdl, &wr, 1, NULL);
581 	if (status != IBT_SUCCESS) {
582 		ISER_LOG(CE_NOTE, "iser_xfer_buf_from_ini: ibt_post_send "
583 		    "failure (%d)", status);
584 		iser_wr_free(iser_wr);
585 		mutex_enter(&iser_chan->ic_sq_post_lock);
586 		iser_chan->ic_sq_post_count--;
587 		mutex_exit(&iser_chan->ic_sq_post_lock);
588 		mutex_exit(&iser_chan->ic_conn->ic_lock);
589 		return (ISER_STATUS_FAIL);
590 	}
591 
592 	mutex_exit(&iser_chan->ic_conn->ic_lock);
593 	return (ISER_STATUS_SUCCESS);
594 }
595