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
iser_xfer_hello_msg(iser_chan_t * chan)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
iser_xfer_helloreply_msg(iser_chan_t * chan)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
iser_xfer_ctrlpdu(iser_chan_t * chan,idm_pdu_t * pdu)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 	ic = chan->ic_conn->ic_idmc;
250 
251 	/* Pull the BHS out of the PDU handle */
252 	bhs = (iscsi_data_hdr_t *)pdu->isp_hdr;
253 
254 	/*
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.
257 	 *
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.
260 	 */
261 	if (pdu->isp_flags & IDM_PDU_SET_STATSN) {
262 		(ic->ic_conn_ops.icb_update_statsn)(NULL, pdu);
263 	}
264 	/*
265 	 * Setup a Send Message for carrying the iSCSI control-type PDU
266 	 * preceeded by an iSER header.
267 	 */
268 	hca = (iser_hca_t *)chan->ic_hca;
269 	if (hca == NULL) {
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);
273 	}
274 
275 	msg = iser_msg_get(hca, 1, NULL);
276 	if (msg == NULL) {
277 		ISER_LOG(CE_NOTE, "iser_xfer_ctrlpdu: iser message cache "
278 		    "alloc failed");
279 		mutex_exit(&chan->ic_conn->ic_lock);
280 		return (ISER_STATUS_FAIL);
281 	}
282 
283 	hdr = (iser_ctrl_hdr_t *)(uintptr_t)msg->msg_ds.ds_va;
284 
285 	/*
286 	 * Initialize header assuming no transfers
287 	 */
288 	bzero(hdr, sizeof (*hdr));
289 	hdr->opcode	= ISER_OPCODE_CTRL_TYPE_PDU;
290 
291 	/*
292 	 * On the initiator side, the task buffers will be used to identify
293 	 * if there are any buffers to be advertised
294 	 */
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)) {
298 
299 		if (!list_is_empty(&idt->idt_inbufv)) {
300 			buf = idm_buf_find(&idt->idt_inbufv, 0);
301 			ASSERT(buf != NULL);
302 
303 			mr = (iser_mr_t *)buf->idb_reg_private;
304 			ASSERT(mr != NULL);
305 
306 			hdr->rsv_flag = 1;
307 			hdr->rstag = htonl(mr->is_mrrkey);
308 			BE_OUT64(&hdr->rva, mr->is_mrva);
309 		}
310 
311 		if (!list_is_empty(&idt->idt_outbufv)) {
312 			buf = idm_buf_find(&idt->idt_outbufv, 0);
313 			ASSERT(buf != NULL);
314 
315 			mr = (iser_mr_t *)buf->idb_reg_private;
316 			ASSERT(mr != NULL);
317 
318 			hdr->wsv_flag = 1;
319 			hdr->wstag = htonl(mr->is_mrrkey);
320 			BE_OUT64(&hdr->wva, mr->is_mrva);
321 		}
322 
323 		/* Release our reference on the task */
324 		idm_task_rele(idt);
325 	}
326 
327 	/* Copy the BHS after the iSER header */
328 	bcopy(pdu->isp_hdr,
329 	    (uint8_t *)(uintptr_t)msg->msg_ds.ds_va + ISER_HEADER_LENGTH,
330 	    pdu->isp_hdrlen);
331 
332 	if (pdu->isp_datalen > 0) {
333 		/* Copy the isp_data after the PDU header */
334 		bcopy(pdu->isp_data,
335 		    (uint8_t *)(uintptr_t)msg->msg_ds.ds_va +
336 		    ISER_HEADER_LENGTH + pdu->isp_hdrlen,
337 		    pdu->isp_datalen);
338 
339 		/* Set the SGE's ds_len */
340 		msg->msg_ds.ds_len = ISER_HEADER_LENGTH + pdu->isp_hdrlen +
341 		    pdu->isp_datalen;
342 	} else {
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;
345 	}
346 
347 	/*
348 	 * Build Work Request to be posted on the Send Queue.
349 	 */
350 	bzero(&wr, sizeof (wr));
351 
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 "
356 		    "iser wr handle");
357 		iser_msg_free(msg);
358 		mutex_exit(&chan->ic_conn->ic_lock);
359 		return (ISER_STATUS_FAIL);
360 	}
361 	iser_wr->iw_pdu = pdu;
362 	iser_wr->iw_msg = msg;
363 	iser_wr->iw_type = ISER_WR_SEND;
364 
365 	/*
366 	 * Use the address of our generic iser_wr handle as our WRID
367 	 * and populate the rest of the work request
368 	 */
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;
372 	wr.wr_nds	= 1;
373 	wr.wr_sgl	= &msg->msg_ds;
374 
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);
381 
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);
387 		iser_msg_free(msg);
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);
394 	}
395 
396 	mutex_exit(&chan->ic_conn->ic_lock);
397 	return (ISER_STATUS_SUCCESS);
398 }
399 
400 /*
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.
406  */
407 int
iser_xfer_buf_to_ini(idm_task_t * idt,idm_buf_t * buf)408 iser_xfer_buf_to_ini(idm_task_t *idt, idm_buf_t *buf)
409 {
410 	iser_conn_t	*iser_conn;
411 	iser_chan_t	*iser_chan;
412 	iser_buf_t	*iser_buf;
413 	iser_wr_t	*iser_wr;
414 	iser_ctrl_hdr_t	*iser_hdr;
415 	ibt_send_wr_t	wr;
416 	uint64_t	reg_raddr;
417 	uint32_t	reg_rkey;
418 	int		status;
419 
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;
423 
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);
430 	}
431 
432 	iser_buf  = (iser_buf_t *)buf->idb_buf_private;
433 	iser_hdr  = (iser_ctrl_hdr_t *)idt->idt_transport_hdr;
434 
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));
438 
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;
443 
444 	/* Set the transfer length from the IDM buf handle */
445 	iser_buf->buf_ds.ds_len	= buf->idb_xfer_len;
446 
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 "
451 		    "iser wr handle");
452 		mutex_exit(&iser_chan->ic_conn->ic_lock);
453 		return (ISER_STATUS_FAIL);
454 	}
455 	iser_wr->iw_buf = buf;
456 	iser_wr->iw_type = ISER_WR_RDMAW;
457 
458 	/* Use the address of our generic iser_wr handle as our WRID */
459 	wr.wr_id	= (ibt_wrid_t)(uintptr_t)iser_wr;
460 
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;
465 	wr.wr_nds	= 1;
466 	wr.wr_sgl	= &iser_buf->buf_ds;
467 
468 #ifdef DEBUG
469 	bcopy(&wr, &iser_buf->buf_wr, sizeof (ibt_send_wr_t));
470 #endif
471 
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,
475 	    uint32_t,  reg_rkey,
476 	    uint32_t, buf->idb_xfer_len, int, XFER_BUF_TX_TO_INI);
477 
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);
484 
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);
495 	}
496 
497 	mutex_exit(&iser_chan->ic_conn->ic_lock);
498 	return (ISER_STATUS_SUCCESS);
499 }
500 
501 /*
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
507  * are generated.
508  */
509 int
iser_xfer_buf_from_ini(idm_task_t * idt,idm_buf_t * buf)510 iser_xfer_buf_from_ini(idm_task_t *idt, idm_buf_t *buf)
511 {
512 	iser_conn_t	*iser_conn;
513 	iser_chan_t	*iser_chan;
514 	iser_buf_t	*iser_buf;
515 	iser_wr_t	*iser_wr;
516 	iser_ctrl_hdr_t	*iser_hdr;
517 	ibt_send_wr_t	wr;
518 	uint64_t	reg_raddr;
519 	uint32_t	reg_rkey;
520 	int		status;
521 
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;
525 
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);
532 	}
533 
534 	iser_buf = (iser_buf_t *)buf->idb_buf_private;
535 	iser_hdr  = (iser_ctrl_hdr_t *)idt->idt_transport_hdr;
536 
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));
540 
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;
545 
546 	/* Set the transfer length from the IDM buf handle */
547 	iser_buf->buf_ds.ds_len	= buf->idb_xfer_len;
548 
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 "
553 		    "iser wr handle");
554 		mutex_exit(&iser_chan->ic_conn->ic_lock);
555 		return (ISER_STATUS_FAIL);
556 	}
557 	iser_wr->iw_buf = buf;
558 	iser_wr->iw_type = ISER_WR_RDMAR;
559 
560 	/* Use the address of our generic iser_wr handle as our WRID */
561 	wr.wr_id	= (ibt_wrid_t)(uintptr_t)iser_wr;
562 
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;
567 	wr.wr_nds	= 1;
568 	wr.wr_sgl	= &iser_buf->buf_ds;
569 
570 #ifdef DEBUG
571 	bcopy(&wr, &iser_buf->buf_wr, sizeof (ibt_send_wr_t));
572 #endif
573 
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,
577 	    uint32_t,  reg_rkey,
578 	    uint32_t, buf->idb_xfer_len, int, XFER_BUF_RX_FROM_INI);
579 
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);
586 
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);
597 	}
598 
599 	mutex_exit(&iser_chan->ic_conn->ic_lock);
600 	return (ISER_STATUS_SUCCESS);
601 }
602