- patches.drivers/cxgb3i: add cxgb3i iscsi driver
authorHannes Reinecke <hare@suse.de>
Fri, 21 Nov 2008 12:09:25 +0000 (13:09 +0100)
committerHannes Reinecke <hare@suse.de>
Fri, 21 Nov 2008 12:09:25 +0000 (13:09 +0100)
  (FATE#304154,bnc#433500).
- patches.drivers/cxgb3i-fixed-offload-array-size: cxgb3i -
  fixed offload wr array size (bnc#447409).
- patches.fixes/dm-table-switch-to-readonly: dm multipath devices
  are not getting created for readonly devices (bnc#382705).
- patches.suse/dm-mpath-null-pgs: Allow zero paths for multipath
  priority groups (bnc#372684).
- patches.suse/scsi-netlink-ml: Netlink interface for SCSI
  sense codes (FATE#303789).

suse-commit: 731044004b59aa446122453fa88c415c98640c90

drivers/md/dm-mpath.c
drivers/md/dm-table.c
drivers/md/dm.c
drivers/scsi/cxgb3i/cxgb3i.h
drivers/scsi/cxgb3i/cxgb3i_iscsi.c
drivers/scsi/cxgb3i/cxgb3i_offload.c
drivers/scsi/cxgb3i/cxgb3i_ulp2.c
drivers/scsi/scsi_error.c
include/scsi/scsi_netlink.h
include/scsi/scsi_netlink_ml.h [new file with mode: 0644]

index 2d43b93..d1ad580 100644 (file)
@@ -768,8 +768,8 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
 {
        /* target parameters */
        static struct param _params[] = {
-               {1, 1024, "invalid number of priority groups"},
-               {1, 1024, "invalid initial priority group number"},
+               {0, 1024, "invalid number of priority groups"},
+               {0, 1024, "invalid initial priority group number"},
        };
 
        int r;
index 49ab737..0938068 100644 (file)
@@ -451,11 +451,19 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti,
                dd->mode = mode;
                dd->bdev = NULL;
 
-               if ((r = open_dev(dd, dev, t->md))) {
+               r = open_dev(dd, dev, t->md);
+               if (r == -EROFS) {
+                       dd->mode &= ~FMODE_WRITE;
+                       r = open_dev(dd, dev, t->md);
+               }
+               if (r) {
                        kfree(dd);
                        return r;
                }
 
+               if (dd->mode != mode)
+                       t->mode = dd->mode;
+
                format_dev_t(dd->name, dev);
 
                atomic_set(&dd->count, 0);
index d41dcdd..a832299 100644 (file)
@@ -310,16 +310,25 @@ static void __exit dm_exit(void)
 static int dm_blk_open(struct inode *inode, struct file *file)
 {
        struct mapped_device *md;
+       int retval = 0;
 
        spin_lock(&_minor_lock);
 
        md = inode->i_bdev->bd_disk->private_data;
-       if (!md)
+       if (!md) {
+               retval = -ENXIO;
                goto out;
+       }
 
        if (test_bit(DMF_FREEING, &md->flags) ||
            test_bit(DMF_DELETING, &md->flags)) {
                md = NULL;
+               retval = -ENXIO;
+               goto out;
+       }
+       if (md->disk->policy && (file->f_mode & FMODE_WRITE)) {
+               md = NULL;
+               retval = -EROFS;
                goto out;
        }
 
@@ -329,7 +338,7 @@ static int dm_blk_open(struct inode *inode, struct file *file)
 out:
        spin_unlock(&_minor_lock);
 
-       return md ? 0 : -ENXIO;
+       return retval;
 }
 
 static int dm_blk_close(struct inode *inode, struct file *file)
@@ -1901,6 +1910,11 @@ static int __bind(struct mapped_device *md, struct dm_table *t)
        write_lock(&md->map_lock);
        md->map = t;
        dm_table_set_restrictions(t, q);
+       if (!(dm_table_get_mode(t) & FMODE_WRITE)) {
+               set_disk_ro(md->disk, 1);
+       } else {
+               set_disk_ro(md->disk, 0);
+       }
        write_unlock(&md->map_lock);
 
        return 0;
index e3f591f..48bc2ed 100644 (file)
@@ -62,6 +62,25 @@ struct cxgb3i_tag_format {
 };
 
 /**
+ * struct cxgb3i_gather_list - cxgb3i direct data placement memory
+ *
+ * @tag:       ddp tag
+ * @length:    total data buffer length
+ * @offset:    initial offset to the 1st page
+ * @nelem:     # of pages
+ * @pages:     pages
+ * @phys_addr: physical address
+ */
+struct cxgb3i_gather_list {
+       u32 tag;
+       unsigned int length;
+       unsigned int offset;
+       unsigned int nelem;
+       struct page **pages;
+       dma_addr_t phys_addr[0];
+};
+
+/**
  * struct cxgb3i_ddp_info - cxgb3i direct data placement for pdu payload
  *
  * @llimit:    lower bound of the page pod memory
@@ -77,7 +96,8 @@ struct cxgb3i_ddp_info {
        unsigned int nppods;
        unsigned int idx_last;
        spinlock_t map_lock;
-       u8 *map;
+       struct cxgb3i_gather_list **gl_map;
+       struct sk_buff **gl_skb;
 };
 
 /**
@@ -119,7 +139,7 @@ struct cxgb3i_adapter {
        unsigned int rx_max_size;
 
        struct cxgb3i_tag_format tag_format;
-       struct cxgb3i_ddp_info ddp;
+       struct cxgb3i_ddp_info *ddp;
 };
 
 /**
@@ -174,6 +194,6 @@ void cxgb3i_ddp_tag_release(struct cxgb3i_adapter *, u32,
                            struct scatterlist *, unsigned int);
 u32 cxgb3i_ddp_tag_reserve(struct cxgb3i_adapter *, unsigned int,
                           u32, unsigned int, struct scatterlist *,
-                          unsigned int);
+                          unsigned int, int);
 int cxgb3i_conn_ulp2_xmit(struct iscsi_conn *);
 #endif
index 768c872..bc75dff 100644 (file)
@@ -723,14 +723,16 @@ static int cxgb3i_reserve_itt(struct iscsi_task *task, itt_t *hdr_itt)
                struct s3_conn *c3cn = (struct s3_conn *)(tcp_conn->sock);
                tag =
                    cxgb3i_ddp_tag_reserve(snic, c3cn->tid, sw_tag,
-                                          scsi_out(sc)->length,
-                                          scsi_out(sc)->table.sgl,
-                                          scsi_out(sc)->table.nents);
+                                          scsi_in(sc)->length,
+                                          scsi_in(sc)->table.sgl,
+                                          scsi_in(sc)->table.nents,
+                                          GFP_ATOMIC);
        }
        if (tag == RESERVED_ITT)
                tag = sw_tag | (snic->tag_format.rsvd_mask <<
                                snic->tag_format.rsvd_shift);
        *hdr_itt = htonl(tag);
+
        return 0;
 }
 
@@ -744,8 +746,8 @@ static void cxgb3i_release_itt(struct iscsi_task *task, itt_t hdr_itt)
        hdr_itt = ntohl(hdr_itt);
        if (sc && (sc->sc_data_direction == DMA_FROM_DEVICE))
                cxgb3i_ddp_tag_release(snic, hdr_itt,
-                                      scsi_out(sc)->table.sgl,
-                                      scsi_out(sc)->table.nents);
+                                      scsi_in(sc)->table.sgl,
+                                      scsi_in(sc)->table.nents);
 }
 
 /**
index 8d0b3bf..e3f3008 100644 (file)
@@ -211,7 +211,8 @@ static unsigned int wrlen __read_mostly;
  * in the skb and whether it has any payload in its main body.  This maps the
  * length of the gather list represented by an skb into the # of necessary WRs.
  */
-static unsigned int skb_wrs[MAX_SKB_FRAGS + 2] __read_mostly;
+#define SKB_WR_LIST_SIZE       (16384/512 + 1)
+static unsigned int skb_wrs[SKB_WR_LIST_SIZE + 2] __read_mostly;
 
 static void s3_init_wr_tab(unsigned int wr_len)
 {
@@ -220,7 +221,7 @@ static void s3_init_wr_tab(unsigned int wr_len)
        if (skb_wrs[1])         /* already initialized */
                return;
 
-       for (i = 1; i < ARRAY_SIZE(skb_wrs); i++) {
+       for (i = 1; i < SKB_WR_LIST_SIZE; i++) {
                int sgl_len = (3 * i) / 2 + (i & 1);
 
                sgl_len += 3;
@@ -582,7 +583,6 @@ static inline void make_tx_data_wr(struct s3_conn *c3cn,
                           V_TX_SHOVE((skb_peek(&c3cn->write_queue) ? 0 : 1)));
 
        if (!c3cn_flag(c3cn, C3CN_TX_DATA_SENT)) {
-
                req->flags |= htonl(V_TX_ACK_PAGES(2) | F_TX_INIT |
                                    V_TX_CPU_IDX(c3cn->qset));
 
@@ -825,7 +825,7 @@ static int s3_push_frames(struct s3_conn *c3cn, int req_completion)
                if (wrs_needed > 1 && len + sizeof(struct tx_data_wr) <= wrlen)
                        wrs_needed = 1;
 
-               WARN_ON(frags >= ARRAY_SIZE(skb_wrs) || wrs_needed < 1);
+               WARN_ON(frags >= SKB_WR_LIST_SIZE || wrs_needed < 1);
 
                if (c3cn->wr_avail < wrs_needed)
                        break;
@@ -941,12 +941,23 @@ int cxgb3i_c3cn_send_pdus(struct s3_conn *c3cn, struct sk_buff *skb, int flags)
                goto out_err;
 
        while (skb) {
+               int frags = skb_shinfo(skb)->nr_frags +
+                           (skb->len != skb->data_len);
+
                if (unlikely(skb_headroom(skb) < TX_HEADER_LEN)) {
                        c3cn_tx_debug("c3cn 0x%p, skb head.\n", c3cn);
                        err = -EINVAL;
                        goto out_err;
                }
 
+               if (frags >= SKB_WR_LIST_SIZE) {
+                       cxgb3i_log_error("c3cn 0x%p, tx frags %d, len %u,%u.\n",
+                                        c3cn, skb_shinfo(skb)->nr_frags,
+                                        skb->len, skb->data_len);
+                       err = -EINVAL;
+                       goto out_err;
+               }
+
                next = skb->next;
                skb->next = NULL;
                skb_entail(c3cn, skb, C3CB_FLAG_NO_APPEND | C3CB_FLAG_NEED_HDR);
index 36a2f20..fc1d413 100644 (file)
 static struct page *pad_page;
 
 #define ULP2_PGIDX_MAX         4
-#define ULP2_4K_PAGE_SHIFT     12
-#define ULP2_4K_PAGE_MASK      (~((1UL << ULP2_4K_PAGE_SHIFT) - 1))
-static unsigned char ddp_page_order[ULP2_PGIDX_MAX];
-static unsigned long ddp_page_size[ULP2_PGIDX_MAX];
-static unsigned char ddp_page_shift[ULP2_PGIDX_MAX];
+#define ULP2_DDP_THRESHOLD     2048
+static unsigned char ddp_page_order[ULP2_PGIDX_MAX] = {0, 1, 2, 4};
+static unsigned char ddp_page_shift[ULP2_PGIDX_MAX] = {12, 13, 14, 16};
 static unsigned char sw_tag_idx_bits;
 static unsigned char sw_tag_age_bits;
+static unsigned char page_idx = ULP2_PGIDX_MAX;
 
 static void cxgb3i_ddp_page_init(void)
 {
        int i;
-       unsigned long n = PAGE_SIZE >> ULP2_4K_PAGE_SHIFT;
-
-       if (PAGE_SIZE & (~ULP2_4K_PAGE_MASK)) {
-               cxgb3i_log_debug("PAGE_SIZE 0x%lx is not multiple of 4K, "
-                               "ddp disabled.\n", PAGE_SIZE);
-               return;
-       }
-       n = __ilog2_u32(n);
-       for (i = 0; i < ULP2_PGIDX_MAX; i++, n++) {
-               ddp_page_order[i] = n;
-               ddp_page_shift[i] = ULP2_4K_PAGE_SHIFT + n;
-               ddp_page_size[i] = 1 << ddp_page_shift[i];
-               cxgb3i_log_debug("%d, order %u, shift %u, size 0x%lx.\n", i,
-                                ddp_page_order[i], ddp_page_shift[i],
-                                ddp_page_size[i]);
-       }
 
        sw_tag_idx_bits = (__ilog2_u32(ISCSI_ITT_MASK)) + 1;
        sw_tag_age_bits = (__ilog2_u32(ISCSI_AGE_MASK)) + 1;
+
+       for (i = 0; i < ULP2_PGIDX_MAX; i++) {
+               if (PAGE_SIZE == (1UL << ddp_page_shift[i])) {
+                       page_idx = i;
+                       cxgb3i_log_info("PAGE_SIZE %lu, idx %u.\n",
+                                       PAGE_SIZE, page_idx);
+               }
+       }
+
+       if (page_idx == ULP2_PGIDX_MAX)
+               cxgb3i_log_info("PAGE_SIZE %lu, no match.\n", PAGE_SIZE);
 }
 
 static inline void ulp_mem_io_set_hdr(struct sk_buff *skb, unsigned int addr)
@@ -91,38 +85,27 @@ static inline void ulp_mem_io_set_hdr(struct sk_buff *skb, unsigned int addr)
 
 static int set_ddp_map(struct cxgb3i_adapter *snic, struct pagepod_hdr *hdr,
                       unsigned int idx, unsigned int npods,
-                      struct scatterlist *sgl, unsigned int sgcnt)
+                      struct cxgb3i_gather_list *gl)
 {
-       struct cxgb3i_ddp_info *ddp = &snic->ddp;
-       struct scatterlist *sg = sgl;
+       struct cxgb3i_ddp_info *ddp = snic->ddp;
        unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ddp->llimit;
        int i;
 
-       for (i = 0; i < npods; i++, pm_addr += PPOD_SIZE) {
-               struct sk_buff *skb;
+       for (i = 0; i < npods; i++, idx++, pm_addr += PPOD_SIZE) {
+               struct sk_buff *skb = ddp->gl_skb[idx];
                struct pagepod *ppod;
-               int j, k;
-               skb =
-                   alloc_skb(sizeof(struct ulp_mem_io) + PPOD_SIZE,
-                             GFP_ATOMIC);
-               if (!skb) {
-                       cxgb3i_log_debug("skb OMM.\n");
-                       return -ENOMEM;
-               }
-               skb_put(skb, sizeof(struct ulp_mem_io) + PPOD_SIZE);
+               int j, pidx;
+
+               /* hold on to the skb until we clear the ddp mapping */
+               skb_get(skb);
 
                ulp_mem_io_set_hdr(skb, pm_addr);
                ppod =
                    (struct pagepod *)(skb->head + sizeof(struct ulp_mem_io));
                memcpy(&(ppod->hdr), hdr, sizeof(struct pagepod));
-               for (j = 0, k = i * 4; j < 5; j++, k++) {
-                       if (k < sgcnt) {
-                               ppod->addr[j] = cpu_to_be64(sg_dma_address(sg));
-                               if (j < 4)
-                                       sg = sg_next(sg);
-                       } else
-                               ppod->addr[j] = 0UL;
-               }
+               for (pidx = 4 * i, j = 0; j < 5; ++j, ++pidx)
+                       ppod->addr[j] = pidx < gl->nelem ?
+                                    cpu_to_be64(gl->phys_addr[pidx]) : 0UL;
 
                skb->priority = CPL_PRIORITY_CONTROL;
                cxgb3_ofld_send(snic->tdev, skb);
@@ -133,18 +116,14 @@ static int set_ddp_map(struct cxgb3i_adapter *snic, struct pagepod_hdr *hdr,
 static int clear_ddp_map(struct cxgb3i_adapter *snic, unsigned int idx,
                         unsigned int npods)
 {
-       struct cxgb3i_ddp_info *ddp = &snic->ddp;
+       struct cxgb3i_ddp_info *ddp = snic->ddp;
        unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ddp->llimit;
        int i;
 
-       for (i = 0; i < npods; i++, pm_addr += PPOD_SIZE) {
-               struct sk_buff *skb;
-               skb =
-                   alloc_skb(sizeof(struct ulp_mem_io) + PPOD_SIZE,
-                             GFP_ATOMIC);
-               if (!skb)
-                       return -ENOMEM;
-               skb_put(skb, sizeof(struct ulp_mem_io) + PPOD_SIZE);
+       for (i = 0; i < npods; i++, idx++, pm_addr += PPOD_SIZE) {
+               struct sk_buff *skb = ddp->gl_skb[idx];
+
+               ddp->gl_skb[idx] = NULL;
                memset((skb->head + sizeof(struct ulp_mem_io)), 0, PPOD_SIZE);
                ulp_mem_io_set_hdr(skb, pm_addr);
                skb->priority = CPL_PRIORITY_CONTROL;
@@ -153,42 +132,80 @@ static int clear_ddp_map(struct cxgb3i_adapter *snic, unsigned int idx,
        return 0;
 }
 
-static int cxgb3i_ddp_sgl_check(struct scatterlist *sgl, unsigned int sgcnt)
+static struct cxgb3i_gather_list *ddp_make_gl(unsigned int xferlen,
+                                             struct scatterlist *sgl,
+                                             unsigned int sgcnt, int gfp)
 {
-       struct scatterlist *sg;
-       int i;
-
-       /* make sure the sgl is fit for ddp:
-        *      each has the same page size, and
-        *      first & last page do not need to be used completely, and
-        *      the rest of page must be used completely
-        */
-       for_each_sg(sgl, sg, sgcnt, i) {
-               if ((i && sg->offset) ||
-                   ((i != sgcnt - 1) &&
-                    (sg->length + sg->offset) != PAGE_SIZE)) {
-                       cxgb3i_tag_debug("sg %u/%u, off %u, len %u.\n",
-                                        i, sgcnt, sg->offset, sg->length);
-                       return -EINVAL;
+       struct cxgb3i_gather_list *gl;
+       struct scatterlist *sg = sgl;
+       struct page *sgpage = sg_page(sg);
+       unsigned int sglen = sg->length;
+       unsigned int sgoffset = sg->offset;
+       unsigned int npages = (xferlen + sgoffset + PAGE_SIZE - 1) >>
+                             PAGE_SHIFT;
+       int i = 1, j = 0;
+
+       gl = kzalloc(sizeof(struct cxgb3i_gather_list) +
+                    npages * (sizeof(dma_addr_t) + sizeof(struct page *)),
+                    gfp);
+       if (!gl)
+               return NULL;
+
+       gl->pages = (struct page **)&gl->phys_addr[npages];
+       gl->length = xferlen;
+       gl->offset = sgoffset;
+       gl->pages[0] = sgpage;
+
+       sg = sg_next(sg);
+       while (sg) {
+               struct page *page = sg_page(sg);
+
+               if (sgpage == page && sg->offset == sgoffset + sglen)
+                       sglen += sg->length;
+               else {
+                       /* make sure the sgl is fit for ddp:
+                        * each has the same page size, and
+                        * all of the middle pages are used completely
+                        */
+                       if ((j && sgoffset) ||
+                           ((i != sgcnt - 1) &&
+                            ((sglen + sgoffset) & ~PAGE_MASK)))
+                               goto error_out;
+
+                       j++;
+                       if (j == gl->nelem)
+                               goto error_out;
+                       gl->pages[j] = page;
+                       sglen = sg->length;
+                       sgoffset = sg->offset;
+                       sgpage = page;
                }
+               i++;
+               sg = sg_next(sg);
        }
+       gl->nelem = ++j;
+       return gl;
 
-       return 0;
+error_out:
+       kfree(gl);
+       return NULL;
 }
 
 static inline int ddp_find_unused_entries(struct cxgb3i_ddp_info *ddp,
-                                         int start, int max, int count)
+                                         int start, int max, int count,
+                                         struct cxgb3i_gather_list *gl)
 {
        unsigned int i, j;
 
        spin_lock(&ddp->map_lock);
        for (i = start; i <= max;) {
                for (j = 0; j < count; j++) {
-                       if (ddp->map[i + j])
+                       if (ddp->gl_map[i + j])
                                break;
                }
                if (j == count) {
-                       memset(&ddp->map[i], 1, count);
+                       for (j = 0; j < count; j++)
+                               ddp->gl_map[i + j] = gl;
                        spin_unlock(&ddp->map_lock);
                        return i;
                }
@@ -202,86 +219,127 @@ static inline void ddp_unmark_entries(struct cxgb3i_ddp_info *ddp,
                                      int start, int count)
 {
        spin_lock(&ddp->map_lock);
-       memset(&ddp->map[start], 0, count);
+       memset(&ddp->gl_map[start], 0,
+              count * sizeof(struct cxgb3i_gather_list *));
        spin_unlock(&ddp->map_lock);
 }
 
-static inline int sgl_map(struct cxgb3i_adapter *snic,
-                         struct scatterlist *sgl, unsigned int sgcnt)
+static inline void ddp_free_gl_skb(struct cxgb3i_ddp_info *ddp,
+                                  int idx, int count)
 {
-       struct scatterlist *sg;
-       int i, err;
+       int i;
 
-       for_each_sg(sgl, sg, sgcnt, i) {
-               err = pci_map_sg(snic->pdev, sg, 1, PCI_DMA_FROMDEVICE);
-               if (err <= 0) {
-                       cxgb3i_tag_debug("sgcnt %d/%u, pci map failed %d.\n",
-                                        i, sgcnt, err);
-                       return err;
+       for (i = 0; i < count; i++, idx++)
+               if (ddp->gl_skb[idx]) {
+                       kfree_skb(ddp->gl_skb[idx]);
+                       ddp->gl_skb[idx] = NULL;
+               }
+}
+
+static inline int ddp_alloc_gl_skb(struct cxgb3i_ddp_info *ddp,
+                                  int idx, int count, int gfp)
+{
+       int i;
+
+       for (i = 0; i < count; i++) {
+               struct sk_buff *skb = alloc_skb(sizeof(struct ulp_mem_io) +
+                                               PPOD_SIZE, gfp);
+               if (skb) {
+                       ddp->gl_skb[idx + i] = skb;
+                       skb_put(skb, sizeof(struct ulp_mem_io) + PPOD_SIZE);
+               } else {
+                       ddp_free_gl_skb(ddp, idx, i);
+                       return -ENOMEM;
                }
        }
-       return sgcnt;
+       return 0;
 }
 
-static inline void sgl_unmap(struct cxgb3i_adapter *snic,
-                            struct scatterlist *sgl, unsigned int sgcnt)
+static inline void ddp_gl_unmap(struct pci_dev *pdev,
+                               struct cxgb3i_gather_list *gl)
 {
-       struct scatterlist *sg;
        int i;
 
-       for_each_sg(sgl, sg, sgcnt, i) {
-               if (sg_dma_address(sg))
-                       pci_unmap_sg(snic->pdev, sg, 1, PCI_DMA_FROMDEVICE);
-               else
-                       break;
+       for (i = 0; i < gl->nelem; i++)
+               pci_unmap_page(pdev, gl->phys_addr[i], PAGE_SIZE,
+                              PCI_DMA_FROMDEVICE);
+}
+
+static inline int ddp_gl_map(struct pci_dev *pdev,
+                            struct cxgb3i_gather_list *gl)
+{
+       int i;
+
+       for (i = 0; i < gl->nelem; i++) {
+               gl->phys_addr[i] = pci_map_page(pdev, gl->pages[i], 0,
+                                               PAGE_SIZE,
+                                               PCI_DMA_FROMDEVICE);
+               if (unlikely(pci_dma_mapping_error(pdev, gl->phys_addr[i])))
+                       goto unmap;
        }
+
+       return i;
+
+unmap:
+       if (i) {
+               unsigned int nelem = gl->nelem;
+
+               gl->nelem = i;
+               ddp_gl_unmap(pdev, gl);
+               gl->nelem = nelem;
+       }
+       return -ENOMEM;
 }
 
 u32 cxgb3i_ddp_tag_reserve(struct cxgb3i_adapter *snic, unsigned int tid,
                           u32 sw_tag, unsigned int xferlen,
-                          struct scatterlist *sgl, unsigned int sgcnt)
+                          struct scatterlist *sgl, unsigned int sgcnt,
+                          int gfp)
 {
-       struct cxgb3i_ddp_info *ddp = &snic->ddp;
+       struct cxgb3i_ddp_info *ddp = snic->ddp;
+       struct cxgb3i_gather_list *gl;
        struct pagepod_hdr hdr;
        unsigned int npods;
        int idx = -1, idx_max;
        u32 tag;
-       int err;
 
-       if (!ddp || !sgcnt || xferlen < PAGE_SIZE) {
-               cxgb3i_tag_debug("sgcnt %u, xferlen %u < %lu, NO DDP.\n",
-                                sgcnt, xferlen, PAGE_SIZE);
+       if (page_idx || !ddp || !sgcnt || xferlen < ULP2_DDP_THRESHOLD) {
+               cxgb3i_tag_debug("pgidx %u, sgcnt %u, xfer %u/%u, NO ddp.\n",
+                                page_idx, sgcnt, xferlen, ULP2_DDP_THRESHOLD);
                return RESERVED_ITT;
        }
 
-       err = cxgb3i_ddp_sgl_check(sgl, sgcnt);
-       if (err < 0) {
+       gl = ddp_make_gl(xferlen, sgl, sgcnt, gfp);
+       if (!gl) {
                cxgb3i_tag_debug("sgcnt %u, xferlen %u, SGL check fail.\n",
                                 sgcnt, xferlen);
                return RESERVED_ITT;
        }
 
-       npods = (sgcnt + PPOD_PAGES_MAX - 1) >> PPOD_PAGES_SHIFT;
+       npods = (gl->nelem + PPOD_PAGES_MAX - 1) >> PPOD_PAGES_SHIFT;
        idx_max = ddp->nppods - npods + 1;
 
        if (ddp->idx_last == ddp->nppods)
-               idx = ddp_find_unused_entries(ddp, 0, idx_max, npods);
+               idx = ddp_find_unused_entries(ddp, 0, idx_max, npods, gl);
        else {
                idx = ddp_find_unused_entries(ddp, ddp->idx_last + 1, idx_max,
-                                             npods);
+                                             npods, gl);
                if ((idx < 0) && (ddp->idx_last >= npods))
                        idx = ddp_find_unused_entries(ddp, 0,
                                                      ddp->idx_last - npods + 1,
-                                                     npods);
+                                                     npods, gl);
        }
        if (idx < 0) {
+               kfree(gl);
                cxgb3i_tag_debug("sgcnt %u, xferlen %u, npods %u NO DDP.\n",
                                 sgcnt, xferlen, npods);
                return RESERVED_ITT;
        }
 
-       err = sgl_map(snic, sgl, sgcnt);
-       if (err < sgcnt)
+       if (ddp_alloc_gl_skb(ddp, idx, npods, gfp) < 0)
+               goto unmark_entries;
+
+       if (ddp_gl_map(snic->pdev, gl) < 0)
                goto unmap_sgl;
 
        tag = sw_tag | (idx << snic->tag_format.rsvd_shift);
@@ -290,9 +348,9 @@ u32 cxgb3i_ddp_tag_reserve(struct cxgb3i_adapter *snic, unsigned int tid,
        hdr.vld_tid = htonl(F_PPOD_VALID | V_PPOD_TID(tid));
        hdr.pgsz_tag_clr = htonl(tag & snic->tag_format.rsvd_tag_mask);
        hdr.maxoffset = htonl(xferlen);
-       hdr.pgoffset = htonl(sgl->offset);
+       hdr.pgoffset = htonl(gl->offset);
 
-       if (set_ddp_map(snic, &hdr, idx, npods, sgl, sgcnt) < 0)
+       if (set_ddp_map(snic, &hdr, idx, npods, gl) < 0)
                goto unmap_sgl;
 
        ddp->idx_last = idx;
@@ -301,7 +359,9 @@ u32 cxgb3i_ddp_tag_reserve(struct cxgb3i_adapter *snic, unsigned int tid,
        return tag;
 
 unmap_sgl:
-       sgl_unmap(snic, sgl, sgcnt);
+       ddp_gl_unmap(snic->pdev, gl);
+       ddp_free_gl_skb(ddp, idx, npods);
+unmark_entries:
        ddp_unmark_entries(ddp, idx, npods);
        return RESERVED_ITT;
 }
@@ -311,14 +371,18 @@ void cxgb3i_ddp_tag_release(struct cxgb3i_adapter *snic, u32 tag,
 {
        u32 idx = (tag >> snic->tag_format.rsvd_shift) &
                  snic->tag_format.rsvd_mask;
-       unsigned int npods = (sgcnt + PPOD_PAGES_MAX - 1) >> PPOD_PAGES_SHIFT;
 
        if (idx < snic->tag_format.rsvd_mask) {
+               struct cxgb3i_ddp_info *ddp = snic->ddp;
+               struct cxgb3i_gather_list *gl = ddp->gl_map[idx];
+               unsigned int npods = (gl->nelem + PPOD_PAGES_MAX - 1) >>
+                                    PPOD_PAGES_SHIFT;
+
                cxgb3i_tag_debug("ddp tag 0x%x, release idx 0x%x, npods %u.\n",
                                 tag, idx, npods);
                clear_ddp_map(snic, idx, npods);
-               ddp_unmark_entries(&snic->ddp, idx, npods);
-               sgl_unmap(snic, sgl, sgcnt);
+               ddp_unmark_entries(ddp, idx, npods);
+               ddp_gl_unmap(snic->pdev, gl);
        }
 }
 
@@ -330,6 +394,7 @@ int cxgb3i_conn_ulp_setup(struct cxgb3i_conn *cconn, int hcrc, int dcrc)
                                        GFP_KERNEL | __GFP_NOFAIL);
        struct cpl_set_tcb_field *req;
        u32 submode = (hcrc ? 1 : 0) | (dcrc ? 2 : 0);
+       u32 pgcode = page_idx < ULP2_PGIDX_MAX ? page_idx : 0;
 
        /* set up ulp submode and page size */
        req = (struct cpl_set_tcb_field *)skb_put(skb, sizeof(*req));
@@ -339,8 +404,7 @@ int cxgb3i_conn_ulp_setup(struct cxgb3i_conn *cconn, int hcrc, int dcrc)
        req->cpu_idx = 0;
        req->word = htons(31);
        req->mask = cpu_to_be64(0xFF000000);
-       /* the connection page size is always the same as ddp-pgsz0 */
-       req->val = cpu_to_be64(submode << 24);
+       req->val = cpu_to_be64(((pgcode << 4) | submode) << 24);
        skb->priority = CPL_PRIORITY_CONTROL;
 
        cxgb3_ofld_send(c3cn->cdev, skb);
@@ -397,13 +461,15 @@ static int cxgb3i_conn_read_pdu_skb(struct iscsi_conn *conn,
                segment->status = (conn->datadgst_en &&
                                   (skb_ulp_mode(skb) & ULP2_FLAG_DCRC_ERROR)) ?
                    ISCSI_SEGMENT_DGST_ERR : 0;
-               if (skb_ulp_mode(skb) & ULP2_FLAG_DATA_DDPED) {
-                       cxgb3i_ddp_debug("opcode 0x%x, data %u, ddp'ed.\n",
-                                        hdr->opcode & ISCSI_OPCODE_MASK,
-                                        tcp_conn->in.datalen);
+               if (skb_ulp_mode(skb) & ULP2_FLAG_DATA_DDPED)
                        segment->total_copied = segment->total_size;
-               } else
+               else {
+                       cxgb3i_ddp_debug("opcode 0x%x, data %u, NOT ddp'ed, "
+                                        "itt 0x%x.\n",
+                                        hdr->opcode & ISCSI_OPCODE_MASK,
+                                        tcp_conn->in.datalen, hdr->itt);
                        offset += sizeof(struct cpl_iscsi_hdr_norss);
+               }
 
                while (segment->total_copied < segment->total_size) {
                        iscsi_tcp_segment_map(segment, 1);
@@ -481,67 +547,64 @@ int cxgb3i_conn_ulp2_xmit(struct iscsi_conn *conn)
                        if (padlen)
                                memset(dst, 0, padlen);
                } else {
-                       unsigned int offset = 0;
-                       while (datalen) {
-                               struct page *page = alloc_page(GFP_ATOMIC);
-                               int idx = skb_shinfo(skb)->nr_frags;
-                               skb_frag_t *frag = &skb_shinfo(skb)->frags[idx];
-
-                               if (!page)
-                                       goto free_skb;
-
-                               frag->page = page;
-                               frag->page_offset = 0;
-                               if (datalen > PAGE_SIZE)
-                                       frag->size = PAGE_SIZE;
-                               else
-                                       frag->size = datalen;
-                               memcpy(page_address(page),
-                                      data_seg->data + offset, frag->size);
-
-                               skb_shinfo(skb)->nr_frags++;
-                               datalen -= frag->size;
-                               offset += frag->size;
-                       }
+                       struct page *pg = virt_to_page(data_seg->data);
+
+                       get_page(pg);
+                       skb_fill_page_desc(skb, 0, pg,
+                                          offset_in_page(data_seg->data),
+                                          datalen);
+                       skb->len += datalen;
+                       skb->data_len += datalen;
+                       skb->truesize += datalen;
                }
        } else {
                struct scatterlist *sg = data_seg->sg;
                unsigned int offset = data_seg->sg_offset;
-               while (datalen) {
-                       int idx = skb_shinfo(skb)->nr_frags;
-                       skb_frag_t *frag = &skb_shinfo(skb)->frags[idx];
-                       struct page *pg = sg_page(sg);
-
-                       get_page(pg);
-                       frag->page = pg;
-                       frag->page_offset = offset + sg->offset;
-                       frag->size = min(sg->length, datalen);
-
-                       offset = 0;
-                       skb_shinfo(skb)->nr_frags++;
-                       datalen -= frag->size;
-                       sg = sg_next(sg);
-               }
+               struct page *page = sg_page(sg);
+               unsigned int sglen = sg->length - offset;
+
+               do {
+                       int i = skb_shinfo(skb)->nr_frags;
+                       unsigned int copy;
+
+                       if (!sglen) {
+                               sg = sg_next(sg);
+                               page = sg_page(sg);
+                               offset = 0;
+                               sglen = sg->length;
+                       }
+                       copy = min(sglen, datalen);
+
+                       if (i && skb_can_coalesce(skb, i, page,
+                                                 sg->offset + offset)) {
+                               skb_shinfo(skb)->frags[i - 1].size += copy;
+                       } else {
+                               get_page(page);
+                               skb_fill_page_desc(skb, i, page,
+                                                  sg->offset + offset, copy);
+                       }
+                       skb->len += copy;
+                       skb->data_len += copy;
+                       skb->truesize += copy;
+                       offset += copy;
+                       sglen -= copy;
+                       datalen -= copy;
+               } while (datalen);
        }
 
-       if (skb_shinfo(skb)->nr_frags) {
-               if (padlen) {
-                       int idx = skb_shinfo(skb)->nr_frags;
-                       skb_frag_t *frag = &skb_shinfo(skb)->frags[idx];
-                       frag->page = pad_page;
-                       frag->page_offset = 0;
-                       frag->size = padlen;
-                       skb_shinfo(skb)->nr_frags++;
-               }
-               datalen = data_seg->total_size + padlen;
-               skb->data_len += datalen;
-               skb->truesize += datalen;
-               skb->len += datalen;
+       if (padlen && skb_shinfo(skb)->nr_frags) {
+               int idx = skb_shinfo(skb)->nr_frags;
+               get_page(pad_page);
+               skb_fill_page_desc(skb, idx, pad_page, 0, padlen);
+               skb->data_len += padlen;
+               skb->truesize += padlen;
+               skb->len += padlen;
        }
 
 send_pdu:
        err = cxgb3i_c3cn_send_pdus((struct s3_conn *)tcp_conn->sock,
                                    skb, MSG_DONTWAIT | MSG_NOSIGNAL);
+
        if (err > 0) {
                int pdulen = hdrlen + datalen + padlen;
                if (conn->hdrdgst_en)
@@ -556,7 +619,6 @@ send_pdu:
                return pdulen;
        }
 
-free_skb:
        kfree_skb(skb);
        if (err < 0 && err != -EAGAIN) {
                cxgb3i_log_error("conn 0x%p, xmit err %d.\n", conn, err);
@@ -649,13 +711,11 @@ void cxgb3i_conn_closing(struct s3_conn *c3cn)
 int cxgb3i_adapter_ulp_init(struct cxgb3i_adapter *snic)
 {
        struct t3cdev *tdev = snic->tdev;
-       struct cxgb3i_ddp_info *ddp = &snic->ddp;
+       struct cxgb3i_ddp_info *ddp;
        struct ulp_iscsi_info uinfo;
        unsigned int ppmax, bits, max_bits;
        int i, err;
 
-       spin_lock_init(&ddp->map_lock);
-
        err = tdev->ctl(tdev, ULP_ISCSI_GET_PARAMS, &uinfo);
        if (err < 0) {
                cxgb3i_log_error("%s, failed to get iscsi param err=%d.\n",
@@ -684,12 +744,21 @@ int cxgb3i_adapter_ulp_init(struct cxgb3i_adapter *snic)
        snic->tag_format.rsvd_tag_mask =
                (1 << (snic->tag_format.rsvd_bits + PPOD_IDX_SHIFT)) - 1;
 
-       ddp->map = cxgb3i_alloc_big_mem(ppmax);
-       if (!ddp->map) {
-               cxgb3i_log_warn("snic unable to alloc ddp ppod 0x%u, "
-                               "ddp disabled.\n", ppmax);
+       ddp = cxgb3i_alloc_big_mem(sizeof(struct cxgb3i_ddp_info) +
+                                  ppmax *
+                                  (sizeof(struct cxgb3i_gather_list *) +
+                                   sizeof(struct sk_buff *)));
+       if (!ddp) {
+               cxgb3i_log_warn("snic %s unable to alloc ddp ppod 0x%u, "
+                               "ddp disabled.\n", tdev->name, ppmax);
                return 0;
        }
+       ddp->gl_map = (struct cxgb3i_gather_list **)(ddp + 1);
+       ddp->gl_skb = (struct sk_buff **)(((char *)ddp->gl_map) +
+                                         ppmax *
+                                         sizeof(struct cxgb3i_gather_list *));
+
+       spin_lock_init(&ddp->map_lock);
        ddp->llimit = uinfo.llimit;
        ddp->ulimit = uinfo.ulimit;
 
@@ -710,7 +779,7 @@ int cxgb3i_adapter_ulp_init(struct cxgb3i_adapter *snic)
        ddp->nppods = ppmax;
        ddp->idx_last = ppmax;
 
-       tdev->ulp_iscsi = ddp;
+       tdev->ulp_iscsi = snic->ddp = ddp;
 
        cxgb3i_log_info("snic nppods %u (0x%x ~ 0x%x), rsvd shift %u, "
                        "bits %u, mask 0x%x, 0x%x, pkt %u,%u.\n",
@@ -723,19 +792,33 @@ int cxgb3i_adapter_ulp_init(struct cxgb3i_adapter *snic)
        return 0;
 
 free_ppod_map:
-       cxgb3i_free_big_mem(ddp->map);
+       cxgb3i_free_big_mem(ddp);
        return 0;
 }
 
 void cxgb3i_adapter_ulp_cleanup(struct cxgb3i_adapter *snic)
 {
-       u8 *map = snic->ddp.map;
+       struct cxgb3i_ddp_info *ddp = snic->ddp;
+
+       if (ddp) {
+               int i = 0;
 
-       if (map) {
                snic->tdev->ulp_iscsi = NULL;
                spin_lock(&snic->lock);
-               snic->ddp.map = NULL;
+               snic->ddp = NULL;
                spin_unlock(&snic->lock);
-               cxgb3i_free_big_mem(map);
+
+               while (i < ddp->nppods) {
+                       struct cxgb3i_gather_list *gl = ddp->gl_map[i];
+                       if (gl) {
+                               int npods = (gl->nelem + PPOD_PAGES_MAX - 1)
+                                            >> PPOD_PAGES_SHIFT;
+
+                               kfree(gl);
+                               ddp_free_gl_skb(ddp, i, npods);
+                       } else
+                               i++;
+               }
+               cxgb3i_free_big_mem(ddp);
        }
 }
index 6b0410e..025bfbf 100644 (file)
@@ -24,6 +24,8 @@
 #include <linux/interrupt.h>
 #include <linux/blkdev.h>
 #include <linux/delay.h>
+#include <linux/netlink.h>
+#include <net/netlink.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
@@ -33,6 +35,7 @@
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_netlink_ml.h>
 
 #include "scsi_priv.h"
 #include "scsi_logging.h"
@@ -226,6 +229,80 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
 }
 #endif
 
+#ifdef CONFIG_SCSI_NETLINK
+/**
+ * scsi_post_sense_event - called to post a 'Sense Code' event
+ *
+ * @sdev:              SCSI device the sense code occured on
+ * @sshdr:             SCSI sense code
+ *
+ * Returns:
+ *   0 on succesful return
+ *   otherwise, failing error code
+ *
+ */
+static void scsi_post_sense_event(struct scsi_device *sdev,
+                       struct scsi_sense_hdr *sshdr)
+{
+       struct sk_buff *skb;
+       struct nlmsghdr *nlh;
+       struct scsi_nl_sense_msg *msg;
+       u32 len, skblen;
+       int err;
+
+       if (!scsi_nl_sock) {
+               err = -ENOENT;
+               goto send_fail;
+       }
+
+       len = SCSI_NL_MSGALIGN(sizeof(*msg));
+       skblen = NLMSG_SPACE(len);
+
+       skb = alloc_skb(skblen, GFP_KERNEL);
+       if (!skb) {
+               err = -ENOBUFS;
+               goto send_fail;
+       }
+
+       nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG,
+                               skblen - sizeof(*nlh), 0);
+       if (!nlh) {
+               err = -ENOBUFS;
+               goto send_fail_skb;
+       }
+       msg = NLMSG_DATA(nlh);
+
+       INIT_SCSI_NL_HDR(&msg->snlh, SCSI_NL_TRANSPORT_ML,
+                        ML_NL_SCSI_SENSE, len);
+       msg->host_no = sdev->host->host_no;
+       msg->channel = sdev->channel;
+       msg->id = sdev->id;
+       msg->lun = sdev->lun;
+       msg->sense = (sshdr->response_code << 24) | (sshdr->sense_key << 16) |
+               (sshdr->asc << 8) | sshdr->ascq;
+
+       err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_ML_EVENTS,
+                             GFP_KERNEL);
+       if (err && (err != -ESRCH))
+               /* nlmsg_multicast already kfree_skb'd */
+               goto send_fail;
+
+       return;
+
+send_fail_skb:
+       kfree_skb(skb);
+send_fail:
+       sdev_printk(KERN_WARNING, sdev,
+                   "Dropped SCSI Msg %02x/%02x/%02x/%02x: err %d\n",
+                   sshdr->response_code, sshdr->sense_key,
+                   sshdr->asc, sshdr->ascq, err);
+       return;
+}
+#else
+static inline void scsi_post_sense_event(struct scsi_device *sdev,
+                          struct scsi_sense_hdr *sshdr) {}
+#endif
+
 /**
  * scsi_check_sense - Examine scsi cmd sense
  * @scmd:      Cmd to have sense checked.
@@ -248,6 +325,8 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
        if (scsi_sense_is_deferred(&sshdr))
                return NEEDS_RETRY;
 
+       scsi_post_sense_event(sdev, &sshdr);
+
        if (sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh &&
                        sdev->scsi_dh_data->scsi_dh->check_sense) {
                int rc;
index 536752c..ac7c766 100644 (file)
@@ -35,7 +35,8 @@
 /* SCSI Transport Broadcast Groups */
        /* leaving groups 0 and 1 unassigned */
 #define SCSI_NL_GRP_FC_EVENTS          (1<<2)          /* Group 2 */
-#define SCSI_NL_GRP_CNT                        3
+#define SCSI_NL_GRP_ML_EVENTS          (1<<3)          /* Group 3 */
+#define SCSI_NL_GRP_CNT                        4
 
 
 /* SCSI_TRANSPORT_MSG event message header */
@@ -56,7 +57,8 @@ struct scsi_nl_hdr {
 /* scsi_nl_hdr->transport value */
 #define SCSI_NL_TRANSPORT                      0
 #define SCSI_NL_TRANSPORT_FC                   1
-#define SCSI_NL_MAX_TRANSPORTS                 2
+#define SCSI_NL_TRANSPORT_ML                   2
+#define SCSI_NL_MAX_TRANSPORTS                 3
 
 /* Transport-based scsi_nl_hdr->msgtype values are defined in each transport */
 
diff --git a/include/scsi/scsi_netlink_ml.h b/include/scsi/scsi_netlink_ml.h
new file mode 100644 (file)
index 0000000..c988458
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ *  SCSI Midlayer Netlink Interface
+ *
+ *  Copyright (C) 2008 Hannes Reinecke, SuSE Linux Products GmbH
+ *
+ *  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; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#ifndef SCSI_NETLINK_ML_H
+#define SCSI_NETLINK_ML_H
+
+#include <scsi/scsi_netlink.h>
+
+/*
+ * This file intended to be included by both kernel and user space
+ */
+
+/*
+ * FC Transport Message Types
+ */
+       /* kernel -> user */
+#define ML_NL_SCSI_SENSE                       0x0100
+       /* user -> kernel */
+/* none */
+
+
+/*
+ * Message Structures :
+ */
+
+/* macro to round up message lengths to 8byte boundary */
+#define SCSI_NL_MSGALIGN(len)          (((len) + 7) & ~7)
+
+
+/*
+ * SCSI Midlayer SCSI Sense messages :
+ *   SCSI_NL_SCSI_SENSE
+ *
+ */
+struct scsi_nl_sense_msg {
+       struct scsi_nl_hdr snlh;                /* must be 1st element ! */
+       uint64_t seconds;
+       u64 id;
+       u64 lun;
+       u16 host_no;
+       u16 channel;
+       u32 sense;
+} __attribute__((aligned(sizeof(uint64_t))));
+
+
+#endif /* SCSI_NETLINK_ML_H */
+