diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-06 13:24:49 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-06 13:24:49 -0700 |
commit | 22eb5aa6c7940861f9603581665b9d9a1c60be30 (patch) | |
tree | 22890bcebae5647bcc1a29e7b544a1c5de2b1f8b /drivers/scsi/cxgb3i | |
parent | d7ca6f8cdffa5765e486edb3dada9121fba8e6aa (diff) | |
parent | 015640edb1f346e0b2eda703587c4cd1c310ec1d (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (74 commits)
[SCSI] sg: fix q->queue_lock on scsi_error_handler path
[SCSI] replace __inline with inline
[SCSI] a2091: make 2 functions static
[SCSI] a3000: make 2 functions static
[SCSI] ses: #if 0 the unused ses_match_host()
[SCSI] use kmem_cache_zalloc instead of kmem_cache_alloc/memset
[SCSI] sg: fix iovec bugs introduced by the block layer conversion
[SCSI] qlogicpti: use request_firmware
[SCSI] advansys: use request_firmware
[SCSI] qla1280: use request_firmware
[SCSI] libiscsi: fix iscsi pool error path
[SCSI] cxgb3i: call ddp release function directly
[SCSI] cxgb3i: merge cxgb3i_ddp into cxgb3i module
[SCSI] cxgb3i: close all tcp connections upon chip reset
[SCSI] cxgb3i: re-read ddp settings information after chip reset
[SCSI] cxgb3i: re-initialize ddp settings after chip reset
[SCSI] cxgb3i: subscribe to error notification from cxgb3 driver
[SCSI] aacraid driver update
[SCSI] mptsas: remove unneeded check
[SCSI] config: Make need for SCSI_CDROM clearer
...
Diffstat (limited to 'drivers/scsi/cxgb3i')
-rw-r--r-- | drivers/scsi/cxgb3i/Kbuild | 4 | ||||
-rw-r--r-- | drivers/scsi/cxgb3i/cxgb3i.h | 10 | ||||
-rw-r--r-- | drivers/scsi/cxgb3i/cxgb3i_ddp.c | 275 | ||||
-rw-r--r-- | drivers/scsi/cxgb3i/cxgb3i_ddp.h | 6 | ||||
-rw-r--r-- | drivers/scsi/cxgb3i/cxgb3i_init.c | 31 | ||||
-rw-r--r-- | drivers/scsi/cxgb3i/cxgb3i_iscsi.c | 129 | ||||
-rw-r--r-- | drivers/scsi/cxgb3i/cxgb3i_offload.c | 101 | ||||
-rw-r--r-- | drivers/scsi/cxgb3i/cxgb3i_offload.h | 13 |
8 files changed, 306 insertions, 263 deletions
diff --git a/drivers/scsi/cxgb3i/Kbuild b/drivers/scsi/cxgb3i/Kbuild index ee7d6d2f9c3..25a2032bfa2 100644 --- a/drivers/scsi/cxgb3i/Kbuild +++ b/drivers/scsi/cxgb3i/Kbuild @@ -1,4 +1,4 @@ EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/cxgb3 -cxgb3i-y := cxgb3i_init.o cxgb3i_iscsi.o cxgb3i_pdu.o cxgb3i_offload.o -obj-$(CONFIG_SCSI_CXGB3_ISCSI) += cxgb3i_ddp.o cxgb3i.o +cxgb3i-y := cxgb3i_init.o cxgb3i_iscsi.o cxgb3i_pdu.o cxgb3i_offload.o cxgb3i_ddp.o +obj-$(CONFIG_SCSI_CXGB3_ISCSI) += cxgb3i.o diff --git a/drivers/scsi/cxgb3i/cxgb3i.h b/drivers/scsi/cxgb3i/cxgb3i.h index a7cf550b9cc..d362860e750 100644 --- a/drivers/scsi/cxgb3i/cxgb3i.h +++ b/drivers/scsi/cxgb3i/cxgb3i.h @@ -66,10 +66,12 @@ struct cxgb3i_hba { * @pdev: pointer to pci dev * @hba_cnt: # of hbas (the same as # of ports) * @hba: all the hbas on this adapter + * @flags: bit flag for adapter event/status * @tx_max_size: max. tx packet size supported * @rx_max_size: max. rx packet size supported * @tag_format: ddp tag format settings */ +#define CXGB3I_ADAPTER_FLAG_RESET 0x1 struct cxgb3i_adapter { struct list_head list_head; spinlock_t lock; @@ -78,6 +80,7 @@ struct cxgb3i_adapter { unsigned char hba_cnt; struct cxgb3i_hba *hba[MAX_NPORTS]; + unsigned int flags; unsigned int tx_max_size; unsigned int rx_max_size; @@ -137,10 +140,9 @@ struct cxgb3i_task_data { int cxgb3i_iscsi_init(void); void cxgb3i_iscsi_cleanup(void); -struct cxgb3i_adapter *cxgb3i_adapter_add(struct t3cdev *); -void cxgb3i_adapter_remove(struct t3cdev *); -int cxgb3i_adapter_ulp_init(struct cxgb3i_adapter *); -void cxgb3i_adapter_ulp_cleanup(struct cxgb3i_adapter *); +struct cxgb3i_adapter *cxgb3i_adapter_find_by_tdev(struct t3cdev *); +void cxgb3i_adapter_open(struct t3cdev *); +void cxgb3i_adapter_close(struct t3cdev *); struct cxgb3i_hba *cxgb3i_hba_find_by_netdev(struct net_device *); struct cxgb3i_hba *cxgb3i_hba_host_add(struct cxgb3i_adapter *, diff --git a/drivers/scsi/cxgb3i/cxgb3i_ddp.c b/drivers/scsi/cxgb3i/cxgb3i_ddp.c index 4eb6f5593b3..d06a661c209 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_ddp.c +++ b/drivers/scsi/cxgb3i/cxgb3i_ddp.c @@ -23,19 +23,6 @@ #include "cxgb3i_ddp.h" -#define DRV_MODULE_NAME "cxgb3i_ddp" -#define DRV_MODULE_VERSION "1.0.0" -#define DRV_MODULE_RELDATE "Dec. 1, 2008" - -static char version[] = - "Chelsio S3xx iSCSI DDP " DRV_MODULE_NAME - " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; - -MODULE_AUTHOR("Karen Xie <kxie@chelsio.com>"); -MODULE_DESCRIPTION("cxgb3i ddp pagepod manager"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_MODULE_VERSION); - #define ddp_log_error(fmt...) printk(KERN_ERR "cxgb3i_ddp: ERR! " fmt) #define ddp_log_warn(fmt...) printk(KERN_WARNING "cxgb3i_ddp: WARN! " fmt) #define ddp_log_info(fmt...) printk(KERN_INFO "cxgb3i_ddp: " fmt) @@ -66,9 +53,6 @@ static unsigned char ddp_page_order[DDP_PGIDX_MAX] = {0, 1, 2, 4}; static unsigned char ddp_page_shift[DDP_PGIDX_MAX] = {12, 13, 14, 16}; static unsigned char page_idx = DDP_PGIDX_MAX; -static LIST_HEAD(cxgb3i_ddp_list); -static DEFINE_RWLOCK(cxgb3i_ddp_rwlock); - /* * functions to program the pagepod in h/w */ @@ -113,8 +97,8 @@ static int set_ddp_map(struct cxgb3i_ddp_info *ddp, struct pagepod_hdr *hdr, return 0; } -static int clear_ddp_map(struct cxgb3i_ddp_info *ddp, unsigned int idx, - unsigned int npods) +static void clear_ddp_map(struct cxgb3i_ddp_info *ddp, unsigned int tag, + unsigned int idx, unsigned int npods) { unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ddp->llimit; int i; @@ -122,13 +106,17 @@ static int clear_ddp_map(struct cxgb3i_ddp_info *ddp, unsigned int idx, for (i = 0; i < npods; i++, idx++, pm_addr += PPOD_SIZE) { struct sk_buff *skb = ddp->gl_skb[idx]; + if (!skb) { + ddp_log_error("ddp tag 0x%x, 0x%x, %d/%u, skb NULL.\n", + tag, idx, i, npods); + continue; + } 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; cxgb3_ofld_send(ddp->tdev, skb); } - return 0; } static inline int ddp_find_unused_entries(struct cxgb3i_ddp_info *ddp, @@ -211,7 +199,6 @@ int cxgb3i_ddp_find_page_index(unsigned long pgsz) ddp_log_debug("ddp page size 0x%lx not supported.\n", pgsz); return DDP_PGIDX_MAX; } -EXPORT_SYMBOL_GPL(cxgb3i_ddp_find_page_index); static inline void ddp_gl_unmap(struct pci_dev *pdev, struct cxgb3i_gather_list *gl) @@ -334,7 +321,6 @@ error_out: kfree(gl); return NULL; } -EXPORT_SYMBOL_GPL(cxgb3i_ddp_make_gl); /** * cxgb3i_ddp_release_gl - release a page buffer list @@ -348,7 +334,6 @@ void cxgb3i_ddp_release_gl(struct cxgb3i_gather_list *gl, ddp_gl_unmap(pdev, gl); kfree(gl); } -EXPORT_SYMBOL_GPL(cxgb3i_ddp_release_gl); /** * cxgb3i_ddp_tag_reserve - set up ddp for a data transfer @@ -430,7 +415,6 @@ unmark_entries: ddp_unmark_entries(ddp, idx, npods); return err; } -EXPORT_SYMBOL_GPL(cxgb3i_ddp_tag_reserve); /** * cxgb3i_ddp_tag_release - release a ddp tag @@ -453,22 +437,21 @@ void cxgb3i_ddp_tag_release(struct t3cdev *tdev, u32 tag) struct cxgb3i_gather_list *gl = ddp->gl_map[idx]; unsigned int npods; - if (!gl) { - ddp_log_error("release ddp 0x%x, idx 0x%x, gl NULL.\n", - tag, idx); + if (!gl || !gl->nelem) { + ddp_log_error("release 0x%x, idx 0x%x, gl 0x%p, %u.\n", + tag, idx, gl, gl ? gl->nelem : 0); return; } npods = (gl->nelem + PPOD_PAGES_MAX - 1) >> PPOD_PAGES_SHIFT; ddp_log_debug("ddp tag 0x%x, release idx 0x%x, npods %u.\n", tag, idx, npods); - clear_ddp_map(ddp, idx, npods); + clear_ddp_map(ddp, tag, idx, npods); ddp_unmark_entries(ddp, idx, npods); cxgb3i_ddp_release_gl(gl, ddp->pdev); } else ddp_log_error("ddp tag 0x%x, idx 0x%x > max 0x%x.\n", tag, idx, ddp->nppods); } -EXPORT_SYMBOL_GPL(cxgb3i_ddp_tag_release); static int setup_conn_pgidx(struct t3cdev *tdev, unsigned int tid, int pg_idx, int reply) @@ -509,7 +492,6 @@ int cxgb3i_setup_conn_host_pagesize(struct t3cdev *tdev, unsigned int tid, { return setup_conn_pgidx(tdev, tid, page_idx, reply); } -EXPORT_SYMBOL_GPL(cxgb3i_setup_conn_host_pagesize); /** * cxgb3i_setup_conn_pagesize - setup the conn.'s ddp page size @@ -526,7 +508,6 @@ int cxgb3i_setup_conn_pagesize(struct t3cdev *tdev, unsigned int tid, return setup_conn_pgidx(tdev, tid, pgidx, reply); } -EXPORT_SYMBOL_GPL(cxgb3i_setup_conn_pagesize); /** * cxgb3i_setup_conn_digest - setup conn. digest setting @@ -562,26 +543,104 @@ int cxgb3i_setup_conn_digest(struct t3cdev *tdev, unsigned int tid, cxgb3_ofld_send(tdev, skb); return 0; } -EXPORT_SYMBOL_GPL(cxgb3i_setup_conn_digest); -static int ddp_init(struct t3cdev *tdev) + +/** + * cxgb3i_adapter_ddp_info - read the adapter's ddp information + * @tdev: t3cdev adapter + * @tformat: tag format + * @txsz: max tx pdu payload size, filled in by this func. + * @rxsz: max rx pdu payload size, filled in by this func. + * setup the tag format for a given iscsi entity + */ +int cxgb3i_adapter_ddp_info(struct t3cdev *tdev, + struct cxgb3i_tag_format *tformat, + unsigned int *txsz, unsigned int *rxsz) +{ + struct cxgb3i_ddp_info *ddp; + unsigned char idx_bits; + + if (!tformat) + return -EINVAL; + + if (!tdev->ulp_iscsi) + return -EINVAL; + + ddp = (struct cxgb3i_ddp_info *)tdev->ulp_iscsi; + + idx_bits = 32 - tformat->sw_bits; + tformat->rsvd_bits = ddp->idx_bits; + tformat->rsvd_shift = PPOD_IDX_SHIFT; + tformat->rsvd_mask = (1 << tformat->rsvd_bits) - 1; + + ddp_log_info("tag format: sw %u, rsvd %u,%u, mask 0x%x.\n", + tformat->sw_bits, tformat->rsvd_bits, + tformat->rsvd_shift, tformat->rsvd_mask); + + *txsz = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD, + ddp->max_txsz - ISCSI_PDU_NONPAYLOAD_LEN); + *rxsz = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD, + ddp->max_rxsz - ISCSI_PDU_NONPAYLOAD_LEN); + ddp_log_info("max payload size: %u/%u, %u/%u.\n", + *txsz, ddp->max_txsz, *rxsz, ddp->max_rxsz); + return 0; +} + +/** + * cxgb3i_ddp_cleanup - release the cxgb3 adapter's ddp resource + * @tdev: t3cdev adapter + * release all the resource held by the ddp pagepod manager for a given + * adapter if needed + */ +void cxgb3i_ddp_cleanup(struct t3cdev *tdev) +{ + int i = 0; + struct cxgb3i_ddp_info *ddp = (struct cxgb3i_ddp_info *)tdev->ulp_iscsi; + + ddp_log_info("t3dev 0x%p, release ddp 0x%p.\n", tdev, ddp); + + if (ddp) { + tdev->ulp_iscsi = NULL; + 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; + ddp_log_info("t3dev 0x%p, ddp %d + %d.\n", + tdev, i, npods); + kfree(gl); + ddp_free_gl_skb(ddp, i, npods); + i += npods; + } else + i++; + } + cxgb3i_free_big_mem(ddp); + } +} + +/** + * ddp_init - initialize the cxgb3 adapter's ddp resource + * @tdev: t3cdev adapter + * initialize the ddp pagepod manager for a given adapter + */ +static void ddp_init(struct t3cdev *tdev) { struct cxgb3i_ddp_info *ddp; struct ulp_iscsi_info uinfo; unsigned int ppmax, bits; int i, err; - static int vers_printed; - if (!vers_printed) { - printk(KERN_INFO "%s", version); - vers_printed = 1; + if (tdev->ulp_iscsi) { + ddp_log_warn("t3dev 0x%p, ddp 0x%p already set up.\n", + tdev, tdev->ulp_iscsi); + return; } err = tdev->ctl(tdev, ULP_ISCSI_GET_PARAMS, &uinfo); if (err < 0) { ddp_log_error("%s, failed to get iscsi param err=%d.\n", tdev->name, err); - return err; + return; } ppmax = (uinfo.ulimit - uinfo.llimit + 1) >> PPOD_SIZE_SHIFT; @@ -598,7 +657,7 @@ static int ddp_init(struct t3cdev *tdev) if (!ddp) { ddp_log_warn("%s unable to alloc ddp 0x%d, ddp disabled.\n", tdev->name, ppmax); - return 0; + return; } ddp->gl_map = (struct cxgb3i_gather_list **)(ddp + 1); ddp->gl_skb = (struct sk_buff **)(((char *)ddp->gl_map) + @@ -632,142 +691,26 @@ static int ddp_init(struct t3cdev *tdev) tdev->ulp_iscsi = ddp; - /* add to the list */ - write_lock(&cxgb3i_ddp_rwlock); - list_add_tail(&ddp->list, &cxgb3i_ddp_list); - write_unlock(&cxgb3i_ddp_rwlock); - - ddp_log_info("nppods %u (0x%x ~ 0x%x), bits %u, mask 0x%x,0x%x " - "pkt %u/%u, %u/%u.\n", - ppmax, ddp->llimit, ddp->ulimit, ddp->idx_bits, - ddp->idx_mask, ddp->rsvd_tag_mask, - ddp->max_txsz, uinfo.max_txsz, + ddp_log_info("tdev 0x%p, nppods %u, bits %u, mask 0x%x,0x%x pkt %u/%u," + " %u/%u.\n", + tdev, ppmax, ddp->idx_bits, ddp->idx_mask, + ddp->rsvd_tag_mask, ddp->max_txsz, uinfo.max_txsz, ddp->max_rxsz, uinfo.max_rxsz); - return 0; + return; free_ddp_map: cxgb3i_free_big_mem(ddp); - return err; -} - -/** - * cxgb3i_adapter_ddp_init - initialize the adapter's ddp resource - * @tdev: t3cdev adapter - * @tformat: tag format - * @txsz: max tx pdu payload size, filled in by this func. - * @rxsz: max rx pdu payload size, filled in by this func. - * initialize the ddp pagepod manager for a given adapter if needed and - * setup the tag format for a given iscsi entity - */ -int cxgb3i_adapter_ddp_init(struct t3cdev *tdev, - struct cxgb3i_tag_format *tformat, - unsigned int *txsz, unsigned int *rxsz) -{ - struct cxgb3i_ddp_info *ddp; - unsigned char idx_bits; - - if (!tformat) - return -EINVAL; - - if (!tdev->ulp_iscsi) { - int err = ddp_init(tdev); - if (err < 0) - return err; - } - ddp = (struct cxgb3i_ddp_info *)tdev->ulp_iscsi; - - idx_bits = 32 - tformat->sw_bits; - tformat->rsvd_bits = ddp->idx_bits; - tformat->rsvd_shift = PPOD_IDX_SHIFT; - tformat->rsvd_mask = (1 << tformat->rsvd_bits) - 1; - - ddp_log_info("tag format: sw %u, rsvd %u,%u, mask 0x%x.\n", - tformat->sw_bits, tformat->rsvd_bits, - tformat->rsvd_shift, tformat->rsvd_mask); - - *txsz = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD, - ddp->max_txsz - ISCSI_PDU_NONPAYLOAD_LEN); - *rxsz = min_t(unsigned int, ULP2_MAX_PDU_PAYLOAD, - ddp->max_rxsz - ISCSI_PDU_NONPAYLOAD_LEN); - ddp_log_info("max payload size: %u/%u, %u/%u.\n", - *txsz, ddp->max_txsz, *rxsz, ddp->max_rxsz); - return 0; -} -EXPORT_SYMBOL_GPL(cxgb3i_adapter_ddp_init); - -static void ddp_release(struct cxgb3i_ddp_info *ddp) -{ - int i = 0; - struct t3cdev *tdev = ddp->tdev; - - tdev->ulp_iscsi = NULL; - 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); -} - -/** - * cxgb3i_adapter_ddp_cleanup - release the adapter's ddp resource - * @tdev: t3cdev adapter - * release all the resource held by the ddp pagepod manager for a given - * adapter if needed - */ -void cxgb3i_adapter_ddp_cleanup(struct t3cdev *tdev) -{ - struct cxgb3i_ddp_info *ddp; - - /* remove from the list */ - write_lock(&cxgb3i_ddp_rwlock); - list_for_each_entry(ddp, &cxgb3i_ddp_list, list) { - if (ddp->tdev == tdev) { - list_del(&ddp->list); - break; - } - } - write_unlock(&cxgb3i_ddp_rwlock); - - if (ddp) - ddp_release(ddp); -} -EXPORT_SYMBOL_GPL(cxgb3i_adapter_ddp_cleanup); - -/** - * cxgb3i_ddp_init_module - module init entry point - * initialize any driver wide global data structures - */ -static int __init cxgb3i_ddp_init_module(void) -{ - page_idx = cxgb3i_ddp_find_page_index(PAGE_SIZE); - ddp_log_info("system PAGE_SIZE %lu, ddp idx %u.\n", - PAGE_SIZE, page_idx); - return 0; } /** - * cxgb3i_ddp_exit_module - module cleanup/exit entry point - * go through the ddp list and release any resource held. + * cxgb3i_ddp_init - initialize ddp functions */ -static void __exit cxgb3i_ddp_exit_module(void) +void cxgb3i_ddp_init(struct t3cdev *tdev) { - struct cxgb3i_ddp_info *ddp; - - /* release all ddp manager if there is any */ - write_lock(&cxgb3i_ddp_rwlock); - list_for_each_entry(ddp, &cxgb3i_ddp_list, list) { - list_del(&ddp->list); - ddp_release(ddp); + if (page_idx == DDP_PGIDX_MAX) { + page_idx = cxgb3i_ddp_find_page_index(PAGE_SIZE); + ddp_log_info("system PAGE_SIZE %lu, ddp idx %u.\n", + PAGE_SIZE, page_idx); } - write_unlock(&cxgb3i_ddp_rwlock); + ddp_init(tdev); } - -module_init(cxgb3i_ddp_init_module); -module_exit(cxgb3i_ddp_exit_module); diff --git a/drivers/scsi/cxgb3i/cxgb3i_ddp.h b/drivers/scsi/cxgb3i/cxgb3i_ddp.h index 75a63a81e87..0d296de7cf3 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_ddp.h +++ b/drivers/scsi/cxgb3i/cxgb3i_ddp.h @@ -301,7 +301,9 @@ int cxgb3i_setup_conn_pagesize(struct t3cdev *, unsigned int tid, int reply, int cxgb3i_setup_conn_digest(struct t3cdev *, unsigned int tid, int hcrc, int dcrc, int reply); int cxgb3i_ddp_find_page_index(unsigned long pgsz); -int cxgb3i_adapter_ddp_init(struct t3cdev *, struct cxgb3i_tag_format *, +int cxgb3i_adapter_ddp_info(struct t3cdev *, struct cxgb3i_tag_format *, unsigned int *txsz, unsigned int *rxsz); -void cxgb3i_adapter_ddp_cleanup(struct t3cdev *); + +void cxgb3i_ddp_init(struct t3cdev *); +void cxgb3i_ddp_cleanup(struct t3cdev *); #endif diff --git a/drivers/scsi/cxgb3i/cxgb3i_init.c b/drivers/scsi/cxgb3i/cxgb3i_init.c index 1ce9f244e46..042d9bce991 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_init.c +++ b/drivers/scsi/cxgb3i/cxgb3i_init.c @@ -12,8 +12,8 @@ #include "cxgb3i.h" #define DRV_MODULE_NAME "cxgb3i" -#define DRV_MODULE_VERSION "1.0.1" -#define DRV_MODULE_RELDATE "Jan. 2009" +#define DRV_MODULE_VERSION "1.0.2" +#define DRV_MODULE_RELDATE "Mar. 2009" static char version[] = "Chelsio S3xx iSCSI Driver " DRV_MODULE_NAME @@ -26,6 +26,7 @@ MODULE_VERSION(DRV_MODULE_VERSION); static void open_s3_dev(struct t3cdev *); static void close_s3_dev(struct t3cdev *); +static void s3_err_handler(struct t3cdev *tdev, u32 status, u32 error); static cxgb3_cpl_handler_func cxgb3i_cpl_handlers[NUM_CPL_CMDS]; static struct cxgb3_client t3c_client = { @@ -33,6 +34,7 @@ static struct cxgb3_client t3c_client = { .handlers = cxgb3i_cpl_handlers, .add = open_s3_dev, .remove = close_s3_dev, + .err_handler = s3_err_handler, }; /** @@ -48,8 +50,9 @@ static void open_s3_dev(struct t3cdev *t3dev) vers_printed = 1; } + cxgb3i_ddp_init(t3dev); cxgb3i_sdev_add(t3dev, &t3c_client); - cxgb3i_adapter_add(t3dev); + cxgb3i_adapter_open(t3dev); } /** @@ -58,8 +61,28 @@ static void open_s3_dev(struct t3cdev *t3dev) */ static void close_s3_dev(struct t3cdev *t3dev) { - cxgb3i_adapter_remove(t3dev); + cxgb3i_adapter_close(t3dev); cxgb3i_sdev_remove(t3dev); + cxgb3i_ddp_cleanup(t3dev); +} + +static void s3_err_handler(struct t3cdev *tdev, u32 status, u32 error) +{ + struct cxgb3i_adapter *snic = cxgb3i_adapter_find_by_tdev(tdev); + + cxgb3i_log_info("snic 0x%p, tdev 0x%p, status 0x%x, err 0x%x.\n", + snic, tdev, status, error); + if (!snic) + return; + + switch (status) { + case OFFLOAD_STATUS_DOWN: + snic->flags |= CXGB3I_ADAPTER_FLAG_RESET; + break; + case OFFLOAD_STATUS_UP: + snic->flags &= ~CXGB3I_ADAPTER_FLAG_RESET; + break; + } } /** diff --git a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c index e185dedc4c1..fff8e432764 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_iscsi.c +++ b/drivers/scsi/cxgb3i/cxgb3i_iscsi.c @@ -53,36 +53,52 @@ static LIST_HEAD(cxgb3i_snic_list); static DEFINE_RWLOCK(cxgb3i_snic_rwlock); /** - * cxgb3i_adapter_add - init a s3 adapter structure and any h/w settings - * @t3dev: t3cdev adapter - * return the resulting cxgb3i_adapter struct + * cxgb3i_adpater_find_by_tdev - find the cxgb3i_adapter structure via t3cdev + * @tdev: t3cdev pointer */ -struct cxgb3i_adapter *cxgb3i_adapter_add(struct t3cdev *t3dev) +struct cxgb3i_adapter *cxgb3i_adapter_find_by_tdev(struct t3cdev *tdev) { struct cxgb3i_adapter *snic; - struct adapter *adapter = tdev2adap(t3dev); - int i; - snic = kzalloc(sizeof(*snic), GFP_KERNEL); - if (!snic) { - cxgb3i_api_debug("cxgb3 %s, OOM.\n", t3dev->name); - return NULL; + read_lock(&cxgb3i_snic_rwlock); + list_for_each_entry(snic, &cxgb3i_snic_list, list_head) { + if (snic->tdev == tdev) { + read_unlock(&cxgb3i_snic_rwlock); + return snic; + } } - spin_lock_init(&snic->lock); + read_unlock(&cxgb3i_snic_rwlock); + return NULL; +} + +static inline int adapter_update(struct cxgb3i_adapter *snic) +{ + cxgb3i_log_info("snic 0x%p, t3dev 0x%p, updating.\n", + snic, snic->tdev); + return cxgb3i_adapter_ddp_info(snic->tdev, &snic->tag_format, + &snic->tx_max_size, + &snic->rx_max_size); +} + +static int adapter_add(struct cxgb3i_adapter *snic) +{ + struct t3cdev *t3dev = snic->tdev; + struct adapter *adapter = tdev2adap(t3dev); + int i, err; - snic->tdev = t3dev; snic->pdev = adapter->pdev; snic->tag_format.sw_bits = sw_tag_idx_bits + sw_tag_age_bits; - if (cxgb3i_adapter_ddp_init(t3dev, &snic->tag_format, + err = cxgb3i_adapter_ddp_info(t3dev, &snic->tag_format, &snic->tx_max_size, - &snic->rx_max_size) < 0) - goto free_snic; + &snic->rx_max_size); + if (err < 0) + return err; for_each_port(adapter, i) { snic->hba[i] = cxgb3i_hba_host_add(snic, adapter->port[i]); if (!snic->hba[i]) - goto ulp_cleanup; + return -EINVAL; } snic->hba_cnt = adapter->params.nports; @@ -91,46 +107,71 @@ struct cxgb3i_adapter *cxgb3i_adapter_add(struct t3cdev *t3dev) list_add_tail(&snic->list_head, &cxgb3i_snic_list); write_unlock(&cxgb3i_snic_rwlock); - return snic; + cxgb3i_log_info("t3dev 0x%p open, snic 0x%p, %u scsi hosts added.\n", + t3dev, snic, snic->hba_cnt); + return 0; +} -ulp_cleanup: - cxgb3i_adapter_ddp_cleanup(t3dev); -free_snic: - kfree(snic); - return NULL; +/** + * cxgb3i_adapter_open - init a s3 adapter structure and any h/w settings + * @t3dev: t3cdev adapter + */ +void cxgb3i_adapter_open(struct t3cdev *t3dev) +{ + struct cxgb3i_adapter *snic = cxgb3i_adapter_find_by_tdev(t3dev); + int err; + + if (snic) + err = adapter_update(snic); + else { + snic = kzalloc(sizeof(*snic), GFP_KERNEL); + if (snic) { + spin_lock_init(&snic->lock); + snic->tdev = t3dev; + err = adapter_add(snic); + } else + err = -ENOMEM; + } + + if (err < 0) { + cxgb3i_log_info("snic 0x%p, f 0x%x, t3dev 0x%p open, err %d.\n", + snic, snic ? snic->flags : 0, t3dev, err); + if (snic) { + snic->flags &= ~CXGB3I_ADAPTER_FLAG_RESET; + cxgb3i_adapter_close(t3dev); + } + } } /** - * cxgb3i_adapter_remove - release the resources held and cleanup h/w settings + * cxgb3i_adapter_close - release the resources held and cleanup h/w settings * @t3dev: t3cdev adapter */ -void cxgb3i_adapter_remove(struct t3cdev *t3dev) +void cxgb3i_adapter_close(struct t3cdev *t3dev) { + struct cxgb3i_adapter *snic = cxgb3i_adapter_find_by_tdev(t3dev); int i; - struct cxgb3i_adapter *snic; + + if (!snic || snic->flags & CXGB3I_ADAPTER_FLAG_RESET) { + cxgb3i_log_info("t3dev 0x%p close, snic 0x%p, f 0x%x.\n", + t3dev, snic, snic ? snic->flags : 0); + return; + } /* remove from the list */ write_lock(&cxgb3i_snic_rwlock); - list_for_each_entry(snic, &cxgb3i_snic_list, list_head) { - if (snic->tdev == t3dev) { - list_del(&snic->list_head); - break; - } - } + list_del(&snic->list_head); write_unlock(&cxgb3i_snic_rwlock); - if (snic) { - for (i = 0; i < snic->hba_cnt; i++) { - if (snic->hba[i]) { - cxgb3i_hba_host_remove(snic->hba[i]); - snic->hba[i] = NULL; - } + for (i = 0; i < snic->hba_cnt; i++) { + if (snic->hba[i]) { + cxgb3i_hba_host_remove(snic->hba[i]); + snic->hba[i] = NULL; } - - /* release ddp resources */ - cxgb3i_adapter_ddp_cleanup(snic->tdev); - kfree(snic); } + cxgb3i_log_info("t3dev 0x%p close, snic 0x%p, %u scsi hosts removed.\n", + t3dev, snic, snic->hba_cnt); + kfree(snic); } /** @@ -170,7 +211,8 @@ struct cxgb3i_hba *cxgb3i_hba_host_add(struct cxgb3i_adapter *snic, shost = iscsi_host_alloc(&cxgb3i_host_template, sizeof(struct cxgb3i_hba), 1); if (!shost) { - cxgb3i_log_info("iscsi_host_alloc failed.\n"); + cxgb3i_log_info("snic 0x%p, ndev 0x%p, host_alloc failed.\n", + snic, ndev); return NULL; } @@ -188,7 +230,8 @@ struct cxgb3i_hba *cxgb3i_hba_host_add(struct cxgb3i_adapter *snic, pci_dev_get(snic->pdev); err = iscsi_host_add(shost, &snic->pdev->dev); if (err) { - cxgb3i_log_info("iscsi_host_add failed.\n"); + cxgb3i_log_info("snic 0x%p, ndev 0x%p, host_add failed.\n", + snic, ndev); goto pci_dev_put; } diff --git a/drivers/scsi/cxgb3i/cxgb3i_offload.c b/drivers/scsi/cxgb3i/cxgb3i_offload.c index c2e434e54e2..4d8654cdbda 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_offload.c +++ b/drivers/scsi/cxgb3i/cxgb3i_offload.c @@ -94,29 +94,30 @@ static int c3cn_get_port(struct s3_conn *c3cn, struct cxgb3i_sdev_data *cdata) if (!cdata) goto error_out; - if (c3cn->saddr.sin_port != 0) { - idx = ntohs(c3cn->saddr.sin_port) - cxgb3_sport_base; - if (idx < 0 || idx >= cxgb3_max_connect) - return 0; - if (!test_and_set_bit(idx, cdata->sport_map)) - return -EADDRINUSE; + if (c3cn->saddr.sin_port) { + cxgb3i_log_error("connect, sin_port NON-ZERO %u.\n", + c3cn->saddr.sin_port); + return -EADDRINUSE; } - /* the sport_map_next may not be accurate but that is okay, sport_map - should be */ - start = idx = cdata->sport_map_next; + spin_lock_bh(&cdata->lock); + start = idx = cdata->sport_next; do { if (++idx >= cxgb3_max_connect) idx = 0; - if (!(test_and_set_bit(idx, cdata->sport_map))) { + if (!cdata->sport_conn[idx]) { c3cn->saddr.sin_port = htons(cxgb3_sport_base + idx); - cdata->sport_map_next = idx; + cdata->sport_next = idx; + cdata->sport_conn[idx] = c3cn; + spin_unlock_bh(&cdata->lock); + c3cn_conn_debug("%s reserve port %u.\n", cdata->cdev->name, cxgb3_sport_base + idx); return 0; } } while (idx != start); + spin_unlock_bh(&cdata->lock); error_out: return -EADDRNOTAVAIL; @@ -124,15 +125,19 @@ error_out: static void c3cn_put_port(struct s3_conn *c3cn) { - struct cxgb3i_sdev_data *cdata = CXGB3_SDEV_DATA(c3cn->cdev); + if (!c3cn->cdev) + return; if (c3cn->saddr.sin_port) { + struct cxgb3i_sdev_data *cdata = CXGB3_SDEV_DATA(c3cn->cdev); int idx = ntohs(c3cn->saddr.sin_port) - cxgb3_sport_base; c3cn->saddr.sin_port = 0; if (idx < 0 || idx >= cxgb3_max_connect) return; - clear_bit(idx, cdata->sport_map); + spin_lock_bh(&cdata->lock); + cdata->sport_conn[idx] = NULL; + spin_unlock_bh(&cdata->lock); c3cn_conn_debug("%s, release port %u.\n", cdata->cdev->name, cxgb3_sport_base + idx); } @@ -1305,11 +1310,7 @@ static void c3cn_release_offload_resources(struct s3_conn *c3cn) struct t3cdev *cdev = c3cn->cdev; unsigned int tid = c3cn->tid; - if (!cdev) - return; - c3cn->qset = 0; - c3cn_free_cpl_skbs(c3cn); if (c3cn->wr_avail != c3cn->wr_max) { @@ -1317,18 +1318,22 @@ static void c3cn_release_offload_resources(struct s3_conn *c3cn) reset_wr_list(c3cn); } - if (c3cn->l2t) { - l2t_release(L2DATA(cdev), c3cn->l2t); - c3cn->l2t = NULL; - } - - if (c3cn->state == C3CN_STATE_CONNECTING) /* we have ATID */ - s3_free_atid(cdev, tid); - else { /* we have TID */ - cxgb3_remove_tid(cdev, (void *)c3cn, tid); - c3cn_put(c3cn); + if (cdev) { + if (c3cn->l2t) { + l2t_release(L2DATA(cdev), c3cn->l2t); + c3cn->l2t = NULL; + } + if (c3cn->state == C3CN_STATE_CONNECTING) + /* we have ATID */ + s3_free_atid(cdev, tid); + else { + /* we have TID */ + cxgb3_remove_tid(cdev, (void *)c3cn, tid); + c3cn_put(c3cn); + } } + c3cn->dst_cache = NULL; c3cn->cdev = NULL; } @@ -1417,17 +1422,18 @@ static void c3cn_active_close(struct s3_conn *c3cn) } /** - * cxgb3i_c3cn_release - close and release an iscsi tcp connection + * cxgb3i_c3cn_release - close and release an iscsi tcp connection and any + * resource held * @c3cn: the iscsi tcp connection */ void cxgb3i_c3cn_release(struct s3_conn *c3cn) { c3cn_conn_debug("c3cn 0x%p, s %u, f 0x%lx.\n", c3cn, c3cn->state, c3cn->flags); - if (likely(c3cn->state != C3CN_STATE_CONNECTING)) - c3cn_active_close(c3cn); - else + if (unlikely(c3cn->state == C3CN_STATE_CONNECTING)) c3cn_set_flag(c3cn, C3CN_ACTIVE_CLOSE_NEEDED); + else if (likely(c3cn->state != C3CN_STATE_CLOSED)) + c3cn_active_close(c3cn); c3cn_put(c3cn); } @@ -1656,7 +1662,6 @@ int cxgb3i_c3cn_connect(struct s3_conn *c3cn, struct sockaddr_in *usin) c3cn_set_state(c3cn, C3CN_STATE_CLOSED); ip_rt_put(rt); c3cn_put_port(c3cn); - c3cn->daddr.sin_port = 0; return err; } @@ -1776,10 +1781,25 @@ out_err: static void sdev_data_cleanup(struct cxgb3i_sdev_data *cdata) { struct adap_ports *ports = &cdata->ports; + struct s3_conn *c3cn; int i; + for (i = 0; i < cxgb3_max_connect; i++) { + if (cdata->sport_conn[i]) { + c3cn = cdata->sport_conn[i]; + cdata->sport_conn[i] = NULL; + + spin_lock_bh(&c3cn->lock); + c3cn->cdev = NULL; + c3cn_set_flag(c3cn, C3CN_OFFLOAD_DOWN); + c3cn_closed(c3cn); + spin_unlock_bh(&c3cn->lock); + } + } + for (i = 0; i < ports->nports; i++) NDEV2CDATA(ports->lldevs[i]) = NULL; + cxgb3i_free_big_mem(cdata); } @@ -1821,21 +1841,27 @@ void cxgb3i_sdev_add(struct t3cdev *cdev, struct cxgb3_client *client) struct cxgb3i_sdev_data *cdata; struct ofld_page_info rx_page_info; unsigned int wr_len; - int mapsize = DIV_ROUND_UP(cxgb3_max_connect, - 8 * sizeof(unsigned long)); + int mapsize = cxgb3_max_connect * sizeof(struct s3_conn *); int i; cdata = cxgb3i_alloc_big_mem(sizeof(*cdata) + mapsize, GFP_KERNEL); - if (!cdata) + if (!cdata) { + cxgb3i_log_warn("t3dev 0x%p, offload up, OOM %d.\n", + cdev, mapsize); return; + } if (cdev->ctl(cdev, GET_WR_LEN, &wr_len) < 0 || cdev->ctl(cdev, GET_PORTS, &cdata->ports) < 0 || - cdev->ctl(cdev, GET_RX_PAGE_INFO, &rx_page_info) < 0) + cdev->ctl(cdev, GET_RX_PAGE_INFO, &rx_page_info) < 0) { + cxgb3i_log_warn("t3dev 0x%p, offload up, ioctl failed.\n", + cdev); goto free_cdata; + } s3_init_wr_tab(wr_len); + spin_lock_init(&cdata->lock); INIT_LIST_HEAD(&cdata->list); cdata->cdev = cdev; cdata->client = client; @@ -1847,6 +1873,7 @@ void cxgb3i_sdev_add(struct t3cdev *cdev, struct cxgb3_client *client) list_add_tail(&cdata->list, &cdata_list); write_unlock(&cdata_rwlock); + cxgb3i_log_info("t3dev 0x%p, offload up, added.\n", cdev); return; free_cdata: @@ -1861,6 +1888,8 @@ void cxgb3i_sdev_remove(struct t3cdev *cdev) { struct cxgb3i_sdev_data *cdata = CXGB3_SDEV_DATA(cdev); + cxgb3i_log_info("t3dev 0x%p, offload down, remove.\n", cdev); + write_lock(&cdata_rwlock); list_del(&cdata->list); write_unlock(&cdata_rwlock); diff --git a/drivers/scsi/cxgb3i/cxgb3i_offload.h b/drivers/scsi/cxgb3i/cxgb3i_offload.h index 275f23f16eb..ebfca960c0a 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_offload.h +++ b/drivers/scsi/cxgb3i/cxgb3i_offload.h @@ -16,7 +16,7 @@ #define _CXGB3I_OFFLOAD_H #include <linux/skbuff.h> -#include <net/tcp.h> +#include <linux/in.h> #include "common.h" #include "adapter.h" @@ -135,11 +135,11 @@ enum c3cn_flags { C3CN_ABORT_RPL_PENDING, /* expecting an abort reply */ C3CN_TX_DATA_SENT, /* already sent a TX_DATA WR */ C3CN_ACTIVE_CLOSE_NEEDED, /* need to be closed */ + C3CN_OFFLOAD_DOWN /* offload function off */ }; /** * cxgb3i_sdev_data - Per adapter data. - * * Linked off of each Ethernet device port on the adapter. * Also available via the t3cdev structure since we have pointers to our port * net_device's there ... @@ -148,16 +148,17 @@ enum c3cn_flags { * @cdev: t3cdev adapter * @client: CPL client pointer * @ports: array of adapter ports - * @sport_map_next: next index into the port map - * @sport_map: source port map + * @sport_next: next port + * @sport_conn: source port connection */ struct cxgb3i_sdev_data { struct list_head list; struct t3cdev *cdev; struct cxgb3_client *client; struct adap_ports ports; - unsigned int sport_map_next; - unsigned long sport_map[0]; + spinlock_t lock; + unsigned int sport_next; + struct s3_conn *sport_conn[0]; }; #define NDEV2CDATA(ndev) (*(struct cxgb3i_sdev_data **)&(ndev)->ec_ptr) #define CXGB3_SDEV_DATA(cdev) NDEV2CDATA((cdev)->lldev) |