int cxgb3i_conn_init_pdu(struct iscsi_task *task, unsigned int offset, unsigned int count) { struct iscsi_conn *conn = task->conn; struct iscsi_tcp_task *tcp_task = task->dd_data; struct cxgb3i_task_data *tdata = tcp_task->dd_data; struct sk_buff *skb = tdata->skb; unsigned int datalen = count; int i, padlen = iscsi_padding(count); struct page *pg; cxgb3i_tx_debug("task 0x%p,0x%p, offset %u, count %u, skb 0x%p.\n", task, task->sc, offset, count, skb); skb_put(skb, task->hdr_len); tx_skb_setmode(skb, conn->hdrdgst_en, datalen ? conn->datadgst_en : 0); if (!count) return 0; if (task->sc) { struct scsi_data_buffer *sdb = scsi_out(task->sc); struct scatterlist *sg = NULL; int err; tdata->offset = offset; tdata->count = count; err = sgl_seek_offset(sdb->table.sgl, sdb->table.nents, tdata->offset, &tdata->sgoffset, &sg); if (err < 0) { cxgb3i_log_warn("tpdu, sgl %u, bad offset %u/%u.\n", sdb->table.nents, tdata->offset, sdb->length); return err; } err = sgl_read_to_frags(sg, tdata->sgoffset, tdata->count, tdata->frags, MAX_PDU_FRAGS); if (err < 0) { cxgb3i_log_warn("tpdu, sgl %u, bad offset %u + %u.\n", sdb->table.nents, tdata->offset, tdata->count); return err; } tdata->nr_frags = err; if (tdata->nr_frags > MAX_SKB_FRAGS || (padlen && tdata->nr_frags == MAX_SKB_FRAGS)) { char *dst = skb->data + task->hdr_len; skb_frag_t *frag = tdata->frags; /* data fits in the skb's headroom */ for (i = 0; i < tdata->nr_frags; i++, frag++) { char *src = kmap_atomic(frag->page, KM_SOFTIRQ0); memcpy(dst, src+frag->page_offset, frag->size); dst += frag->size; kunmap_atomic(src, KM_SOFTIRQ0); } if (padlen) { memset(dst, 0, padlen); padlen = 0; } skb_put(skb, count + padlen); } else { /* data fit into frag_list */ for (i = 0; i < tdata->nr_frags; i++) get_page(tdata->frags[i].page); memcpy(skb_shinfo(skb)->frags, tdata->frags, sizeof(skb_frag_t) * tdata->nr_frags); skb_shinfo(skb)->nr_frags = tdata->nr_frags; skb->len += count; skb->data_len += count; skb->truesize += count; } } else { pg = virt_to_page(task->data); get_page(pg); skb_fill_page_desc(skb, 0, pg, offset_in_page(task->data), count); skb->len += count; skb->data_len += count; skb->truesize += count; } if (padlen) { i = skb_shinfo(skb)->nr_frags; get_page(pad_page); skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, pad_page, 0, padlen); skb->data_len += padlen; skb->truesize += padlen; skb->len += padlen; } return 0; }
/** * iscsi_tcp_segment_done - check whether the segment is complete * @tcp_conn: iscsi tcp connection * @segment: iscsi segment to check * @recv: set to one of this is called from the recv path * @copied: number of bytes copied * * Check if we're done receiving this segment. If the receive * buffer is full but we expect more data, move on to the * next entry in the scatterlist. * * If the amount of data we received isn't a multiple of 4, * we will transparently receive the pad bytes, too. * * This function must be re-entrant. */ int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn, struct iscsi_segment *segment, int recv, unsigned copied) { struct scatterlist sg; unsigned int pad; ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "copied %u %u size %u %s\n", segment->copied, copied, segment->size, recv ? "recv" : "xmit"); if (segment->hash && copied) { /* * If a segment is kmapd we must unmap it before sending * to the crypto layer since that will try to kmap it again. */ iscsi_tcp_segment_unmap(segment); if (!segment->data) { sg_init_table(&sg, 1); sg_set_page(&sg, sg_page(segment->sg), copied, segment->copied + segment->sg_offset + segment->sg->offset); } else sg_init_one(&sg, segment->data + segment->copied, copied); crypto_hash_update(segment->hash, &sg, copied); } segment->copied += copied; if (segment->copied < segment->size) { iscsi_tcp_segment_map(segment, recv); return 0; } segment->total_copied += segment->copied; segment->copied = 0; segment->size = 0; /* Unmap the current scatterlist page, if there is one. */ iscsi_tcp_segment_unmap(segment); /* Do we have more scatterlist entries? */ ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "total copied %u total size %u\n", segment->total_copied, segment->total_size); if (segment->total_copied < segment->total_size) { /* Proceed to the next entry in the scatterlist. */ iscsi_tcp_segment_init_sg(segment, sg_next(segment->sg), 0); iscsi_tcp_segment_map(segment, recv); BUG_ON(segment->size == 0); return 0; } /* Do we need to handle padding? */ if (!(tcp_conn->iscsi_conn->session->tt->caps & CAP_PADDING_OFFLOAD)) { pad = iscsi_padding(segment->total_copied); if (pad != 0) { ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "consume %d pad bytes\n", pad); segment->total_size += pad; segment->size = pad; segment->data = segment->padbuf; return 0; } } /* * Set us up for transferring the data digest. hdr digest * is completely handled in hdr done function. */ if (segment->hash) { crypto_hash_final(segment->hash, segment->digest); iscsi_tcp_segment_splice_digest(segment, recv ? segment->recv_digest : segment->digest); return 0; } return 1; }