/* * Deliver read data back to initiator. * XXX TBD handle resource problems later. */ int ft_queue_data_in(struct se_cmd *se_cmd) { struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); struct fc_frame *fp = NULL; struct fc_exch *ep; struct fc_lport *lport; struct scatterlist *sg = NULL; size_t remaining; u32 f_ctl = FC_FC_EX_CTX | FC_FC_REL_OFF; u32 mem_off = 0; u32 fh_off = 0; u32 frame_off = 0; size_t frame_len = 0; size_t mem_len = 0; size_t tlen; size_t off_in_page; struct page *page = NULL; int use_sg; int error; void *page_addr; void *from; void *to = NULL; if (cmd->aborted) return 0; if (se_cmd->scsi_status == SAM_STAT_TASK_SET_FULL) goto queue_status; ep = fc_seq_exch(cmd->seq); lport = ep->lp; cmd->seq = lport->tt.seq_start_next(cmd->seq); remaining = se_cmd->data_length; /* * Setup to use first mem list entry, unless no data. */ BUG_ON(remaining && !se_cmd->t_data_sg); if (remaining) { sg = se_cmd->t_data_sg; mem_len = sg->length; mem_off = sg->offset; page = sg_page(sg); } /* no scatter/gather in skb for odd word length due to fc_seq_send() */ use_sg = !(remaining % 4); while (remaining) { struct fc_seq *seq = cmd->seq; if (!seq) { pr_debug("%s: Command aborted, xid 0x%x\n", __func__, ep->xid); break; } if (!mem_len) { sg = sg_next(sg); mem_len = min((size_t)sg->length, remaining); mem_off = sg->offset; page = sg_page(sg); } if (!frame_len) { /* * If lport's has capability of Large Send Offload LSO) * , then allow 'frame_len' to be as big as 'lso_max' * if indicated transfer length is >= lport->lso_max */ frame_len = (lport->seq_offload) ? lport->lso_max : cmd->sess->max_frame; frame_len = min(frame_len, remaining); fp = fc_frame_alloc(lport, use_sg ? 0 : frame_len); if (!fp) return -ENOMEM; to = fc_frame_payload_get(fp, 0); fh_off = frame_off; frame_off += frame_len; /* * Setup the frame's max payload which is used by base * driver to indicate HW about max frame size, so that * HW can do fragmentation appropriately based on * "gso_max_size" of underline netdev. */ fr_max_payload(fp) = cmd->sess->max_frame; } tlen = min(mem_len, frame_len); if (use_sg) { off_in_page = mem_off; BUG_ON(!page); get_page(page); skb_fill_page_desc(fp_skb(fp), skb_shinfo(fp_skb(fp))->nr_frags, page, off_in_page, tlen); fr_len(fp) += tlen; fp_skb(fp)->data_len += tlen; fp_skb(fp)->truesize += PAGE_SIZE << compound_order(page); } else { BUG_ON(!page); from = kmap_atomic(page + (mem_off >> PAGE_SHIFT)); page_addr = from; from += mem_off & ~PAGE_MASK; tlen = min(tlen, (size_t)(PAGE_SIZE - (mem_off & ~PAGE_MASK))); memcpy(to, from, tlen); kunmap_atomic(page_addr); to += tlen; } mem_off += tlen; mem_len -= tlen; frame_len -= tlen; remaining -= tlen; if (frame_len && (skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN)) continue; if (!remaining) f_ctl |= FC_FC_END_SEQ; fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid, FC_TYPE_FCP, f_ctl, fh_off); error = lport->tt.seq_send(lport, seq, fp); if (error) { pr_info_ratelimited("%s: Failed to send frame %p, " "xid <0x%x>, remaining %zu, " "lso_max <0x%x>\n", __func__, fp, ep->xid, remaining, lport->lso_max); /* * Go ahead and set TASK_SET_FULL status ignoring the * rest of the DataIN, and immediately attempt to * send the response via ft_queue_status() in order * to notify the initiator that it should reduce it's * per LUN queue_depth. */ se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL; break; } } queue_status: return ft_queue_status(se_cmd); }
/* * Send read data back to initiator. */ int ft_send_read_data(struct scst_cmd *cmd) { struct ft_cmd *fcmd; struct fc_frame *fp = NULL; struct fc_exch *ep; struct fc_lport *lport; size_t remaining; u32 fh_off = 0; u32 frame_off; size_t frame_len = 0; size_t mem_len; u32 mem_off; size_t tlen; struct page *page; int use_sg; int error; void *to = NULL; u8 *from = NULL; int loop_limit = 10000; fcmd = scst_cmd_get_tgt_priv(cmd); ep = fc_seq_exch(fcmd->seq); lport = ep->lp; frame_off = fcmd->read_data_len; tlen = scst_cmd_get_resp_data_len(cmd); FT_IO_DBG("oid %x oxid %x resp_len %zd frame_off %u\n", ep->oid, ep->oxid, tlen, frame_off); if (tlen <= frame_off) return SCST_TGT_RES_SUCCESS; remaining = tlen - frame_off; if (remaining > UINT_MAX) FT_ERR("oid %x oxid %x resp_len %zd frame_off %u\n", ep->oid, ep->oxid, tlen, frame_off); mem_len = scst_get_buf_first(cmd, &from); mem_off = 0; if (!mem_len) { FT_IO_DBG("mem_len 0\n"); return SCST_TGT_RES_SUCCESS; } FT_IO_DBG("sid %x oxid %x mem_len %zd frame_off %u remaining %zd\n", ep->sid, ep->oxid, mem_len, frame_off, remaining); /* * If we've already transferred some of the data, skip through * the buffer over the data already sent and continue with the * same sequence. Otherwise, get a new sequence for the data. */ if (frame_off) { tlen = frame_off; while (mem_len <= tlen) { tlen -= mem_len; scst_put_buf(cmd, from); mem_len = scst_get_buf_next(cmd, &from); if (!mem_len) return SCST_TGT_RES_SUCCESS; } mem_len -= tlen; mem_off = tlen; } else fcmd->seq = lport->tt.seq_start_next(fcmd->seq); /* no scatter/gather in skb for odd word length due to fc_seq_send() */ use_sg = !(remaining % 4) && lport->sg_supp; while (remaining) { if (!loop_limit) { FT_ERR("hit loop limit. remaining %zx mem_len %zx " "frame_len %zx tlen %zx\n", remaining, mem_len, frame_len, tlen); break; } loop_limit--; if (!mem_len) { scst_put_buf(cmd, from); mem_len = scst_get_buf_next(cmd, &from); mem_off = 0; if (!mem_len) { FT_ERR("mem_len 0 from get_buf_next\n"); break; } } if (!frame_len) { frame_len = fcmd->max_lso_payload; frame_len = min(frame_len, remaining); fp = fc_frame_alloc(lport, use_sg ? 0 : frame_len); if (!fp) { FT_IO_DBG("frame_alloc failed. " "use_sg %d frame_len %zd\n", use_sg, frame_len); break; } fr_max_payload(fp) = fcmd->max_payload; to = fc_frame_payload_get(fp, 0); fh_off = frame_off; } tlen = min(mem_len, frame_len); BUG_ON(!tlen); BUG_ON(tlen > remaining); BUG_ON(tlen > mem_len); BUG_ON(tlen > frame_len); if (use_sg) { page = virt_to_page(from + mem_off); get_page(page); tlen = min_t(size_t, tlen, PAGE_SIZE - (mem_off & ~PAGE_MASK)); skb_fill_page_desc(fp_skb(fp), skb_shinfo(fp_skb(fp))->nr_frags, page, offset_in_page(from + mem_off), tlen); fr_len(fp) += tlen; fp_skb(fp)->data_len += tlen; fp_skb(fp)->truesize += PAGE_SIZE << compound_order(page); frame_len -= tlen; if (skb_shinfo(fp_skb(fp))->nr_frags >= FC_FRAME_SG_LEN) frame_len = 0; } else { memcpy(to, from + mem_off, tlen); to += tlen; frame_len -= tlen; } mem_len -= tlen; mem_off += tlen; remaining -= tlen; frame_off += tlen; if (frame_len) continue; fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid, FC_TYPE_FCP, remaining ? (FC_FC_EX_CTX | FC_FC_REL_OFF) : (FC_FC_EX_CTX | FC_FC_REL_OFF | FC_FC_END_SEQ), fh_off); error = lport->tt.seq_send(lport, fcmd->seq, fp); if (error) { WARN_ON(1); /* XXX For now, initiator will retry */ } else fcmd->read_data_len = frame_off; } if (mem_len) scst_put_buf(cmd, from); if (remaining) { FT_IO_DBG("remaining read data %zd\n", remaining); return SCST_TGT_RES_QUEUE_FULL; } return SCST_TGT_RES_SUCCESS; }