]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/scsi/cxgb3i/cxgb3i_pdu.c
Merge tag 'v2.6.37' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / scsi / cxgb3i / cxgb3i_pdu.c
diff --git a/drivers/scsi/cxgb3i/cxgb3i_pdu.c b/drivers/scsi/cxgb3i/cxgb3i_pdu.c
deleted file mode 100644 (file)
index dc5e3e7..0000000
+++ /dev/null
@@ -1,495 +0,0 @@
-/*
- * cxgb3i_pdu.c: Chelsio S3xx iSCSI driver.
- *
- * Copyright (c) 2008 Chelsio Communications, Inc.
- * Copyright (c) 2008 Mike Christie
- * Copyright (c) 2008 Red Hat, Inc.  All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation.
- *
- * Written by: Karen Xie (kxie@chelsio.com)
- */
-
-#include <linux/slab.h>
-#include <linux/skbuff.h>
-#include <linux/crypto.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_host.h>
-
-#include "cxgb3i.h"
-#include "cxgb3i_pdu.h"
-
-#ifdef __DEBUG_CXGB3I_RX__
-#define cxgb3i_rx_debug                cxgb3i_log_debug
-#else
-#define cxgb3i_rx_debug(fmt...)
-#endif
-
-#ifdef __DEBUG_CXGB3I_TX__
-#define cxgb3i_tx_debug                cxgb3i_log_debug
-#else
-#define cxgb3i_tx_debug(fmt...)
-#endif
-
-/* always allocate rooms for AHS */
-#define SKB_TX_PDU_HEADER_LEN  \
-       (sizeof(struct iscsi_hdr) + ISCSI_MAX_AHS_SIZE)
-static unsigned int skb_extra_headroom;
-static struct page *pad_page;
-
-/*
- * pdu receive, interact with libiscsi_tcp
- */
-static inline int read_pdu_skb(struct iscsi_conn *conn, struct sk_buff *skb,
-                              unsigned int offset, int offloaded)
-{
-       int status = 0;
-       int bytes_read;
-
-       bytes_read = iscsi_tcp_recv_skb(conn, skb, offset, offloaded, &status);
-       switch (status) {
-       case ISCSI_TCP_CONN_ERR:
-               return -EIO;
-       case ISCSI_TCP_SUSPENDED:
-               /* no transfer - just have caller flush queue */
-               return bytes_read;
-       case ISCSI_TCP_SKB_DONE:
-               /*
-                * pdus should always fit in the skb and we should get
-                * segment done notifcation.
-                */
-               iscsi_conn_printk(KERN_ERR, conn, "Invalid pdu or skb.");
-               return -EFAULT;
-       case ISCSI_TCP_SEGMENT_DONE:
-               return bytes_read;
-       default:
-               iscsi_conn_printk(KERN_ERR, conn, "Invalid iscsi_tcp_recv_skb "
-                                 "status %d\n", status);
-               return -EINVAL;
-       }
-}
-
-static int cxgb3i_conn_read_pdu_skb(struct iscsi_conn *conn,
-                                   struct sk_buff *skb)
-{
-       struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
-       bool offloaded = 0;
-       unsigned int offset;
-       int rc;
-
-       cxgb3i_rx_debug("conn 0x%p, skb 0x%p, len %u, flag 0x%x.\n",
-                       conn, skb, skb->len, skb_ulp_mode(skb));
-
-       if (!iscsi_tcp_recv_segment_is_hdr(tcp_conn)) {
-               iscsi_conn_failure(conn, ISCSI_ERR_PROTO);
-               return -EIO;
-       }
-
-       if (conn->hdrdgst_en && (skb_ulp_mode(skb) & ULP2_FLAG_HCRC_ERROR)) {
-               iscsi_conn_failure(conn, ISCSI_ERR_HDR_DGST);
-               return -EIO;
-       }
-
-       if (conn->datadgst_en && (skb_ulp_mode(skb) & ULP2_FLAG_DCRC_ERROR)) {
-               iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST);
-               return -EIO;
-       }
-
-       /* iscsi hdr */
-       rc = read_pdu_skb(conn, skb, 0, 0);
-       if (rc <= 0)
-               return rc;
-
-       if (iscsi_tcp_recv_segment_is_hdr(tcp_conn))
-               return 0;
-
-       offset = rc;
-       if (conn->hdrdgst_en)
-               offset += ISCSI_DIGEST_SIZE;
-
-       /* iscsi data */
-       if (skb_ulp_mode(skb) & ULP2_FLAG_DATA_DDPED) {
-               cxgb3i_rx_debug("skb 0x%p, opcode 0x%x, data %u, ddp'ed, "
-                               "itt 0x%x.\n",
-                               skb,
-                               tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK,
-                               tcp_conn->in.datalen,
-                               ntohl(tcp_conn->in.hdr->itt));
-               offloaded = 1;
-       } else {
-               cxgb3i_rx_debug("skb 0x%p, opcode 0x%x, data %u, NOT ddp'ed, "
-                               "itt 0x%x.\n",
-                               skb,
-                               tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK,
-                               tcp_conn->in.datalen,
-                               ntohl(tcp_conn->in.hdr->itt));
-               offset += sizeof(struct cpl_iscsi_hdr_norss);
-       }
-
-       rc = read_pdu_skb(conn, skb, offset, offloaded);
-       if (rc < 0)
-               return rc;
-       else
-               return 0;
-}
-
-/*
- * pdu transmit, interact with libiscsi_tcp
- */
-static inline void tx_skb_setmode(struct sk_buff *skb, int hcrc, int dcrc)
-{
-       u8 submode = 0;
-
-       if (hcrc)
-               submode |= 1;
-       if (dcrc)
-               submode |= 2;
-       skb_ulp_mode(skb) = (ULP_MODE_ISCSI << 4) | submode;
-}
-
-void cxgb3i_conn_cleanup_task(struct iscsi_task *task)
-{
-       struct cxgb3i_task_data *tdata = task->dd_data +
-                                       sizeof(struct iscsi_tcp_task);
-
-       /* never reached the xmit task callout */
-       if (tdata->skb)
-               __kfree_skb(tdata->skb);
-       memset(tdata, 0, sizeof(struct cxgb3i_task_data));
-
-       /* MNC - Do we need a check in case this is called but
-        * cxgb3i_conn_alloc_pdu has never been called on the task */
-       cxgb3i_release_itt(task, task->hdr_itt);
-       iscsi_tcp_cleanup_task(task);
-}
-
-static int sgl_seek_offset(struct scatterlist *sgl, unsigned int sgcnt,
-                               unsigned int offset, unsigned int *off,
-                               struct scatterlist **sgp)
-{
-       int i;
-       struct scatterlist *sg;
-
-       for_each_sg(sgl, sg, sgcnt, i) {
-               if (offset < sg->length) {
-                       *off = offset;
-                       *sgp = sg;
-                       return 0;
-               }
-               offset -= sg->length;
-       }
-       return -EFAULT;
-}
-
-static int sgl_read_to_frags(struct scatterlist *sg, unsigned int sgoffset,
-                               unsigned int dlen, skb_frag_t *frags,
-                               int frag_max)
-{
-       unsigned int datalen = dlen;
-       unsigned int sglen = sg->length - sgoffset;
-       struct page *page = sg_page(sg);
-       int i;
-
-       i = 0;
-       do {
-               unsigned int copy;
-
-               if (!sglen) {
-                       sg = sg_next(sg);
-                       if (!sg) {
-                               cxgb3i_log_error("%s, sg NULL, len %u/%u.\n",
-                                                __func__, datalen, dlen);
-                               return -EINVAL;
-                       }
-                       sgoffset = 0;
-                       sglen = sg->length;
-                       page = sg_page(sg);
-
-               }
-               copy = min(datalen, sglen);
-               if (i && page == frags[i - 1].page &&
-                   sgoffset + sg->offset ==
-                       frags[i - 1].page_offset + frags[i - 1].size) {
-                       frags[i - 1].size += copy;
-               } else {
-                       if (i >= frag_max) {
-                               cxgb3i_log_error("%s, too many pages %u, "
-                                                "dlen %u.\n", __func__,
-                                                frag_max, dlen);
-                               return -EINVAL;
-                       }
-
-                       frags[i].page = page;
-                       frags[i].page_offset = sg->offset + sgoffset;
-                       frags[i].size = copy;
-                       i++;
-               }
-               datalen -= copy;
-               sgoffset += copy;
-               sglen -= copy;
-       } while (datalen);
-
-       return i;
-}
-
-int cxgb3i_conn_alloc_pdu(struct iscsi_task *task, u8 opcode)
-{
-       struct iscsi_conn *conn = task->conn;
-       struct iscsi_tcp_task *tcp_task = task->dd_data;
-       struct cxgb3i_task_data *tdata = task->dd_data + sizeof(*tcp_task);
-       struct scsi_cmnd *sc = task->sc;
-       int headroom = SKB_TX_PDU_HEADER_LEN;
-
-       tcp_task->dd_data = tdata;
-       task->hdr = NULL;
-
-       /* write command, need to send data pdus */
-       if (skb_extra_headroom && (opcode == ISCSI_OP_SCSI_DATA_OUT ||
-           (opcode == ISCSI_OP_SCSI_CMD &&
-           (scsi_bidi_cmnd(sc) || sc->sc_data_direction == DMA_TO_DEVICE))))
-               headroom += min(skb_extra_headroom, conn->max_xmit_dlength);
-
-       tdata->skb = alloc_skb(TX_HEADER_LEN + headroom, GFP_ATOMIC);
-       if (!tdata->skb)
-               return -ENOMEM;
-       skb_reserve(tdata->skb, TX_HEADER_LEN);
-
-       cxgb3i_tx_debug("task 0x%p, opcode 0x%x, skb 0x%p.\n",
-                       task, opcode, tdata->skb);
-
-       task->hdr = (struct iscsi_hdr *)tdata->skb->data;
-       task->hdr_max = SKB_TX_PDU_HEADER_LEN;
-
-       /* data_out uses scsi_cmd's itt */
-       if (opcode != ISCSI_OP_SCSI_DATA_OUT)
-               cxgb3i_reserve_itt(task, &task->hdr->itt);
-
-       return 0;
-}
-
-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;
-}
-
-int cxgb3i_conn_xmit_pdu(struct iscsi_task *task)
-{
-       struct iscsi_tcp_conn *tcp_conn = task->conn->dd_data;
-       struct cxgb3i_conn *cconn = tcp_conn->dd_data;
-       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;
-       int err;
-
-       if (!skb)
-               return 0;
-
-       datalen = skb->data_len;
-       tdata->skb = NULL;
-       err = cxgb3i_c3cn_send_pdus(cconn->cep->c3cn, skb);
-       if (err > 0) {
-               int pdulen = err;
-
-               cxgb3i_tx_debug("task 0x%p, skb 0x%p, len %u/%u, rv %d.\n",
-                               task, skb, skb->len, skb->data_len, err);
-
-               if (task->conn->hdrdgst_en)
-                       pdulen += ISCSI_DIGEST_SIZE;
-               if (datalen && task->conn->datadgst_en)
-                       pdulen += ISCSI_DIGEST_SIZE;
-
-               task->conn->txdata_octets += pdulen;
-               return 0;
-       }
-
-       if (err == -EAGAIN || err == -ENOBUFS) {
-               /* reset skb to send when we are called again */
-               tdata->skb = skb;
-               return err;
-       }
-
-       kfree_skb(skb);
-       cxgb3i_tx_debug("itt 0x%x, skb 0x%p, len %u/%u, xmit err %d.\n",
-                       task->itt, skb, skb->len, skb->data_len, err);
-       iscsi_conn_printk(KERN_ERR, task->conn, "xmit err %d.\n", err);
-       iscsi_conn_failure(task->conn, ISCSI_ERR_XMIT_FAILED);
-       return err;
-}
-
-int cxgb3i_pdu_init(void)
-{
-       if (SKB_TX_HEADROOM > (512 * MAX_SKB_FRAGS))
-               skb_extra_headroom = SKB_TX_HEADROOM;
-       pad_page = alloc_page(GFP_KERNEL);
-       if (!pad_page)
-               return -ENOMEM;
-       memset(page_address(pad_page), 0, PAGE_SIZE);
-       return 0;
-}
-
-void cxgb3i_pdu_cleanup(void)
-{
-       if (pad_page) {
-               __free_page(pad_page);
-               pad_page = NULL;
-       }
-}
-
-void cxgb3i_conn_pdu_ready(struct s3_conn *c3cn)
-{
-       struct sk_buff *skb;
-       unsigned int read = 0;
-       struct iscsi_conn *conn = c3cn->user_data;
-       int err = 0;
-
-       cxgb3i_rx_debug("cn 0x%p.\n", c3cn);
-
-       read_lock(&c3cn->callback_lock);
-       if (unlikely(!conn || conn->suspend_rx)) {
-               cxgb3i_rx_debug("conn 0x%p, id %d, suspend_rx %lu!\n",
-                               conn, conn ? conn->id : 0xFF,
-                               conn ? conn->suspend_rx : 0xFF);
-               read_unlock(&c3cn->callback_lock);
-               return;
-       }
-       skb = skb_peek(&c3cn->receive_queue);
-       while (!err && skb) {
-               __skb_unlink(skb, &c3cn->receive_queue);
-               read += skb_rx_pdulen(skb);
-               cxgb3i_rx_debug("conn 0x%p, cn 0x%p, rx skb 0x%p, pdulen %u.\n",
-                               conn, c3cn, skb, skb_rx_pdulen(skb));
-               err = cxgb3i_conn_read_pdu_skb(conn, skb);
-               __kfree_skb(skb);
-               skb = skb_peek(&c3cn->receive_queue);
-       }
-       read_unlock(&c3cn->callback_lock);
-       c3cn->copied_seq += read;
-       cxgb3i_c3cn_rx_credits(c3cn, read);
-       conn->rxdata_octets += read;
-
-       if (err) {
-               cxgb3i_log_info("conn 0x%p rx failed err %d.\n", conn, err);
-               iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
-       }
-}
-
-void cxgb3i_conn_tx_open(struct s3_conn *c3cn)
-{
-       struct iscsi_conn *conn = c3cn->user_data;
-
-       cxgb3i_tx_debug("cn 0x%p.\n", c3cn);
-       if (conn) {
-               cxgb3i_tx_debug("cn 0x%p, cid %d.\n", c3cn, conn->id);
-               iscsi_conn_queue_work(conn);
-       }
-}
-
-void cxgb3i_conn_closing(struct s3_conn *c3cn)
-{
-       struct iscsi_conn *conn;
-
-       read_lock(&c3cn->callback_lock);
-       conn = c3cn->user_data;
-       if (conn && c3cn->state != C3CN_STATE_ESTABLISHED)
-               iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
-       read_unlock(&c3cn->callback_lock);
-}