[PATCH 19/20] scsi_dh_alua: update 'access_state' field
Track attached SCSI devices and update the 'access_state' field whenever an ALUA state change has been detected. Signed-off-by: Hannes Reinecke--- drivers/scsi/device_handler/scsi_dh_alua.c | 29 + 1 file changed, 29 insertions(+) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 6c6ff0b..d01c86407 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,7 @@ static struct workqueue_struct *kaluad_wq; struct alua_port_group { struct kref kref; struct list_headnode; + struct list_headdh_list; unsigned char device_id_str[256]; int device_id_len; int group_id; @@ -93,6 +95,7 @@ struct alua_port_group { }; struct alua_dh_data { + struct list_headnode; struct alua_port_group *pg; int rel_port; spinlock_t pg_lock; @@ -243,6 +246,7 @@ struct alua_port_group *alua_get_pg(struct scsi_device *sdev, INIT_DELAYED_WORK(>rtpg_work, alua_rtpg_work); INIT_LIST_HEAD(>rtpg_list); INIT_LIST_HEAD(>node); + INIT_LIST_HEAD(>dh_list); spin_lock_init(>lock); /* Re-check list again to catch concurrent updates */ @@ -370,13 +374,26 @@ static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h, old_pg = pg; /* port_group has changed. Update to new port group */ if (h->pg != pg) { + unsigned long flags; + old_pg = h->pg; rcu_assign_pointer(h->pg, pg); + spin_lock_irqsave(_pg->lock, flags); + list_del_rcu(>node); + spin_unlock_irqrestore(_pg->lock, flags); + spin_lock_irqsave(>lock, flags); + list_add_rcu(>node, >dh_list); + spin_unlock_irqrestore(>lock, flags); h->pg->expiry = 0; pg_found = true; } } else { + unsigned long flags; + rcu_assign_pointer(h->pg, pg); + spin_lock_irqsave(>lock, flags); + list_add_rcu(>node, >dh_list); + spin_unlock_irqrestore(>lock, flags); pg_found = true; } alua_rtpg_queue(h->pg, sdev, NULL, true); @@ -630,6 +647,8 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) spin_lock_irqsave(_group_lock, flags); list_for_each_entry(tmp_pg, _group_list, node) { + struct alua_dh_data *h; + if (tmp_pg->group_id != group_id) continue; if (tmp_pg->device_id_len != pg->device_id_len) @@ -639,6 +658,12 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) continue; tmp_pg->state = desc[0] & 0x0f; tmp_pg->pref = desc[0] >> 7; + rcu_read_lock(); + list_for_each_entry_rcu(h, _pg->dh_list, node) { + if (h->sdev) + h->sdev->access_state = desc[0]; + } + rcu_read_unlock(); if (tmp_pg == pg) valid_states = desc[1]; } @@ -1056,6 +1081,7 @@ static int alua_bus_attach(struct scsi_device *sdev) h->rel_port = -1; h->init_error = SCSI_DH_OK; h->sdev = sdev; + INIT_LIST_HEAD(>node); mutex_init(>init_mutex); err = alua_initialize(sdev, h); @@ -1086,6 +1112,9 @@ static void alua_bus_detach(struct scsi_device *sdev) h->sdev = NULL; spin_unlock(>pg_lock); if (pg) { + spin_lock(>lock); + list_del_rcu(>node); + spin_unlock(>lock); synchronize_rcu(); if (pg->rtpg_sdev) flush_delayed_work(>rtpg_work); -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 12/20] scsi_dh_alua: Allow workqueue to run synchronously
Some arrays may only capable of handling one STPG at a time, so this patch implements a module option 'sync_stpg' to have STPGs submitted synchronously. Signed-off-by: Hannes Reinecke--- drivers/scsi/device_handler/scsi_dh_alua.c | 10 -- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 6fcdcd5..525449f 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -73,6 +73,10 @@ static uint optimize_stpg; module_param(optimize_stpg, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(optimize_stpg, "Allow use of a non-optimized path, rather than sending a STPG, when implicit TPGS is supported (0=No,1=Yes). Default is 0."); +static uint sync_stpg; +module_param(sync_stpg, uint, S_IRUGO); +MODULE_PARM_DESC(sync_stpg, "Issue STPG synchronously (0=No,1=Yes). Default is 0."); + static LIST_HEAD(port_group_list); static DEFINE_SPINLOCK(port_group_lock); static struct workqueue_struct *kaluad_wq; @@ -1019,9 +1023,11 @@ static struct scsi_device_handler alua_dh = { static int __init alua_init(void) { - int r; + int r, max_active = 0; - kaluad_wq = alloc_workqueue("kaluad", WQ_MEM_RECLAIM, 0); + if (sync_stpg) + max_active = 1; + kaluad_wq = alloc_workqueue("kaluad", WQ_MEM_RECLAIM, max_active); if (!kaluad_wq) { /* Temporary failure, bypass */ return SCSI_DH_DEV_TEMP_BUSY; -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 11/20] scsi_dh_alua: Use workqueue for RTPG
The current ALUA device_handler has two drawbacks: - We're sending a 'SET TARGET PORT GROUP' command to every LUN, disregarding the fact that several LUNs might be in a port group and will be automatically switched whenever _any_ LUN within that port group receives the command. - Whenever a LUN is in 'transitioning' mode we cannot block I/O to that LUN, instead the controller has to abort the command. This leads to increased traffic across the wire and heavy load on the controller during switchover. With this patch the RTPG handling is moved to a per-portgroup workqueue. This reduces the number of 'REPORT TARGET PORT GROUP' and 'SET TARGET PORT GROUPS' sent to the controller as we're sending them now per port group, and not per device as previously. It also allows us to block I/O to any LUN / port group found to be in 'transitioning' ALUA mode, as the workqueue item will be requeued until the controller moves out of transitioning. Signed-off-by: Hannes Reinecke--- drivers/scsi/device_handler/scsi_dh_alua.c | 331 +++-- 1 file changed, 270 insertions(+), 61 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 8d80ac6..6fcdcd5 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -59,13 +59,23 @@ #define ALUA_RTPG_SIZE 128 #define ALUA_FAILOVER_TIMEOUT 60 #define ALUA_FAILOVER_RETRIES 5 +#define ALUA_RTPG_DELAY_MSECS 5 /* device handler flags */ -#define ALUA_OPTIMIZE_STPG 1 -#define ALUA_RTPG_EXT_HDR_UNSUPP 2 +#define ALUA_OPTIMIZE_STPG 0x01 +#define ALUA_RTPG_EXT_HDR_UNSUPP 0x02 +/* State machine flags */ +#define ALUA_PG_RUN_RTPG 0x10 +#define ALUA_PG_RUN_STPG 0x20 +#define ALUA_PG_RUNNING0x40 + +static uint optimize_stpg; +module_param(optimize_stpg, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(optimize_stpg, "Allow use of a non-optimized path, rather than sending a STPG, when implicit TPGS is supported (0=No,1=Yes). Default is 0."); static LIST_HEAD(port_group_list); static DEFINE_SPINLOCK(port_group_lock); +static struct workqueue_struct *kaluad_wq; struct alua_port_group { struct kref kref; @@ -78,12 +88,25 @@ struct alua_port_group { int pref; unsignedflags; /* used for optimizing STPG */ unsigned char transition_tmo; + unsigned long expiry; + unsigned long interval; + struct delayed_work rtpg_work; + spinlock_t lock; + struct list_headrtpg_list; + struct scsi_device *rtpg_sdev; }; struct alua_dh_data { struct alua_port_group *pg; int rel_port; + spinlock_t pg_lock; struct scsi_device *sdev; + int init_error; + struct mutexinit_mutex; +}; + +struct alua_queue_data { + struct list_headentry; activate_complete callback_fn; void*callback_data; }; @@ -91,8 +114,10 @@ struct alua_dh_data { #define ALUA_POLICY_SWITCH_CURRENT 0 #define ALUA_POLICY_SWITCH_ALL 1 -static int alua_rtpg(struct scsi_device *, struct alua_port_group *); -static char print_alua_state(int); +static void alua_rtpg_work(struct work_struct *work); +static void alua_rtpg_queue(struct alua_port_group *pg, + struct scsi_device *sdev, + struct alua_queue_data *qdata); static void release_port_group(struct kref *kref) { @@ -102,6 +127,7 @@ static void release_port_group(struct kref *kref) spin_lock(_group_lock); list_del(>node); spin_unlock(_group_lock); + WARN_ON(pg->rtpg_sdev); kfree(pg); } @@ -167,7 +193,7 @@ static int submit_stpg(struct scsi_device *sdev, int group_id, struct alua_port_group *alua_lookup_pg(char *id_str, size_t id_size, int group_id) { - struct alua_port_group *pg = NULL; + struct alua_port_group *pg; list_for_each_entry(pg, _group_list, node) { if (pg->group_id != group_id) @@ -214,18 +240,26 @@ struct alua_port_group *alua_get_pg(struct scsi_device *sdev, pg->group_id = group_id; pg->tpgs = tpgs; pg->state = TPGS_STATE_OPTIMIZED; + if (optimize_stpg) + pg->flags |= ALUA_OPTIMIZE_STPG; kref_init(>kref); + INIT_DELAYED_WORK(>rtpg_work, alua_rtpg_work); + INIT_LIST_HEAD(>rtpg_list); + INIT_LIST_HEAD(>node); + spin_lock_init(>lock); /* Re-check list again to catch concurrent updates */ spin_lock(_group_lock); tmp_pg = alua_lookup_pg(id_str, id_size, group_id); if
[PATCH v2 78/71] ncr5380: Add support for HP 53C400A-based cards (C2502)
HP C2502 cards (based on 53C400A chips) use different magic numbers for software-based I/O address configuration than other cards. The configuration is also extended to allow setting the IRQ. Move the configuration to a new function magic_configure() and move magic the magic numbers into an array. Add new magic numbers for these HP cards and hp_53c400a module parameter to use them. Tested with HP C2502 and DTCT-436P. Signed-off-by: Ondrej Zary--- drivers/scsi/g_NCR5380.c | 76 -- drivers/scsi/g_NCR5380.h |1 + 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c index 42fdeaf..121d143 100644 --- a/drivers/scsi/g_NCR5380.c +++ b/drivers/scsi/g_NCR5380.c @@ -80,6 +80,7 @@ static int ncr_5380; static int ncr_53c400; static int ncr_53c400a; static int dtc_3181e; +static int hp_53c400a; static struct override { NCR5380_map_type NCR5380_map_name; @@ -225,6 +226,32 @@ static int __init do_DTC3181E_setup(char *str) #endif +#ifndef SCSI_G_NCR5380_MEM +/* + * Configure I/O address of 53C400A or DTC 3181 by writing magic numbers + * to ports 0x779 and 0x379. Two magic number sequences are known: + * 1. for generic NCR 53C400A-based cards and DTC436 chips + * 2. for HP C2502 card (also based on 53C400A but different decode logic) + */ +static void magic_configure(int idx, u8 irq, u8 magic[]) +{ + u8 cfg = 0; + + outb(magic[0], 0x779); + outb(magic[1], 0x379); + outb(magic[2], 0x379); + outb(magic[3], 0x379); + outb(magic[4], 0x379); + + /* allowed IRQs for HP 53C400A */ + if (irq != 2 && irq != 3 && irq != 4 && irq != 5 && irq != 7) + irq = 0; + if (idx >= 0 && idx <= 7) + cfg = 0x80 | idx | (irq << 4); + outb(cfg, 0x379); +} +#endif + /** * generic_NCR5380_detect - look for NCR5380 controllers * @tpnt: the scsi template @@ -241,8 +268,9 @@ static int __init generic_NCR5380_detect(struct scsi_host_template *tpnt) static int current_override; int count; unsigned int *ports; + u8 *magic; #ifndef SCSI_G_NCR5380_MEM - int i; + int i, port_idx; unsigned long region_size = 16; #endif static unsigned int __initdata ncr_53c400a_ports[] = { @@ -251,6 +279,12 @@ static int __init generic_NCR5380_detect(struct scsi_host_template *tpnt) static unsigned int __initdata dtc_3181e_ports[] = { 0x220, 0x240, 0x280, 0x2a0, 0x2c0, 0x300, 0x320, 0x340, 0 }; + static u8 ncr_53c400a_magic[] __initdata = {/* 53C400A & DTC 3181 */ + 0x59, 0xb9, 0xc5, 0xae, 0xa6 + }; + static u8 hp_53c400a_magic[] __initdata = { /* HP C2502 */ + 0x0f, 0x22, 0xf0, 0x20, 0x80 + }; int flags; struct Scsi_Host *instance; struct NCR5380_hostdata *hostdata; @@ -273,6 +307,8 @@ static int __init generic_NCR5380_detect(struct scsi_host_template *tpnt) overrides[0].board = BOARD_NCR53C400A; else if (dtc_3181e) overrides[0].board = BOARD_DTC3181E; + else if (hp_53c400a) + overrides[0].board = BOARD_HP53C400A; #ifndef SCSI_G_NCR5380_MEM if (!current_override && isapnp_present()) { struct pnp_dev *dev = NULL; @@ -326,10 +362,17 @@ static int __init generic_NCR5380_detect(struct scsi_host_template *tpnt) case BOARD_NCR53C400A: flags = FLAG_NO_DMA_FIXUP; ports = ncr_53c400a_ports; + magic = ncr_53c400a_magic; + break; + case BOARD_HP53C400A: + flags = FLAG_NO_DMA_FIXUP; + ports = ncr_53c400a_ports; + magic = hp_53c400a_magic; break; case BOARD_DTC3181E: flags = FLAG_NO_DMA_FIXUP; ports = dtc_3181e_ports; + magic = ncr_53c400a_magic; break; } @@ -338,12 +381,7 @@ static int __init generic_NCR5380_detect(struct scsi_host_template *tpnt) /* wakeup sequence for the NCR53C400A and DTC3181E */ /* Disable the adapter and look for a free io port */ - outb(0x59, 0x779); - outb(0xb9, 0x379); - outb(0xc5, 0x379); - outb(0xae, 0x379); - outb(0xa6, 0x379); - outb(0x00, 0x379); + magic_configure(-1, 0, magic); if (overrides[current_override].NCR5380_map_name != PORT_AUTO) for (i = 0; ports[i]; i++) { @@ -362,17 +400,14 @@ static int __init
[PATCH 07/20] scsi_dh_alua: allocate RTPG buffer separately
The RTPG buffer will only evaluated within alua_rtpg(), so we can allocate it locally there and avoid having to put it into the global structure. Reviewed-by: Ewan MilneReviewed-by: Christoph Hellwig Signed-off-by: Hannes Reinecke --- drivers/scsi/device_handler/scsi_dh_alua.c | 56 +++--- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index f3191f7..ca6322d 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -56,7 +56,7 @@ #define TPGS_MODE_IMPLICIT 0x1 #define TPGS_MODE_EXPLICIT 0x2 -#define ALUA_INQUIRY_SIZE 36 +#define ALUA_RTPG_SIZE 128 #define ALUA_FAILOVER_TIMEOUT 60 #define ALUA_FAILOVER_RETRIES 5 @@ -75,9 +75,6 @@ struct alua_port_group { int state; int pref; unsignedflags; /* used for optimizing STPG */ - unsigned char inq[ALUA_INQUIRY_SIZE]; - unsigned char *buff; - int bufflen; unsigned char transition_tmo; }; @@ -95,21 +92,6 @@ struct alua_dh_data { static char print_alua_state(int); -static int realloc_buffer(struct alua_port_group *pg, unsigned len) -{ - if (pg->buff && pg->buff != pg->inq) - kfree(pg->buff); - - pg->buff = kmalloc(len, GFP_NOIO); - if (!pg->buff) { - pg->buff = pg->inq; - pg->bufflen = ALUA_INQUIRY_SIZE; - return 1; - } - pg->bufflen = len; - return 0; -} - static void release_port_group(struct kref *kref) { struct alua_port_group *pg; @@ -118,8 +100,6 @@ static void release_port_group(struct kref *kref) spin_lock(_group_lock); list_del(>node); spin_unlock(_group_lock); - if (pg->buff && pg->inq != pg->buff) - kfree(pg->buff); kfree(pg); } @@ -201,8 +181,6 @@ struct alua_port_group *alua_get_pg(struct scsi_device *sdev, return NULL; pg->group_id = group_id; - pg->buff = pg->inq; - pg->bufflen = ALUA_INQUIRY_SIZE; pg->tpgs = tpgs; pg->state = TPGS_STATE_OPTIMIZED; kref_init(>kref); @@ -387,8 +365,8 @@ static int alua_check_sense(struct scsi_device *sdev, static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg, int wait_for_transition) { struct scsi_sense_hdr sense_hdr; - int len, k, off, valid_states = 0; - unsigned char *ucp; + int len, k, off, valid_states = 0, bufflen = ALUA_RTPG_SIZE; + unsigned char *ucp, *buff; unsigned err, retval; unsigned long expiry, interval = 0; unsigned int tpg_desc_tbl_off; @@ -399,15 +377,19 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg, int w else expiry = round_jiffies_up(jiffies + pg->transition_tmo * HZ); + buff = kzalloc(bufflen, GFP_KERNEL); + if (!buff) + return SCSI_DH_DEV_TEMP_BUSY; + retry: - retval = submit_rtpg(sdev, pg->buff, pg->bufflen, -_hdr, pg->flags); + retval = submit_rtpg(sdev, buff, bufflen, _hdr, pg->flags); if (retval) { if (!scsi_sense_valid(_hdr)) { sdev_printk(KERN_INFO, sdev, "%s: rtpg failed, result %d\n", ALUA_DH_NAME, retval); + kfree(buff); if (driver_byte(retval) == DRIVER_ERROR) return SCSI_DH_DEV_TEMP_BUSY; return SCSI_DH_IO; @@ -445,14 +427,18 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg, int w sdev_printk(KERN_ERR, sdev, "%s: rtpg failed\n", ALUA_DH_NAME); scsi_print_sense_hdr(sdev, ALUA_DH_NAME, _hdr); + kfree(buff); return SCSI_DH_IO; } - len = get_unaligned_be32(>buff[0]) + 4; + len = get_unaligned_be32([0]) + 4; - if (len > pg->bufflen) { + if (len > bufflen) { /* Resubmit with the correct length */ - if (realloc_buffer(pg, len)) { + kfree(buff); + bufflen = len; + buff = kmalloc(bufflen, GFP_KERNEL); + if (!buff) { sdev_printk(KERN_WARNING, sdev, "%s: kmalloc buffer failed\n",__func__); /* Temporary failure, bypass */ @@ -462,9 +448,8 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg, int w } orig_transition_tmo =
[PATCH 13/20] scsi_dh_alua: Recheck state on unit attention
When we receive a unit attention code of 'ALUA state changed' we should recheck the state, as it might be due to an implicit ALUA state transition. This allows us to return NEEDS_RETRY instead of ADD_TO_MLQUEUE, allowing to terminate the retries after a certain time. At the same time a workqueue item might already be queued, which should be started immediately to avoid any delays. Signed-off-by: Hannes Reinecke--- drivers/scsi/device_handler/scsi_dh_alua.c | 58 -- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 525449f..04a3a543 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -121,7 +121,8 @@ struct alua_queue_data { static void alua_rtpg_work(struct work_struct *work); static void alua_rtpg_queue(struct alua_port_group *pg, struct scsi_device *sdev, - struct alua_queue_data *qdata); + struct alua_queue_data *qdata, bool force); +static void alua_check(struct scsi_device *sdev, bool force); static void release_port_group(struct kref *kref) { @@ -386,7 +387,7 @@ static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h, rcu_assign_pointer(h->pg, pg); pg_found = true; } - alua_rtpg_queue(h->pg, sdev, NULL); + alua_rtpg_queue(h->pg, sdev, NULL, true); spin_unlock(>pg_lock); if (pg_found) @@ -427,18 +428,24 @@ static int alua_check_sense(struct scsi_device *sdev, { switch (sense_hdr->sense_key) { case NOT_READY: - if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) + if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) { /* * LUN Not Accessible - ALUA state transition */ - return ADD_TO_MLQUEUE; + alua_check(sdev, false); + return NEEDS_RETRY; + } break; case UNIT_ATTENTION: - if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00) + if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00) { /* -* Power On, Reset, or Bus Device Reset, just retry. +* Power On, Reset, or Bus Device Reset. +* Might have obscured a state transition, +* so schedule a recheck. */ + alua_check(sdev, true); return ADD_TO_MLQUEUE; + } if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x04) /* * Device internal reset @@ -449,16 +456,20 @@ static int alua_check_sense(struct scsi_device *sdev, * Mode Parameters Changed */ return ADD_TO_MLQUEUE; - if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) + if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) { /* * ALUA state changed */ + alua_check(sdev, true); return ADD_TO_MLQUEUE; - if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07) + } + if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07) { /* * Implicit ALUA state transition failed */ + alua_check(sdev, true); return ADD_TO_MLQUEUE; + } if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x03) /* * Inquiry data has changed @@ -777,7 +788,7 @@ static void alua_rtpg_work(struct work_struct *work) static void alua_rtpg_queue(struct alua_port_group *pg, struct scsi_device *sdev, - struct alua_queue_data *qdata) + struct alua_queue_data *qdata, bool force) { int start_queue = 0; unsigned long flags; @@ -797,7 +808,9 @@ static void alua_rtpg_queue(struct alua_port_group *pg, pg->rtpg_sdev = sdev; scsi_device_get(sdev); start_queue = 1; - } + } else if (!(pg->flags & ALUA_PG_RUN_RTPG) && force) + start_queue = 1; + spin_unlock_irqrestore(>lock, flags); if (start_queue && @@ -912,7 +925,7 @@ static int alua_activate(struct scsi_device *sdev, kref_get(>kref); rcu_read_unlock(); - alua_rtpg_queue(pg, sdev, qdata); + alua_rtpg_queue(pg, sdev, qdata, true);
[PATCH 09/20] scsi_dh_alua: simplify alua_initialize()
Rework alua_check_vpd() to use scsi_vpd_get_tpg() and move the port group selection into the function, too. With that we can simplify alua_initialize() to just call alua_check_tpgs() and alua_check_vpd(); Signed-off-by: Hannes Reinecke--- drivers/scsi/device_handler/scsi_dh_alua.c | 70 +- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 688e0f7..1eacf3f 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -82,7 +82,6 @@ struct alua_port_group { struct alua_dh_data { struct alua_port_group *pg; - int group_id; int rel_port; struct scsi_device *sdev; activate_complete callback_fn; @@ -92,6 +91,7 @@ struct alua_dh_data { #define ALUA_POLICY_SWITCH_CURRENT 0 #define ALUA_POLICY_SWITCH_ALL 1 +static int alua_rtpg(struct scsi_device *, struct alua_port_group *, int); static char print_alua_state(int); static void release_port_group(struct kref *kref) @@ -288,10 +288,15 @@ static int alua_check_tpgs(struct scsi_device *sdev) * * Extract the relative target port and the target port group * descriptor from the list of identificators. + * + * Returns 0 or SCSI_DH_ error code on failure. */ -static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h) +static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h, + int tpgs) { int rel_port = -1, group_id; + char id_str[256]; + int id_size; group_id = scsi_vpd_tpg_id(sdev, _port); if (group_id < 0) { @@ -305,14 +310,28 @@ static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h) ALUA_DH_NAME); return SCSI_DH_DEV_UNSUPP; } - h->state = TPGS_STATE_OPTIMIZED; - h->group_id = group_id; + h->rel_port = rel_port; + id_size = scsi_vpd_lun_id(sdev, id_str, 256); + if (id_size <= 0) { + /* +* Internal error: TPGS supported but no device +* identifcation found. Disable ALUA support. +*/ + sdev_printk(KERN_INFO, sdev, + "%s: No device descriptors found\n", + ALUA_DH_NAME); + return SCSI_DH_DEV_UNSUPP; + } sdev_printk(KERN_INFO, sdev, - "%s: port group %02x rel port %02x\n", - ALUA_DH_NAME, h->group_id, h->rel_port); + "%s: device %s port group %02x rel port %02x\n", + ALUA_DH_NAME, id_str, group_id, h->rel_port); - return 0; + h->pg = alua_get_pg(sdev, group_id, tpgs, id_str, id_size); + if (!h->pg) + return SCSI_DH_NOMEM; + + return alua_rtpg(sdev, h->pg, 0); } static char print_alua_state(int state) @@ -631,45 +650,14 @@ static unsigned alua_stpg(struct scsi_device *sdev, struct alua_port_group *pg) static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h) { int err = SCSI_DH_DEV_UNSUPP, tpgs; - char device_id_str[256]; - int device_id_len; tpgs = alua_check_tpgs(sdev); - if (tpgs == TPGS_MODE_NONE) - goto out; - - err = alua_check_vpd(sdev, h); - if (err != SCSI_DH_OK) - goto out; + if (tpgs != TPGS_MODE_NONE) + err = alua_check_vpd(sdev, h, tpgs); - device_id_len = scsi_vpd_lun_id(sdev, device_id_str, - sizeof(device_id_str)); - if (device_id_len <= 0) { - /* -* Internal error: TPGS supported but no device -* identifcation found. Disable ALUA support. -*/ - sdev_printk(KERN_INFO, sdev, - "%s: No device descriptors found\n", - ALUA_DH_NAME); - goto out; - } - sdev_printk(KERN_INFO, sdev, - "%s: device %s port group %02x rel port %02x\n", - ALUA_DH_NAME, device_id_str, h->group_id, h->rel_port); - - h->pg = alua_get_pg(sdev, h->group_id, tpgs, - device_id_str, device_id_len); - if (!h->pg) { - err = SCSI_DH_NOMEM; - goto out; - } - kref_get(>pg->kref); - err = alua_rtpg(sdev, h->pg, 0); - kref_put(>pg->kref, release_port_group); -out: return err; } + /* * alua_set_params - set/unset the optimize flag * @sdev: device on the path to be activated -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at
[PATCH 10/20] revert commit a8e5a2d593cb ("[SCSI] scsi_dh_alua: ALUA handler attach should succeed while TPG is transitioning")
Obsoleted by the next patch. Reviewed-by: Ewan MilneSigned-off-by: Hannes Reinecke --- drivers/scsi/device_handler/scsi_dh_alua.c | 31 -- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 1eacf3f..8d80ac6 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -91,7 +91,7 @@ struct alua_dh_data { #define ALUA_POLICY_SWITCH_CURRENT 0 #define ALUA_POLICY_SWITCH_ALL 1 -static int alua_rtpg(struct scsi_device *, struct alua_port_group *, int); +static int alua_rtpg(struct scsi_device *, struct alua_port_group *); static char print_alua_state(int); static void release_port_group(struct kref *kref) @@ -331,7 +331,7 @@ static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h, if (!h->pg) return SCSI_DH_NOMEM; - return alua_rtpg(sdev, h->pg, 0); + return alua_rtpg(sdev, h->pg); } static char print_alua_state(int state) @@ -414,13 +414,12 @@ static int alua_check_sense(struct scsi_device *sdev, /* * alua_rtpg - Evaluate REPORT TARGET GROUP STATES * @sdev: the device to be evaluated. - * @wait_for_transition: if nonzero, wait ALUA_FAILOVER_TIMEOUT seconds for device to exit transitioning state * * Evaluate the Target Port Group State. * Returns SCSI_DH_DEV_OFFLINED if the path is * found to be unusable. */ -static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg, int wait_for_transition) +static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) { struct scsi_sense_hdr sense_hdr; int len, k, off, valid_states = 0, bufflen = ALUA_RTPG_SIZE; @@ -511,8 +510,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg, int w else pg->transition_tmo = ALUA_FAILOVER_TIMEOUT; - if (wait_for_transition && - (orig_transition_tmo != pg->transition_tmo)) { + if (orig_transition_tmo != pg->transition_tmo) { sdev_printk(KERN_INFO, sdev, "%s: transition timeout set to %d seconds\n", ALUA_DH_NAME, pg->transition_tmo); @@ -550,19 +548,14 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg, int w switch (pg->state) { case TPGS_STATE_TRANSITIONING: - if (wait_for_transition) { - if (time_before(jiffies, expiry)) { - /* State transition, retry */ - interval += 2000; - msleep(interval); - goto retry; - } - err = SCSI_DH_RETRY; - } else { - err = SCSI_DH_OK; + if (time_before(jiffies, expiry)) { + /* State transition, retry */ + interval += 2000; + msleep(interval); + goto retry; } - /* Transitioning time exceeded, set port to standby */ + err = SCSI_DH_RETRY; pg->state = TPGS_STATE_STANDBY; break; case TPGS_STATE_OFFLINE: @@ -723,14 +716,14 @@ static int alua_activate(struct scsi_device *sdev, if (optimize_stpg) h->pg->flags |= ALUA_OPTIMIZE_STPG; - err = alua_rtpg(sdev, h->pg, 1); + err = alua_rtpg(sdev, h->pg); if (err != SCSI_DH_OK) { kref_put(>pg->kref, release_port_group); goto out; } err = alua_stpg(sdev, h->pg); if (err == SCSI_DH_RETRY) - err = alua_rtpg(sdev, h->pg, 1); + err = alua_rtpg(sdev, h->pg); kref_put(>pg->kref, release_port_group); out: if (fn) -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 04/20] scsi_dh_alua: call alua_rtpg() if stpg fails
If the call to SET TARGET PORT GROUPS fails we have no idea what state the array is left in, so we need to issue a call to REPORT TARGET PORT GROUPS in these cases. Signed-off-by: Hannes Reinecke--- drivers/scsi/device_handler/scsi_dh_alua.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 609691f..ba3d23e 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -678,6 +678,8 @@ static int alua_activate(struct scsi_device *sdev, h->flags |= ALUA_OPTIMIZE_STPG; err = alua_stpg(sdev, h); + if (err == SCSI_DH_RETRY) + err = alua_rtpg(sdev, h, 1); out: if (fn) fn(data, err); -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 20/20] scsi_dh_alua: Update version to 2.0
Signed-off-by: Hannes Reinecke--- drivers/scsi/device_handler/scsi_dh_alua.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index d01c86407..1f97b65 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -30,7 +30,7 @@ #include #define ALUA_DH_NAME "alua" -#define ALUA_DH_VER "1.3" +#define ALUA_DH_VER "2.0" #define TPGS_SUPPORT_NONE 0x00 #define TPGS_SUPPORT_OPTIMIZED 0x01 -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 08/20] scsi_dh_alua: use unique device id
Use scsi_vpd_lun_id() to assign a unique device identification to the alua port group structure. Signed-off-by: Hannes Reinecke--- drivers/scsi/device_handler/scsi_dh_alua.c | 64 -- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index ca6322d..688e0f7 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -70,6 +70,8 @@ static DEFINE_SPINLOCK(port_group_lock); struct alua_port_group { struct kref kref; struct list_headnode; + unsigned char device_id_str[256]; + int device_id_len; int group_id; int tpgs; int state; @@ -162,6 +164,25 @@ static int submit_stpg(struct scsi_device *sdev, int group_id, ALUA_FAILOVER_RETRIES, NULL, req_flags); } +struct alua_port_group *alua_lookup_pg(char *id_str, size_t id_size, + int group_id) +{ + struct alua_port_group *pg = NULL; + + list_for_each_entry(pg, _group_list, node) { + if (pg->group_id != group_id) + continue; + if (pg->device_id_len != id_size) + continue; + if (strncmp(pg->device_id_str, id_str, id_size)) + continue; + kref_get(>kref); + return pg; + } + + return NULL; +} + /* * alua_get_pg - Allocate a new port_group structure * @sdev: scsi device @@ -172,19 +193,37 @@ static int submit_stpg(struct scsi_device *sdev, int group_id, * device. */ struct alua_port_group *alua_get_pg(struct scsi_device *sdev, - int group_id, int tpgs) + int group_id, int tpgs, + char *id_str, size_t id_size) { - struct alua_port_group *pg = NULL; + struct alua_port_group *pg = NULL, *tmp_pg; + + spin_lock(_group_lock); + pg = alua_lookup_pg(id_str, id_size, group_id); + spin_unlock(_group_lock); + if (pg) + return pg; pg = kzalloc(sizeof(struct alua_port_group), GFP_KERNEL); if (!pg) return NULL; + strncpy(pg->device_id_str, id_str, sizeof(pg->device_id_str)); + + pg->device_id_len = id_size; pg->group_id = group_id; pg->tpgs = tpgs; pg->state = TPGS_STATE_OPTIMIZED; kref_init(>kref); + + /* Re-check list again to catch concurrent updates */ spin_lock(_group_lock); + tmp_pg = alua_lookup_pg(id_str, id_size, group_id); + if (tmp_pg) { + spin_unlock(_group_lock); + kfree(pg); + return tmp_pg; + } list_add(>node, _group_list); spin_unlock(_group_lock); @@ -592,6 +631,8 @@ static unsigned alua_stpg(struct scsi_device *sdev, struct alua_port_group *pg) static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h) { int err = SCSI_DH_DEV_UNSUPP, tpgs; + char device_id_str[256]; + int device_id_len; tpgs = alua_check_tpgs(sdev); if (tpgs == TPGS_MODE_NONE) @@ -601,7 +642,24 @@ static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h) if (err != SCSI_DH_OK) goto out; - h->pg = alua_get_pg(sdev, h->group_id, tpgs); + device_id_len = scsi_vpd_lun_id(sdev, device_id_str, + sizeof(device_id_str)); + if (device_id_len <= 0) { + /* +* Internal error: TPGS supported but no device +* identifcation found. Disable ALUA support. +*/ + sdev_printk(KERN_INFO, sdev, + "%s: No device descriptors found\n", + ALUA_DH_NAME); + goto out; + } + sdev_printk(KERN_INFO, sdev, + "%s: device %s port group %02x rel port %02x\n", + ALUA_DH_NAME, device_id_str, h->group_id, h->rel_port); + + h->pg = alua_get_pg(sdev, h->group_id, tpgs, + device_id_str, device_id_len); if (!h->pg) { err = SCSI_DH_NOMEM; goto out; -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 16/20] scsi_dh: add 'rescan' callback
If a device needs to be rescanned the device_handler might need to be rechecked, too. So add a 'rescan' callback to the device handler and call it upon scsi_rescan_device(). Signed-off-by: Hannes Reinecke--- drivers/scsi/device_handler/scsi_dh_alua.c | 8 drivers/scsi/scsi_lib.c| 1 + drivers/scsi/scsi_scan.c | 8 +++- include/scsi/scsi_dh.h | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 6014750..58c51ad 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -1040,6 +1040,13 @@ static int alua_prep_fn(struct scsi_device *sdev, struct request *req) } +static void alua_rescan(struct scsi_device *sdev) +{ + struct alua_dh_data *h = sdev->handler_data; + + alua_initialize(sdev, h); +} + /* * alua_bus_attach - Attach device handler * @sdev: device to be attached to @@ -1104,6 +,7 @@ static struct scsi_device_handler alua_dh = { .prep_fn = alua_prep_fn, .check_sense = alua_check_sense, .activate = alua_activate, + .rescan = alua_rescan, .set_params = alua_set_params, }; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index fa6b2c4..d46193a 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -2699,6 +2699,7 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt) envp[idx++] = "SDEV_MEDIA_CHANGE=1"; break; case SDEV_EVT_INQUIRY_CHANGE_REPORTED: + scsi_rescan_device(>sdev_gendev); envp[idx++] = "SDEV_UA=INQUIRY_DATA_HAS_CHANGED"; break; case SDEV_EVT_CAPACITY_CHANGE_REPORTED: diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index a1c195d..05dc1aa 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include "scsi_priv.h" @@ -1516,9 +1517,14 @@ EXPORT_SYMBOL(scsi_add_device); void scsi_rescan_device(struct device *dev) { + struct scsi_device *sdev = to_scsi_device(dev); + device_lock(dev); - scsi_attach_vpd(to_scsi_device(dev)); + scsi_attach_vpd(sdev); + + if (sdev->handler && sdev->handler->rescan) + sdev->handler->rescan(sdev); if (dev->driver && try_module_get(dev->driver->owner)) { struct scsi_driver *drv = to_scsi_driver(dev->driver); diff --git a/include/scsi/scsi_dh.h b/include/scsi/scsi_dh.h index 7e184c6..c7bba2b 100644 --- a/include/scsi/scsi_dh.h +++ b/include/scsi/scsi_dh.h @@ -71,6 +71,7 @@ struct scsi_device_handler { int (*activate)(struct scsi_device *, activate_complete, void *); int (*prep_fn)(struct scsi_device *, struct request *); int (*set_params)(struct scsi_device *, const char *); + void (*rescan)(struct scsi_device *); }; #ifdef CONFIG_SCSI_DH -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 03/20] scsi_dh_alua: Make stpg synchronous
The 'activate_complete' function needs to be executed after stpg has finished, so we can as well execute stpg synchronously and call the function directly. Signed-off-by: Hannes Reinecke--- drivers/scsi/device_handler/scsi_dh_alua.c | 156 ++--- 1 file changed, 55 insertions(+), 101 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 7c66e4a..609691f 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -169,81 +169,28 @@ done: } /* - * stpg_endio - Evaluate SET TARGET GROUP STATES - * @sdev: the device to be evaluated - * @state: the new target group state - * - * Evaluate a SET TARGET GROUP STATES command response. - */ -static void stpg_endio(struct request *req, int error) -{ - struct alua_dh_data *h = req->end_io_data; - struct scsi_sense_hdr sense_hdr; - unsigned err = SCSI_DH_OK; - - if (host_byte(req->errors) != DID_OK || - msg_byte(req->errors) != COMMAND_COMPLETE) { - err = SCSI_DH_IO; - goto done; - } - - if (scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, -_hdr)) { - if (sense_hdr.sense_key == NOT_READY && - sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a) { - /* ALUA state transition already in progress */ - err = SCSI_DH_OK; - goto done; - } - if (sense_hdr.sense_key == UNIT_ATTENTION) { - err = SCSI_DH_RETRY; - goto done; - } - sdev_printk(KERN_INFO, h->sdev, "%s: stpg failed\n", - ALUA_DH_NAME); - scsi_print_sense_hdr(h->sdev, ALUA_DH_NAME, _hdr); - err = SCSI_DH_IO; - } else if (error) - err = SCSI_DH_IO; - - if (err == SCSI_DH_OK) { - h->state = TPGS_STATE_OPTIMIZED; - sdev_printk(KERN_INFO, h->sdev, - "%s: port group %02x switched to state %c\n", - ALUA_DH_NAME, h->group_id, - print_alua_state(h->state)); - } -done: - req->end_io_data = NULL; - __blk_put_request(req->q, req); - if (h->callback_fn) { - h->callback_fn(h->callback_data, err); - h->callback_fn = h->callback_data = NULL; - } - return; -} - -/* * submit_stpg - Issue a SET TARGET GROUP STATES command * * Currently we're only setting the current target port group state * to 'active/optimized' and let the array firmware figure out * the states of the remaining groups. */ -static unsigned submit_stpg(struct alua_dh_data *h) +static unsigned submit_stpg(struct scsi_device *sdev, int group_id, + unsigned char *sense) { struct request *rq; + unsigned char stpg_data[8]; int stpg_len = 8; - struct scsi_device *sdev = h->sdev; + int err = 0; /* Prepare the data buffer */ - memset(h->buff, 0, stpg_len); - h->buff[4] = TPGS_STATE_OPTIMIZED & 0x0f; - put_unaligned_be16(h->group_id, >buff[6]); + memset(stpg_data, 0, stpg_len); + stpg_data[4] = TPGS_STATE_OPTIMIZED & 0x0f; + put_unaligned_be16(group_id, _data[6]); - rq = get_alua_req(sdev, h->buff, stpg_len, WRITE); + rq = get_alua_req(sdev, stpg_data, stpg_len, WRITE); if (!rq) - return SCSI_DH_RES_TEMP_UNAVAIL; + return DRIVER_BUSY << 24; /* Prepare the command. */ rq->cmd[0] = MAINTENANCE_OUT; @@ -251,13 +198,17 @@ static unsigned submit_stpg(struct alua_dh_data *h) put_unaligned_be32(stpg_len, >cmd[6]); rq->cmd_len = COMMAND_SIZE(MAINTENANCE_OUT); - rq->sense = h->sense; + rq->sense = sense; memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); rq->sense_len = 0; - rq->end_io_data = h; - blk_execute_rq_nowait(rq->q, NULL, rq, 1, stpg_endio); - return SCSI_DH_OK; + blk_execute_rq(rq->q, NULL, rq, 1); + if (rq->errors) + err = rq->errors; + + blk_put_request(rq); + + return err; } /* @@ -582,59 +533,63 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, int wait_ * alua_stpg - Issue a SET TARGET GROUP STATES command * * Issue a SET TARGET GROUP STATES command and evaluate the - * response. Returns SCSI_DH_RETRY if the target port group - * state is found to be in 'transitioning'. - * If SCSI_DH_OK is returned the passed-in 'fn' function - * this function will take care of executing 'fn'. - * Otherwise 'fn' should be executed by the caller with the - * returned error code. + * response. Returns SCSI_DH_RETRY per default to trigger + * a re-evaluation of the target
[PATCH 06/20] scsi_dh_alua: Use separate alua_port_group structure
The port group needs to be a separate structure as several LUNs might belong to the same group. Reviewed-by: Ewan MilneSigned-off-by: Hannes Reinecke --- drivers/scsi/device_handler/scsi_dh_alua.c | 221 - include/scsi/scsi_dh.h | 1 + 2 files changed, 151 insertions(+), 71 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 063c03a..f3191f7 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -64,9 +64,13 @@ #define ALUA_OPTIMIZE_STPG 1 #define ALUA_RTPG_EXT_HDR_UNSUPP 2 -struct alua_dh_data { +static LIST_HEAD(port_group_list); +static DEFINE_SPINLOCK(port_group_lock); + +struct alua_port_group { + struct kref kref; + struct list_headnode; int group_id; - int rel_port; int tpgs; int state; int pref; @@ -75,6 +79,12 @@ struct alua_dh_data { unsigned char *buff; int bufflen; unsigned char transition_tmo; +}; + +struct alua_dh_data { + struct alua_port_group *pg; + int group_id; + int rel_port; struct scsi_device *sdev; activate_complete callback_fn; void*callback_data; @@ -85,21 +95,34 @@ struct alua_dh_data { static char print_alua_state(int); -static int realloc_buffer(struct alua_dh_data *h, unsigned len) +static int realloc_buffer(struct alua_port_group *pg, unsigned len) { - if (h->buff && h->buff != h->inq) - kfree(h->buff); + if (pg->buff && pg->buff != pg->inq) + kfree(pg->buff); - h->buff = kmalloc(len, GFP_NOIO); - if (!h->buff) { - h->buff = h->inq; - h->bufflen = ALUA_INQUIRY_SIZE; + pg->buff = kmalloc(len, GFP_NOIO); + if (!pg->buff) { + pg->buff = pg->inq; + pg->bufflen = ALUA_INQUIRY_SIZE; return 1; } - h->bufflen = len; + pg->bufflen = len; return 0; } +static void release_port_group(struct kref *kref) +{ + struct alua_port_group *pg; + + pg = container_of(kref, struct alua_port_group, kref); + spin_lock(_group_lock); + list_del(>node); + spin_unlock(_group_lock); + if (pg->buff && pg->inq != pg->buff) + kfree(pg->buff); + kfree(pg); +} + /* * submit_rtpg - Issue a REPORT TARGET GROUP STATES command * @sdev: sdev the command should be sent to @@ -160,6 +183,37 @@ static int submit_stpg(struct scsi_device *sdev, int group_id, } /* + * alua_get_pg - Allocate a new port_group structure + * @sdev: scsi device + * @h: alua device_handler data + * @group_id: port group id + * + * Allocate a new port_group structure for a given + * device. + */ +struct alua_port_group *alua_get_pg(struct scsi_device *sdev, + int group_id, int tpgs) +{ + struct alua_port_group *pg = NULL; + + pg = kzalloc(sizeof(struct alua_port_group), GFP_KERNEL); + if (!pg) + return NULL; + + pg->group_id = group_id; + pg->buff = pg->inq; + pg->bufflen = ALUA_INQUIRY_SIZE; + pg->tpgs = tpgs; + pg->state = TPGS_STATE_OPTIMIZED; + kref_init(>kref); + spin_lock(_group_lock); + list_add(>node, _group_list); + spin_unlock(_group_lock); + + return pg; +} + +/* * alua_check_tpgs - Evaluate TPGS setting * @sdev: device to be checked * @@ -330,7 +384,7 @@ static int alua_check_sense(struct scsi_device *sdev, * Returns SCSI_DH_DEV_OFFLINED if the path is * found to be unusable. */ -static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, int wait_for_transition) +static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg, int wait_for_transition) { struct scsi_sense_hdr sense_hdr; int len, k, off, valid_states = 0; @@ -340,13 +394,14 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, int wait_ unsigned int tpg_desc_tbl_off; unsigned char orig_transition_tmo; - if (!h->transition_tmo) + if (!pg->transition_tmo) expiry = round_jiffies_up(jiffies + ALUA_FAILOVER_TIMEOUT * HZ); else - expiry = round_jiffies_up(jiffies + h->transition_tmo * HZ); + expiry = round_jiffies_up(jiffies + pg->transition_tmo * HZ); retry: - retval = submit_rtpg(sdev, h->buff, h->bufflen, _hdr, h->flags); + retval = submit_rtpg(sdev, pg->buff, pg->bufflen, +_hdr, pg->flags); if (retval) { if
[PATCH 05/20] scsi_dh_alua: switch to scsi_execute_req_flags()
All commands are issued synchronously, so no need to open-code scsi_execute_req_flags() anymore. And we can get rid of the static sense code structure element. scsi_execute_req_flags() will be setting REQ_QUIET and REQ_PREEMPT, but that is perfectly fine as we're evaluating and logging any errors ourselves and we really need to send the command even if the device is quiesced. Reviewed-by: Christoph HellwigReviewed-by: Ewan Milne Signed-off-by: Hannes Reinecke --- drivers/scsi/device_handler/scsi_dh_alua.c | 125 + 1 file changed, 36 insertions(+), 89 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index ba3d23e..063c03a 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -75,7 +75,6 @@ struct alua_dh_data { unsigned char *buff; int bufflen; unsigned char transition_tmo; - unsigned char sense[SCSI_SENSE_BUFFERSIZE]; struct scsi_device *sdev; activate_complete callback_fn; void*callback_data; @@ -101,71 +100,30 @@ static int realloc_buffer(struct alua_dh_data *h, unsigned len) return 0; } -static struct request *get_alua_req(struct scsi_device *sdev, - void *buffer, unsigned buflen, int rw) -{ - struct request *rq; - struct request_queue *q = sdev->request_queue; - - rq = blk_get_request(q, rw, GFP_NOIO); - - if (IS_ERR(rq)) { - sdev_printk(KERN_INFO, sdev, - "%s: blk_get_request failed\n", __func__); - return NULL; - } - blk_rq_set_block_pc(rq); - - if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) { - blk_put_request(rq); - sdev_printk(KERN_INFO, sdev, - "%s: blk_rq_map_kern failed\n", __func__); - return NULL; - } - - rq->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | -REQ_FAILFAST_DRIVER; - rq->retries = ALUA_FAILOVER_RETRIES; - rq->timeout = ALUA_FAILOVER_TIMEOUT * HZ; - - return rq; -} - /* * submit_rtpg - Issue a REPORT TARGET GROUP STATES command * @sdev: sdev the command should be sent to */ -static unsigned submit_rtpg(struct scsi_device *sdev, unsigned char *buff, - int bufflen, unsigned char *sense, int flags) +static int submit_rtpg(struct scsi_device *sdev, unsigned char *buff, + int bufflen, struct scsi_sense_hdr *sshdr, int flags) { - struct request *rq; - int err = 0; - - rq = get_alua_req(sdev, buff, bufflen, READ); - if (!rq) { - err = DRIVER_BUSY << 24; - goto done; - } + u8 cdb[COMMAND_SIZE(MAINTENANCE_IN)]; + int req_flags = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | + REQ_FAILFAST_DRIVER; /* Prepare the command. */ - rq->cmd[0] = MAINTENANCE_IN; + memset(cdb, 0x0, COMMAND_SIZE(MAINTENANCE_IN)); + cdb[0] = MAINTENANCE_IN; if (!(flags & ALUA_RTPG_EXT_HDR_UNSUPP)) - rq->cmd[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT; + cdb[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT; else - rq->cmd[1] = MI_REPORT_TARGET_PGS; - put_unaligned_be32(bufflen, >cmd[6]); - rq->cmd_len = COMMAND_SIZE(MAINTENANCE_IN); - - rq->sense = sense; - memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); - rq->sense_len = 0; - - blk_execute_rq(rq->q, NULL, rq, 1); - if (rq->errors) - err = rq->errors; - blk_put_request(rq); -done: - return err; + cdb[1] = MI_REPORT_TARGET_PGS; + put_unaligned_be32(bufflen, [6]); + + return scsi_execute_req_flags(sdev, cdb, DMA_FROM_DEVICE, + buff, bufflen, sshdr, + ALUA_FAILOVER_TIMEOUT * HZ, + ALUA_FAILOVER_RETRIES, NULL, req_flags); } /* @@ -175,40 +133,30 @@ done: * to 'active/optimized' and let the array firmware figure out * the states of the remaining groups. */ -static unsigned submit_stpg(struct scsi_device *sdev, int group_id, - unsigned char *sense) +static int submit_stpg(struct scsi_device *sdev, int group_id, + struct scsi_sense_hdr *sshdr) { - struct request *rq; + u8 cdb[COMMAND_SIZE(MAINTENANCE_OUT)]; unsigned char stpg_data[8]; int stpg_len = 8; - int err = 0; + int req_flags = REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | + REQ_FAILFAST_DRIVER; /* Prepare the data buffer */ memset(stpg_data, 0, stpg_len);
[PATCH 15/20] scsi_dh_alua: Send TEST UNIT READY to poll for transitioning
Sending a 'REPORT TARGET PORT GROUP' command is a costly operation, as the array has to gather information about all ports. So instead of using RTPG to poll for a status update when a port is in transitioning we should be sending a TEST UNIT READY, and wait for the sense code to report success. Signed-off-by: Hannes ReineckeReviewed-by: Ewan Milne --- drivers/scsi/device_handler/scsi_dh_alua.c | 37 ++ 1 file changed, 37 insertions(+) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 6ddbb88..6014750 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -489,6 +489,30 @@ static int alua_check_sense(struct scsi_device *sdev, } /* + * alua_tur - Send a TEST UNIT READY + * @sdev: device to which the TEST UNIT READY command should be send + * + * Send a TEST UNIT READY to @sdev to figure out the device state + * Returns SCSI_DH_RETRY if the sense code is NOT READY/ALUA TRANSITIONING, + * SCSI_DH_OK if no error occurred, and SCSI_DH_IO otherwise. + */ +static int alua_tur(struct scsi_device *sdev) +{ + struct scsi_sense_hdr sense_hdr; + int retval; + + retval = scsi_test_unit_ready(sdev, ALUA_FAILOVER_TIMEOUT * HZ, + ALUA_FAILOVER_RETRIES, _hdr); + if (sense_hdr.sense_key == NOT_READY && + sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a) + return SCSI_DH_RETRY; + else if (retval) + return SCSI_DH_IO; + else + return SCSI_DH_OK; +} + +/* * alua_rtpg - Evaluate REPORT TARGET GROUP STATES * @sdev: the device to be evaluated. * @@ -752,7 +776,20 @@ static void alua_rtpg_work(struct work_struct *work) } pg->flags |= ALUA_PG_RUNNING; if (pg->flags & ALUA_PG_RUN_RTPG) { + int state = pg->state; + spin_unlock_irqrestore(>lock, flags); + if (state == TPGS_STATE_TRANSITIONING) { + if (alua_tur(sdev) == SCSI_DH_RETRY) { + spin_lock_irqsave(>lock, flags); + pg->flags &= ~ALUA_PG_RUNNING; + spin_unlock_irqrestore(>lock, flags); + queue_delayed_work(kaluad_wq, >rtpg_work, + pg->interval * HZ); + return; + } + /* Send RTPG on failure or if TUR indicates SUCCESS */ + } err = alua_rtpg(sdev, pg); spin_lock_irqsave(>lock, flags); if (err == SCSI_DH_RETRY) { -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 14/20] scsi_dh_alua: update all port states
When we read in the target port group state we should be updating all affected port groups, otherwise we risk running out of sync. Signed-off-by: Hannes Reinecke--- drivers/scsi/device_handler/scsi_dh_alua.c | 31 +- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 04a3a543..6ddbb88 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -499,11 +499,13 @@ static int alua_check_sense(struct scsi_device *sdev, static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) { struct scsi_sense_hdr sense_hdr; + struct alua_port_group *tmp_pg; int len, k, off, valid_states = 0, bufflen = ALUA_RTPG_SIZE; - unsigned char *ucp, *buff; + unsigned char *desc, *buff; unsigned err, retval; unsigned int tpg_desc_tbl_off; unsigned char orig_transition_tmo; + unsigned long flags; if (!pg->expiry) { unsigned long transition_tmo = ALUA_FAILOVER_TIMEOUT * HZ; @@ -605,16 +607,27 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) else tpg_desc_tbl_off = 4; - for (k = tpg_desc_tbl_off, ucp = buff + tpg_desc_tbl_off; + for (k = tpg_desc_tbl_off, desc = buff + tpg_desc_tbl_off; k < len; -k += off, ucp += off) { - - if (pg->group_id == get_unaligned_be16([2])) { - pg->state = ucp[0] & 0x0f; - pg->pref = ucp[0] >> 7; - valid_states = ucp[1]; +k += off, desc += off) { + u16 group_id = get_unaligned_be16([2]); + + spin_lock_irqsave(_group_lock, flags); + list_for_each_entry(tmp_pg, _group_list, node) { + if (tmp_pg->group_id != group_id) + continue; + if (tmp_pg->device_id_len != pg->device_id_len) + continue; + if (strncmp(tmp_pg->device_id_str, pg->device_id_str, + tmp_pg->device_id_len)) + continue; + tmp_pg->state = desc[0] & 0x0f; + tmp_pg->pref = desc[0] >> 7; + if (tmp_pg == pg) + valid_states = desc[1]; } - off = 8 + (ucp[7] * 4); + spin_unlock_irqrestore(_group_lock, flags); + off = 8 + (desc[7] * 4); } sdev_printk(KERN_INFO, sdev, -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 02/20] scsi_dh_alua: separate out alua_stpg()
Separate out SET TARGET PORT GROUP functionality into a separate function alua_stpg(). Signed-off-by: Hannes Reinecke--- drivers/scsi/device_handler/scsi_dh_alua.c | 95 +++--- 1 file changed, 61 insertions(+), 34 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index df71e85..7c66e4a 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -579,6 +579,65 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, int wait_ } /* + * alua_stpg - Issue a SET TARGET GROUP STATES command + * + * Issue a SET TARGET GROUP STATES command and evaluate the + * response. Returns SCSI_DH_RETRY if the target port group + * state is found to be in 'transitioning'. + * If SCSI_DH_OK is returned the passed-in 'fn' function + * this function will take care of executing 'fn'. + * Otherwise 'fn' should be executed by the caller with the + * returned error code. + */ +static unsigned alua_stpg(struct scsi_device *sdev, struct alua_dh_data *h, + activate_complete fn, void *data) +{ + int err = SCSI_DH_OK; + int stpg = 0; + + if (!(h->tpgs & TPGS_MODE_EXPLICIT)) + /* Only implicit ALUA supported */ + goto out; + + switch (h->state) { + case TPGS_STATE_NONOPTIMIZED: + stpg = 1; + if ((h->flags & ALUA_OPTIMIZE_STPG) && + !h->pref && + (h->tpgs & TPGS_MODE_IMPLICIT)) + stpg = 0; + break; + case TPGS_STATE_STANDBY: + case TPGS_STATE_UNAVAILABLE: + stpg = 1; + break; + case TPGS_STATE_OFFLINE: + err = SCSI_DH_IO; + break; + case TPGS_STATE_TRANSITIONING: + err = SCSI_DH_RETRY; + break; + default: + break; + } + + if (stpg) { + h->callback_fn = fn; + h->callback_data = data; + err = submit_stpg(h); + if (err != SCSI_DH_OK) + h->callback_fn = h->callback_data = NULL; + else + fn = NULL; + } +out: + if (fn) + fn(data, err); + + return err; +} + +/* * alua_initialize - Initialize ALUA state * @sdev: the device to be initialized * @@ -655,7 +714,6 @@ static int alua_activate(struct scsi_device *sdev, { struct alua_dh_data *h = sdev->handler_data; int err = SCSI_DH_OK; - int stpg = 0; err = alua_rtpg(sdev, h, 1); if (err != SCSI_DH_OK) @@ -664,41 +722,10 @@ static int alua_activate(struct scsi_device *sdev, if (optimize_stpg) h->flags |= ALUA_OPTIMIZE_STPG; - if (h->tpgs & TPGS_MODE_EXPLICIT) { - switch (h->state) { - case TPGS_STATE_NONOPTIMIZED: - stpg = 1; - if ((h->flags & ALUA_OPTIMIZE_STPG) && - (!h->pref) && - (h->tpgs & TPGS_MODE_IMPLICIT)) - stpg = 0; - break; - case TPGS_STATE_STANDBY: - case TPGS_STATE_UNAVAILABLE: - stpg = 1; - break; - case TPGS_STATE_OFFLINE: - err = SCSI_DH_IO; - break; - case TPGS_STATE_TRANSITIONING: - err = SCSI_DH_RETRY; - break; - default: - break; - } - } - - if (stpg) { - h->callback_fn = fn; - h->callback_data = data; - err = submit_stpg(h); - if (err == SCSI_DH_OK) - return 0; - h->callback_fn = h->callback_data = NULL; - } + err = alua_stpg(sdev, h, fn, data); out: - if (fn) + if (err != SCSI_DH_OK && fn) fn(data, err); return 0; } -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 18/20] scsi_dh_alua: use common definitions for ALUA state
scsi_proto.h now contains definitions for the ALUA state, so we don't have to carry them in the device handler. Signed-off-by: Hannes Reinecke--- drivers/scsi/device_handler/scsi_dh_alua.c | 62 +- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 58c51ad..6c6ff0b 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -31,14 +31,6 @@ #define ALUA_DH_NAME "alua" #define ALUA_DH_VER "1.3" -#define TPGS_STATE_OPTIMIZED 0x0 -#define TPGS_STATE_NONOPTIMIZED0x1 -#define TPGS_STATE_STANDBY 0x2 -#define TPGS_STATE_UNAVAILABLE 0x3 -#define TPGS_STATE_LBA_DEPENDENT 0x4 -#define TPGS_STATE_OFFLINE 0xe -#define TPGS_STATE_TRANSITIONING 0xf - #define TPGS_SUPPORT_NONE 0x00 #define TPGS_SUPPORT_OPTIMIZED 0x01 #define TPGS_SUPPORT_NONOPTIMIZED 0x02 @@ -180,7 +172,7 @@ static int submit_stpg(struct scsi_device *sdev, int group_id, /* Prepare the data buffer */ memset(stpg_data, 0, stpg_len); - stpg_data[4] = TPGS_STATE_OPTIMIZED & 0x0f; + stpg_data[4] = SCSI_ACCESS_STATE_OPTIMAL & 0x0f; put_unaligned_be16(group_id, _data[6]); /* Prepare the command. */ @@ -244,7 +236,7 @@ struct alua_port_group *alua_get_pg(struct scsi_device *sdev, pg->device_id_len = id_size; pg->group_id = group_id; pg->tpgs = tpgs; - pg->state = TPGS_STATE_OPTIMIZED; + pg->state = SCSI_ACCESS_STATE_OPTIMAL; if (optimize_stpg) pg->flags |= ALUA_OPTIMIZE_STPG; kref_init(>kref); @@ -401,22 +393,22 @@ static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h, return SCSI_DH_OK; } -static char print_alua_state(int state) +static char print_alua_state(enum scsi_access_state state) { switch (state) { - case TPGS_STATE_OPTIMIZED: + case SCSI_ACCESS_STATE_OPTIMAL: return 'A'; - case TPGS_STATE_NONOPTIMIZED: + case SCSI_ACCESS_STATE_ACTIVE: return 'N'; - case TPGS_STATE_STANDBY: + case SCSI_ACCESS_STATE_STANDBY: return 'S'; - case TPGS_STATE_UNAVAILABLE: + case SCSI_ACCESS_STATE_UNAVAILABLE: return 'U'; - case TPGS_STATE_LBA_DEPENDENT: + case SCSI_ACCESS_STATE_LBA: return 'L'; - case TPGS_STATE_OFFLINE: + case SCSI_ACCESS_STATE_OFFLINE: return 'O'; - case TPGS_STATE_TRANSITIONING: + case SCSI_ACCESS_STATE_TRANSITIONING: return 'T'; default: return 'X'; @@ -667,7 +659,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) valid_states_SUPPORT_OPTIMIZED?'A':'a'); switch (pg->state) { - case TPGS_STATE_TRANSITIONING: + case SCSI_ACCESS_STATE_TRANSITIONING: if (time_before(jiffies, pg->expiry)) { /* State transition, retry */ pg->interval = 2; @@ -675,11 +667,11 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) } else { /* Transitioning time exceeded, set port to standby */ err = SCSI_DH_IO; - pg->state = TPGS_STATE_STANDBY; + pg->state = SCSI_ACCESS_STATE_STANDBY; pg->expiry = 0; } break; - case TPGS_STATE_OFFLINE: + case SCSI_ACCESS_STATE_OFFLINE: /* Path unusable */ err = SCSI_DH_DEV_OFFLINED; pg->expiry = 0; @@ -712,21 +704,21 @@ static unsigned alua_stpg(struct scsi_device *sdev, struct alua_port_group *pg) return SCSI_DH_RETRY; } switch (pg->state) { - case TPGS_STATE_OPTIMIZED: + case SCSI_ACCESS_STATE_OPTIMAL: return SCSI_DH_OK; - case TPGS_STATE_NONOPTIMIZED: + case SCSI_ACCESS_STATE_ACTIVE: if ((pg->flags & ALUA_OPTIMIZE_STPG) && !pg->pref && (pg->tpgs & TPGS_MODE_IMPLICIT)) return SCSI_DH_OK; break; - case TPGS_STATE_STANDBY: - case TPGS_STATE_UNAVAILABLE: + case SCSI_ACCESS_STATE_STANDBY: + case SCSI_ACCESS_STATE_UNAVAILABLE: break; - case TPGS_STATE_OFFLINE: + case SCSI_ACCESS_STATE_OFFLINE: return SCSI_DH_IO; break; - case TPGS_STATE_TRANSITIONING: + case SCSI_ACCESS_STATE_TRANSITIONING: break; default: sdev_printk(KERN_INFO, sdev, @@ -736,7 +728,7 @@ static unsigned alua_stpg(struct scsi_device
[PATCH 17/20] scsi: Add 'access_state' attribute
Add an 'access_state' attribute to struct scsi_device to display the asymmetric LUN access state. Signed-off-by: Hannes Reinecke--- drivers/scsi/scsi_scan.c | 1 + drivers/scsi/scsi_sysfs.c | 49 ++ include/scsi/scsi_device.h | 1 + include/scsi/scsi_proto.h | 13 4 files changed, 64 insertions(+) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 05dc1aa..9fc1531 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -231,6 +231,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, sdev->lun = lun; sdev->channel = starget->channel; sdev->sdev_state = SDEV_CREATED; + sdev->access_state = SCSI_ACCESS_STATE_UNKNOWN; INIT_LIST_HEAD(>siblings); INIT_LIST_HEAD(>same_target_siblings); INIT_LIST_HEAD(>cmd_list); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index ef36053..80b4c67 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -81,6 +81,34 @@ const char *scsi_host_state_name(enum scsi_host_state state) return name; } +static const struct { + enum scsi_access_state value; + char*name; +} sdev_access_states[] = { + { SCSI_ACCESS_STATE_OPTIMAL, "active/optimized" }, + { SCSI_ACCESS_STATE_ACTIVE, "active/non-optimized" }, + { SCSI_ACCESS_STATE_STANDBY, "standby" }, + { SCSI_ACCESS_STATE_UNAVAILABLE, "unavailable" }, + { SCSI_ACCESS_STATE_LBA, "lba-dependent" }, + { SCSI_ACCESS_STATE_OFFLINE, "offline" }, + { SCSI_ACCESS_STATE_TRANSITIONING, "transitioning" }, + { SCSI_ACCESS_STATE_UNKNOWN, "unknown" }, +}; + +const char *scsi_access_state_name(enum scsi_access_state state) +{ + int i; + char *name = NULL; + + for (i = 0; i < ARRAY_SIZE(sdev_access_states); i++) { + if (sdev_access_states[i].value == state) { + name = sdev_access_states[i].name; + break; + } + } + return name; +} + static int check_set(unsigned long long *val, char *src) { char *last; @@ -973,6 +1001,26 @@ sdev_store_dh_state(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(dh_state, S_IRUGO | S_IWUSR, sdev_show_dh_state, sdev_store_dh_state); + +static ssize_t +sdev_show_access_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + enum scsi_access_state access_state; + bool pref = false; + + if (sdev->access_state & SCSI_ACCESS_STATE_PREFERRED) + pref = true; + + access_state = (sdev->access_state & SCSI_ACCESS_STATE_MASK); + + return snprintf(buf, 32, "%s%s\n", + scsi_access_state_name(access_state), + pref ? " preferred" : ""); +} +static DEVICE_ATTR(access_state, S_IRUGO, sdev_show_access_state, NULL); #endif static ssize_t @@ -1047,6 +1095,7 @@ static struct attribute *scsi_sdev_attrs[] = { _attr_wwid.attr, #ifdef CONFIG_SCSI_DH _attr_dh_state.attr, + _attr_access_state.attr, #endif _attr_queue_ramp_up_period.attr, REF_EVT(media_change), diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index f63a167..5dd00e9f 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -200,6 +200,7 @@ struct scsi_device { struct scsi_device_handler *handler; void*handler_data; + enum scsi_access_state access_state; enum scsi_device_state sdev_state; unsigned long sdev_data[0]; } __attribute__((aligned(sizeof(unsigned long; diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h index a9fbf1b..80e85e7 100644 --- a/include/scsi/scsi_proto.h +++ b/include/scsi/scsi_proto.h @@ -277,5 +277,18 @@ struct scsi_lun { __u8 scsi_lun[8]; }; +/* SPC asymmetric access states */ +enum scsi_access_state { + SCSI_ACCESS_STATE_OPTIMAL = 0, + SCSI_ACCESS_STATE_ACTIVE, + SCSI_ACCESS_STATE_STANDBY, + SCSI_ACCESS_STATE_UNAVAILABLE, + SCSI_ACCESS_STATE_LBA, + SCSI_ACCESS_STATE_OFFLINE = 0xe, + SCSI_ACCESS_STATE_TRANSITIONING = 0xf, + SCSI_ACCESS_STATE_UNKNOWN = 0x70, +}; +#define SCSI_ACCESS_STATE_MASK 0x0f +#define SCSI_ACCESS_STATE_PREFERRED 0x80 #endif /* _SCSI_PROTO_H_ */ -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 00/20] ALUA device handler update, part II
Hi all, as promised here is now the second part of my ALUA device handler update. This contains a major rework of the ALUA device handler as execution is moved onto a workqueue. This has the advantage that we avoid having to do multiple calls to the same LUN (as happens frequently when failing over a LUN with several paths) and finally retries are handled correctly. As some arrays are only capable of handling one STPG at a time I've added a module parameter 'sync_stpg' which switches to a singlethreaded workqueue, thereby effectively synchronize STPG handling. Thanks to Bart for this suggestion. As usual, comments and reviews are welcome. Hannes Reinecke (20): scsi_dh_alua: Pass buffer as function argument scsi_dh_alua: separate out alua_stpg() scsi_dh_alua: Make stpg synchronous scsi_dh_alua: call alua_rtpg() if stpg fails scsi_dh_alua: switch to scsi_execute_req_flags() scsi_dh_alua: Use separate alua_port_group structure scsi_dh_alua: allocate RTPG buffer separately scsi_dh_alua: use unique device id scsi_dh_alua: simplify alua_initialize() revert commit a8e5a2d593cb ("[SCSI] scsi_dh_alua: ALUA handler attach should succeed while TPG is transitioning") scsi_dh_alua: Use workqueue for RTPG scsi_dh_alua: Allow workqueue to run synchronously scsi_dh_alua: Recheck state on unit attention scsi_dh_alua: update all port states scsi_dh_alua: Send TEST UNIT READY to poll for transitioning scsi_dh: add 'rescan' callback scsi: Add 'access_state' attribute scsi_dh_alua: use common definitions for ALUA state scsi_dh_alua: update 'access_state' field scsi_dh_alua: Update version to 2.0 drivers/scsi/device_handler/scsi_dh_alua.c | 977 - drivers/scsi/scsi_lib.c| 1 + drivers/scsi/scsi_scan.c | 9 +- drivers/scsi/scsi_sysfs.c | 49 ++ include/scsi/scsi_device.h | 1 + include/scsi/scsi_dh.h | 2 + include/scsi/scsi_proto.h | 13 + 7 files changed, 745 insertions(+), 307 deletions(-) -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 01/20] scsi_dh_alua: Pass buffer as function argument
Pass in the buffer as a function argument for submit_rtpg(). Reviewed-by: Martin K. PetersenReviewed-by: Christoph Hellwig Reviewed-by: Bart Van Assche Signed-off-by: Hannes Reinecke --- drivers/scsi/device_handler/scsi_dh_alua.c | 13 +++-- 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 5a328bf..df71e85 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -135,12 +135,13 @@ static struct request *get_alua_req(struct scsi_device *sdev, * submit_rtpg - Issue a REPORT TARGET GROUP STATES command * @sdev: sdev the command should be sent to */ -static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) +static unsigned submit_rtpg(struct scsi_device *sdev, unsigned char *buff, + int bufflen, unsigned char *sense, int flags) { struct request *rq; int err = 0; - rq = get_alua_req(sdev, h->buff, h->bufflen, READ); + rq = get_alua_req(sdev, buff, bufflen, READ); if (!rq) { err = DRIVER_BUSY << 24; goto done; @@ -148,14 +149,14 @@ static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) /* Prepare the command. */ rq->cmd[0] = MAINTENANCE_IN; - if (!(h->flags & ALUA_RTPG_EXT_HDR_UNSUPP)) + if (!(flags & ALUA_RTPG_EXT_HDR_UNSUPP)) rq->cmd[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT; else rq->cmd[1] = MI_REPORT_TARGET_PGS; - put_unaligned_be32(h->bufflen, >cmd[6]); + put_unaligned_be32(bufflen, >cmd[6]); rq->cmd_len = COMMAND_SIZE(MAINTENANCE_IN); - rq->sense = h->sense; + rq->sense = sense; memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); rq->sense_len = 0; @@ -446,7 +447,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, int wait_ expiry = round_jiffies_up(jiffies + h->transition_tmo * HZ); retry: - retval = submit_rtpg(sdev, h); + retval = submit_rtpg(sdev, h->buff, h->bufflen, h->sense, h->flags); if (retval) { if (!scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, _hdr)) { -- 1.8.5.6 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[Bug 108771] scsi: ses: kasan: ses_enclosure_data_process use after free on boot SAS2X28
https://bugzilla.kernel.org/show_bug.cgi?id=108771 --- Comment #1 from Pavel Tikhomirov--- Aditional info about enclosue(from that node, but older 3.10 based kernel): [root@p9 crash]# modprobe sg [root@p9 crash]# sg_map -i /dev/sg0 LSI SAS2X28 0e12 /dev/sg1 /dev/sda LSI MR9260-4i 2.13 [root@p9 crash]# lsscsi -gs [1:0:16:0] enclosu LSI SAS2X28 0e12 - /dev/sg0 - [1:2:0:0]diskLSI MR9260-4i2.13 /dev/sda /dev/sg1 3.99TB [root@p9 crash]# sg_ses /dev/sg0 LSI SAS2X28 0e12 Supported diagnostic pages: Supported Diagnostic Pages [sdp] [0x0] Configuration (SES) [cf] [0x1] Enclosure Status/Control (SES) [ec,es] [0x2] Element Descriptor (SES) [ed] [0x7] Additional Element Status (SES-2) [aes] [0xa] Download Microcode (SES-2) [dm] [0xe] [root@p9 crash]# sg_ses /dev/sg1 LSI MR9260-4i 2.13 disk device (not an enclosure) Supported diagnostic pages: -- You are receiving this mail because: You are watching the assignee of the bug. -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 04/10] aacraid: Fix memory leak in aac_fib_map_free
On 5.12.2015 01:40, Raghava Aditya Renukunta wrote: > Hello Tomas, > > >> -Original Message- >> From: Tomas Henzl [mailto:the...@redhat.com] >> Sent: Friday, December 4, 2015 6:35 AM >> To: Raghava Aditya Renukunta; jbottom...@parallels.com; linux- >> s...@vger.kernel.org >> Cc: Mahesh Rajashekhara; Murthy Bhat; Santosh Akula; Gana Sridaran; >> aacr...@pmc-sierra.com; Rich Bono >> Subject: Re: [PATCH 04/10] aacraid: Fix memory leak in aac_fib_map_free >> >> On 1.12.2015 13:39, Raghava Aditya Renukunta wrote: >>> From: Raghava Aditya Renukunta>>> >>> aac_fib_map_free() calls pci_free_consistent() without checking that >>> dev->hw_fib_va is not NULL and dev->max_fib_size is not zero.If they >>> are indeed NULL/0, this will result in a hang as pci_free_consistent() >>> will attempt to invalidate cache for the entire 64-bit address space >>> (which would take a very long time). >>> >>> Fixed by adding a check to make sure that dev->hw_fib_va and >>> dev->max_fib_size are not NULL and 0 respectively. >>> >>> Signed-off-by: Raghava Aditya Renukunta >> >> >> Reviewed-by: Tomas Henzl >> >> Is the can_queue constant during the driver's life, or is it possible >> to manipulate it (aac_change_queue_depth)? >> >> Tomas > can_queue is only changed in aac_init_adapter. Do you want to save > (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB) in a variable > So that the whole can_queue dereference need not be used? It's fine as it is, (I thought it may change elsewhere in your code but now I think that I was wrong). --tm > > Regards, > Raghava Aditya > >>> --- >>> drivers/scsi/aacraid/commsup.c | 9 ++--- >>> 1 file changed, 6 insertions(+), 3 deletions(-) >>> >>> diff --git a/drivers/scsi/aacraid/commsup.c >> b/drivers/scsi/aacraid/commsup.c >>> index b257d3b..9533f47 100644 >>> --- a/drivers/scsi/aacraid/commsup.c >>> +++ b/drivers/scsi/aacraid/commsup.c >>> @@ -83,9 +83,12 @@ static int fib_map_alloc(struct aac_dev *dev) >>> >>> void aac_fib_map_free(struct aac_dev *dev) >>> { >>> - pci_free_consistent(dev->pdev, >>> - dev->max_fib_size * (dev->scsi_host_ptr->can_queue + >> AAC_NUM_MGT_FIB), >>> - dev->hw_fib_va, dev->hw_fib_pa); >>> + if (dev->hw_fib_va && dev->max_fib_size) { >>> + pci_free_consistent(dev->pdev, >>> + (dev->max_fib_size * >>> + (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB)), >>> + dev->hw_fib_va, dev->hw_fib_pa); >>> + } >>> dev->hw_fib_va = NULL; >>> dev->hw_fib_pa = 0; >>> } > -- > To unsubscribe from this list: send the line "unsubscribe linux-scsi" in > the body of a message to majord...@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] scsi_transport_fc: cancel scan work always before freeing fc_rport.
Hello, on my FC environment target machine hanged always while rebooting the initiator machine. I was able to capture the following call trace: [19236.146988] rport-11:0-0: blocked FC remote port time out: removing target and saving binding [19236.157185] rport-10:0-0: blocked FC remote port time out: removing target and saving binding [19236.157288] scsi scan: 37 byte inquiry failed. Consider BLIST_INQUIRY_36 for this device [19236.157290] scsi scan: 37 byte inquiry failed. Consider BLIST_INQUIRY_36 for this device [19236.157412] BUG: unable to handle kernel NULL pointer dereference at (null) [19236.157416] IP: [] scsi_device_put+0xf/0x50 [19236.157423] PGD 0 [19236.157425] Oops: [#1] SMP [19236.157427] Modules linked in: iscsi_scst(O) scst_vdisk(O) qla2x00tgt(O) scst(O) sch_htb rpcsec_gss_krb5 nls_iso8859_1 nls_cp437 vfat fat zfs(PO) zunicode(PO) zavl(PO) zcommon(PO) znvpair(PO) spl(O) crc32c_intel sg qla2xxx(O) scsi_transport_fc mpt2sas(O) raid_class scsi_transport_sas button acpi_cpufreq mperf processor ixgbe(O) igb(O) ptp pps_core aufs [last unloaded: scst] [19236.157449] CPU: 0 PID: 28914 Comm: kworker/0:0 Tainted: P O 3.10.92-oe64-ge331686 #15 [19236.157451] Hardware name: Supermicro X8DTS/X8DTS, BIOS 2.1 06/25/2012 [19236.157457] Workqueue: fc_wq_10 fc_starget_delete [scsi_transport_fc] [19236.157459] task: 88030d8741a0 ti: 8802ec38e000 task.ti: 8802ec38e000 [19236.157461] RIP: 0010:[] [] scsi_device_put+0xf/0x50 [19236.157464] RSP: 0018:8802ec38fdf0 EFLAGS: 00010202 [19236.157466] RAX: RBX: 88030be48800 RCX: 000181ba [19236.157467] RDX: 000181bb RSI: 88030e4b0860 RDI: 88030be48800 [19236.157469] RBP: 88032ca8d000 R08: R09: ea000c392c00 [19236.157470] R10: 880332803d00 R11: 8142992c R12: 88032b951860 [19236.157472] R13: 88032ca8d010 R14: 8802ef3e0c00 R15: 88030be48800 [19236.157474] FS: () GS:880332e0() knlGS: [19236.157475] CS: 0010 DS: ES: CR0: 8005003b [19236.157477] CR2: CR3: 0195e000 CR4: 07f0 [19236.157478] DR0: DR1: DR2: [19236.157480] DR3: DR6: 0ff0 DR7: 0400 [19236.157481] Stack: [19236.157482] 88032ca8d000 88032ca8d000 81429aba 0286 [19236.157484] 8802dd800800 88032b951b08 880332e11680 [19236.157487] e8a05900 0001 8105ce4d 8105a4a7 [19236.157489] Call Trace: [19236.157494] [] ? scsi_remove_target+0x16a/0x250 [19236.157499] [] ? process_one_work+0x13d/0x3b0 [19236.157502] [] ? pwq_activate_delayed_work+0x27/0x40 [19236.157504] [] ? worker_thread+0x121/0x3d0 [19236.157507] [] ? manage_workers.isra.26+0x280/0x280 [19236.157510] [] ? kthread+0xc2/0xd0 [19236.157514] [] ? sched_clock_cpu+0x30/0x100 [19236.157517] [] ? kthread_create_on_node+0x110/0x110 [19236.157521] [] ? ret_from_fork+0x58/0x90 [19236.157524] [] ? kthread_create_on_node+0x110/0x110 [19236.157525] Code: 7d 58 4c 89 fe e8 92 a2 27 00 48 89 d8 5b 5d 41 5c 41 5d 41 5e 41 5f c3 0f 1f 40 00 55 53 48 89 fb 48 8b 07 48 8b 80 c0 00 00 00 <48> 8b 28 48 85 ed 74 0d 48 89 ef e8 71 c4 c6 ff 48 85 c0 75 14 [19236.157548] RIP [] scsi_device_put+0xf/0x50 [19236.157551] RSP [19236.157552] CR2: [19236.157555] ---[ end trace 37bfa3906f93d93a ]--- [19236.157578] BUG: unable to handle kernel paging request at ffd8 [19236.157580] IP: [] kthread_data+0x7/0x10 [19236.157583] PGD 1961067 PUD 1963067 PMD 0 [19236.157586] Oops: [#2] SMP [19236.157587] Modules linked in: iscsi_scst(O) scst_vdisk(O) qla2x00tgt(O) scst(O) sch_htb rpcsec_gss_krb5 nls_iso8859_1 nls_cp437 vfat fat zfs(PO) zunicode(PO) zavl(PO) zcommon(PO) znvpair(PO) spl(O) crc32c_intel sg qla2xxx(O) scsi_transport_fc mpt2sas(O) raid_class scsi_transport_sas button acpi_cpufreq mperf processor ixgbe(O) igb(O) ptp pps_core aufs [last unloaded: scst] [19236.157605] CPU: 0 PID: 28914 Comm: kworker/0:0 Tainted: P DO 3.10.92-oe64-ge331686 #15 [19236.157606] Hardware name: Supermicro X8DTS/X8DTS, BIOS 2.1 06/25/2012 [19236.157617] task: 88030d8741a0 ti: 8802ec38e000 task.ti: 8802ec38e000 [19236.157618] RIP: 0010:[] [] kthread_data+0x7/0x10 [19236.157621] RSP: 0018:8802ec38fa48 EFLAGS: 00010002 [19236.157623] RAX: RBX: RCX: 0001 [19236.157624] RDX: RSI: RDI: 88030d8741a0 [19236.157626] RBP: 88030d8741a0 R08: R09: 880332803a00 [19236.157627] R10: 880332e14a80 R11: ea000b862a00 R12: [19236.157629] R13: 88030d874490 R14: 88030d874190 R15: 0246 [19236.157630] FS: ()
Re: [PATCH v2 77/71] ncr5380: Fix wait for 53C80 registers registers after PDMA
On Monday 07 December 2015 04:16:14 Finn Thain wrote: > > On Mon, 7 Dec 2015, Ondrej Zary wrote: > > > The check for 53C80 registers accessibility was commented out because > > it was broken (inverted). Fix and enable it. > > > > Signed-off-by: Ondrej Zary> > --- > > drivers/scsi/g_NCR5380.c | 37 ++--- > > 1 file changed, 6 insertions(+), 31 deletions(-) > > > > diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c > > index 038dddf..a7479c6 100644 > > --- a/drivers/scsi/g_NCR5380.c > > +++ b/drivers/scsi/g_NCR5380.c > > @@ -603,14 +603,10 @@ static inline int NCR5380_pread(struct Scsi_Host > > *instance, unsigned char *dst, > > if (!(NCR5380_read(hostdata->c400_ctl_status) & CSR_GATED_53C80_IRQ)) > > printk("53C400r: no 53C80 gated irq after transfer"); > > > > -#if 0 > > - /* > > -* DON'T DO THIS - THEY NEVER ARRIVE! > > -*/ > > - printk("53C400r: Waiting for 53C80 registers\n"); > > - while (NCR5380_read(hostdata->c400_ctl_status) & CSR_53C80_REG) > > + /* wait for 53C80 registers to be available */ > > + while (!(NCR5380_read(hostdata->c400_ctl_status) & CSR_53C80_REG)) > > ; > > In the previous patch, udelay(4) was added to a CSR_GATED_53C80_IRQ > polling loop. It is interesting that you don't need it here when polling > CSR_53C80_REG. Yes, it's weird. Reads work fine without the delay. Small writes work most of the time without the delay but crash sometimes. Large writes crash the chip consistently without the delay. > > -#endif > > + > > if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER)) > > printk(KERN_ERR "53C400r: no end dma signal\n"); > > > > @@ -632,7 +628,6 @@ static inline int NCR5380_pwrite(struct Scsi_Host > > *instance, unsigned char *src, > > struct NCR5380_hostdata *hostdata = shost_priv(instance); > > int blocks = len / 128; > > int start = 0; > > - int i; > > > > NCR5380_write(hostdata->c400_ctl_status, CSR_BASE); > > NCR5380_write(hostdata->c400_blk_cnt, blocks); > > @@ -681,36 +676,16 @@ static inline int NCR5380_pwrite(struct Scsi_Host > > *instance, unsigned char *src, > > blocks--; > > } > > > > -#if 0 > > - printk("53C400w: waiting for registers to be available\n"); > > - THEY NEVER DO ! while (NCR5380_read(hostdata->c400_ctl_status) & > > CSR_53C80_REG); > > - printk("53C400w: Got em\n"); > > -#endif > > - > > - /* Let's wait for this instead - could be ugly */ > > - /* All documentation says to check for this. Maybe my hardware is too > > -* fast. Waiting for it seems to work fine! KLL > > -*/ > > - while (!(i = NCR5380_read(hostdata->c400_ctl_status) & > > CSR_GATED_53C80_IRQ)) { > > + /* wait for 53C80 registers to be available */ > > + while (!(NCR5380_read(hostdata->c400_ctl_status) & CSR_53C80_REG)) { > > udelay(4); /* DTC436 chip hangs without this */ > > ... based on the above, this udelay is probably not needed. > > (Or perhaps it is only needed once, in between the final host_buf register > access and the first ctl_status access??) I guess that the delay is needed when the chip does write in the background. But why only on the last block? Adding delays inside the while(1) loop does not help, it crashes anyway. Single delay before the first ctl_status does not help either (perhaps only if it's long enough for the write to complete). The chip also crashes in transfer_pio during bigger transfers in a similar way. With Quantum HDD, it did not crash once I got PDMA working. But with a faster IBM HDD, it crashes even with smaller PIO trasnfers. > Is there any reference in the docs to timing sensitivity? Haven't found anything in NCR docs. Unfortunately, we don't have any DTC docs. > > /* FIXME - no timeout */ > > } > > > > - /* > > -* I know. i is certainly != 0 here but the loop is new. See previous > > -* comment. > > -*/ > > Thanks for cleaning up this mess! > -- Ondrej Zary -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
transakce~
napiste mi na e-mailovou adresu pro podrobnosti o vzájemne spoluprace. jng.ch...@gmail.com -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 05/20] qla2xxx: Added interface to send ELS commands from driver.
Hi Himanshu, [auto build test WARNING on target/master] [also build test WARNING on v4.4-rc4 next-20151207] [cannot apply to scsi/for-next] url: https://github.com/0day-ci/linux/commits/Himanshu-Madhani/qla2xxx-Patches-for-target-pending-branch/20151208-093328 base: https://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending.git master coccinelle warnings: (new ones prefixed by >>) >> drivers/scsi/qla2xxx/qla_iocb.c:2022:2-7: WARNING: NULL check before freeing >> functions like kfree, debugfs_remove, debugfs_remove_recursive or >> usb_free_urb is not needed. Maybe consider reorganizing relevant code to >> avoid passing NULL values. Please review and possibly fold the followup patch. --- 0-DAY kernel test infrastructureOpen Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH] qla2xxx: fix ifnullfree.cocci warnings
drivers/scsi/qla2xxx/qla_iocb.c:2022:2-7: WARNING: NULL check before freeing functions like kfree, debugfs_remove, debugfs_remove_recursive or usb_free_urb is not needed. Maybe consider reorganizing relevant code to avoid passing NULL values. NULL check before some freeing functions is not needed. Based on checkpatch warning "kfree(NULL) is safe this check is probably not required" and kfreeaddr.cocci by Julia Lawall. Generated by: scripts/coccinelle/free/ifnullfree.cocci CC: Himanshu MadhaniSigned-off-by: Fengguang Wu --- qla_iocb.c |3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -2018,8 +2018,7 @@ qla2x00_els_dcmd_sp_free(void *ptr, void srb_t *sp = (srb_t *)data; struct srb_iocb *elsio = >u.iocb_cmd; - if (sp->fcport) - kfree(sp->fcport); + kfree(sp->fcport); if (elsio->u.els_logo.els_logo_pyld) dma_free_coherent(>pdev->dev, DMA_POOL_SIZE, -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 13/20] qla2xxx: Remove dependency on hardware_lock to reduce lock contention.
Looks suspicious. Please check. julia On Tue, 8 Dec 2015, kbuild test robot wrote: > CC: kbuild-...@01.org > In-Reply-To: <1449535747-2850-14-git-send-email-himanshu.madh...@qlogic.com> > TO: Himanshu Madhani <himanshu.madh...@qlogic.com> > CC: target-de...@vger.kernel.org, n...@linux-iscsi.org > CC: giridhar.malav...@qlogic.com, linux-scsi@vger.kernel.org, > himanshu.madh...@qlogic.com > > Hi Quinn, > > [auto build test WARNING on target/master] > [also build test WARNING on v4.4-rc4 next-20151207] > [cannot apply to scsi/for-next] > > url: > https://github.com/0day-ci/linux/commits/Himanshu-Madhani/qla2xxx-Patches-for-target-pending-branch/20151208-093328 > base: > https://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending.git master > :: branch date: 47 minutes ago > :: commit date: 47 minutes ago > > >> drivers/scsi/qla2xxx/qla_target.c:1091:2-8: preceding lock on line 1074 > > git remote add linux-review https://github.com/0day-ci/linux > git remote update linux-review > git checkout 13bfc932c19778c08e1fc8908c4f0825fd70583f > vim +1091 drivers/scsi/qla2xxx/qla_target.c > > 2d70c103 Nicholas Bellinger 2012-05-15 1068 if > (!vha->hw->tgt.tgt_ops) > 2d70c103 Nicholas Bellinger 2012-05-15 1069 return; > 2d70c103 Nicholas Bellinger 2012-05-15 1070 > b2032fd5 Roland Dreier 2015-07-14 1071 if (!tgt) > 2d70c103 Nicholas Bellinger 2012-05-15 1072 return; > 2d70c103 Nicholas Bellinger 2012-05-15 1073 > 13bfc932 Quinn Tran 2015-12-07 @1074 > spin_lock_irqsave(>hw->tgt.sess_lock, flags); > 2d70c103 Nicholas Bellinger 2012-05-15 1075 if (tgt->tgt_stop) { > 13bfc932 Quinn Tran 2015-12-07 1076 > spin_unlock_irqrestore(>hw->tgt.sess_lock, flags); > 2d70c103 Nicholas Bellinger 2012-05-15 1077 return; > 2d70c103 Nicholas Bellinger 2012-05-15 1078 } > 2d70c103 Nicholas Bellinger 2012-05-15 1079 sess = > qlt_find_sess_by_port_name(tgt, fcport->port_name); > 2d70c103 Nicholas Bellinger 2012-05-15 1080 if (!sess) { > 13bfc932 Quinn Tran 2015-12-07 1081 > spin_unlock_irqrestore(>hw->tgt.sess_lock, flags); > 2d70c103 Nicholas Bellinger 2012-05-15 1082 return; > 2d70c103 Nicholas Bellinger 2012-05-15 1083 } > 2d70c103 Nicholas Bellinger 2012-05-15 1084 > df673274 Alexei Potashnik 2015-07-14 1085 if (max_gen - > sess->generation < 0) { > df673274 Alexei Potashnik 2015-07-14 1086 > ql_dbg(ql_dbg_tgt_mgt, vha, 0xf092, > df673274 Alexei Potashnik 2015-07-14 1087 "Ignoring > stale deletion request for se_sess %p / sess %p" > df673274 Alexei Potashnik 2015-07-14 1088 " for port > %8phC, req_gen %d, sess_gen %d\n", > df673274 Alexei Potashnik 2015-07-14 1089 > sess->se_sess, sess, sess->port_name, max_gen, > df673274 Alexei Potashnik 2015-07-14 1090 > sess->generation); > df673274 Alexei Potashnik 2015-07-14 @1091 return; > df673274 Alexei Potashnik 2015-07-14 1092 } > df673274 Alexei Potashnik 2015-07-14 1093 > 2d70c103 Nicholas Bellinger 2012-05-15 1094 ql_dbg(ql_dbg_tgt_mgt, > vha, 0xf008, "qla_tgt_fc_port_deleted %p", sess); > > :: The code at line 1091 was first introduced by commit > :: df673274fa4896f25f0bf348d2a3535d74b4cbec qla2xxx: added sess > generations to detect RSCN update races > > :: TO: Alexei Potashnik <ale...@purestorage.com> > :: CC: Nicholas Bellinger <n...@linux-iscsi.org> > > --- > 0-DAY kernel test infrastructureOpen Source Technology Center > https://lists.01.org/pipermail/kbuild-all Intel Corporation > -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 09/20] qla2xxx: Change check_stop_free to always return 1
On Mon, Dec 07, 2015 at 07:48:56PM -0500, Himanshu Madhani wrote: > From: Quinn Tran> > change tcm_qla2xxx_check_stop_free to always return 1 > to prevent transport_cmd_finish_abort from accidently > taking extra kref_put. This looks a bit fishy. Even if this was the right behavior it's something all something all drivers returning the value of target_put_sess_cmd in their check_stop_free would need to do. Can you explain the issue you're trying to address in a little more detail? -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 10/20] qla2xxx: Fix interaction issue between qla2xxx and Target Core Module
> -void qlt_abort_cmd(struct qla_tgt_cmd *cmd) > +int qlt_abort_cmd(struct qla_tgt_cmd *cmd) > { > struct qla_tgt *tgt = cmd->tgt; > struct scsi_qla_host *vha = tgt->vha; > struct se_cmd *se_cmd = >se_cmd; > + unsigned long flags,refcount; > > ql_dbg(ql_dbg_tgt_mgt, vha, 0xf014, > "qla_target(%d): terminating exchange for aborted cmd=%p " > "(se_cmd=%p, tag=%llu)", vha->vp_idx, cmd, >se_cmd, > se_cmd->tag); > > +spin_lock_irqsave(>cmd_lock, flags); > +if (cmd->aborted) { > +spin_unlock_irqrestore(>cmd_lock, flags); > + > +/* It's normal to see 2 calls in this path: > + * 1) XFER Rdy completion + CMD_T_ABORT > + * 2) TCM TMR - drain_state_list > + */ > +refcount = atomic_read(>se_cmd.cmd_kref.refcount); > +ql_dbg(ql_dbg_tgt_mgt, vha, 0x, > + "multiple abort. %p refcount %lx" > + "transport_state %x, t_state %x, se_cmd_flags %x \n", > + cmd, refcount,cmd->se_cmd.transport_state, > + cmd->se_cmd.t_state,cmd->se_cmd.se_cmd_flags); > + > +return EIO; > +} Err, no. Looking into the refcount inside a kref is never the right thing to do. > +typedef enum { > + /* > + * BIT_0 - Atio Arrival / schedule to work > + * BIT_1 - qlt_do_work > + * BIT_2 - qlt_do work failed > + * BIT_3 - xfer rdy/tcm_qla2xxx_write_pending > + * BIT_4 - read respond/tcm_qla2xx_queue_data_in > + * BIT_5 - status respond / tcm_qla2xx_queue_status > + * BIT_6 - tcm request to abort/Term exchange. > + * pre_xmit_response->qlt_send_term_exchange > + * BIT_7 - SRR received (qlt_handle_srr->qlt_xmit_response) > + * BIT_8 - SRR received (qlt_handle_srr->qlt_rdy_to_xfer) > + * BIT_9 - SRR received (qla_handle_srr->qlt_send_term_exchange) > + * BIT_10 - Data in - hanlde_data->tcm_qla2xxx_handle_data > + > + * BIT_12 - good completion - qlt_ctio_do_completion -->free_cmd > + * BIT_13 - Bad completion - > + * qlt_ctio_do_completion --> qlt_term_ctio_exchange > + * BIT_14 - Back end data received/sent. > + * BIT_15 - SRR prepare ctio > + * BIT_16 - complete free > + * BIT_17 - flush - qlt_abort_cmd_on_host_reset > + * BIT_18 - completion w/abort status > + * BIT_19 - completion w/unknown status > + * BIT_20 - tcm_qla2xxx_free_cmd Please use descriptive names for these flags in the source code! > + BUG_ON(cmd->cmd_flags & BIT_20); > + cmd->cmd_flags |= BIT_20; > + And no crazieness like this. While we're at it: what synchronizes access to ->cmd_flags? > @@ -466,13 +484,25 @@ static int tcm_qla2xxx_handle_cmd(scsi_qla_host_t *vha, > struct qla_tgt_cmd *cmd, > static void tcm_qla2xxx_handle_data_work(struct work_struct *work) > { > struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work); > + unsigned long flags; > > /* >* Ensure that the complete FCP WRITE payload has been received. >* Otherwise return an exception via CHECK_CONDITION status. >*/ > cmd->cmd_in_wq = 0; > - cmd->cmd_flags |= BIT_11; > + > + spin_lock_irqsave(>cmd_lock, flags); > + cmd->cmd_flags |= CMD_FLAG_DATA_WORK; > + if (cmd->aborted) { > + cmd->cmd_flags |= CMD_FLAG_DATA_WORK_FREE; > + spin_unlock_irqrestore(>cmd_lock, flags); > + > + tcm_qla2xxx_free_cmd(cmd); > + return; > + } > + spin_unlock_irqrestore(>cmd_lock, flags); All these abort flag hacks look very suspicios. Can you explain the exact theory of operation behind them? -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 11/20] qla2xxx: Add TAS detection for kernel 3.15 n newer
> diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c > b/drivers/scsi/qla2xxx/tcm_qla2xxx.c > index 842fcca..2e9c194 100644 > --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c > +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c > @@ -617,6 +617,26 @@ static int tcm_qla2xxx_queue_status(struct se_cmd > *se_cmd) > struct qla_tgt_cmd, se_cmd); > int xmit_type = QLA_TGT_XMIT_STATUS; > > + if (se_cmd->transport_state & CMD_T_ABORTED) { > + /* For TCM TAS support n kernel >= 3.15: > + * This cmd is attempting to respond with "Task Aborted Status". > + */ > + if (cmd->aborted) { > + return 0; > + } else if ((cmd->state == QLA_TGT_STATE_NEED_DATA) && > + cmd->cmd_sent_to_fw) { > + qlt_abort_cmd(cmd); > + return 0; > + } else if (cmd->state == QLA_TGT_STATE_PROCESSED) { > + if (cmd->cmd_sent_to_fw) { > + qlt_abort_cmd(cmd); > + return 0; > + } else {/* about to be free */ > + return 0; > + } > + } > + } > + This is really something that should be explicitly communicated from the core instead of having to second guess it. -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 12/20] target/tmr: LUN reset cause cmd premature free.
On Mon, Dec 07, 2015 at 07:48:59PM -0500, Himanshu Madhani wrote: > From: Quinn Tran> > During LUN/Target reset, the TMR code attempt to intercept > cmds and try to aborted them. Current code assume cmds are > always intercepted at the back end device. The cleanup code > would issue a "queue_status() & check_stop_free()" to terminate > the command. However, when a cmd is intercepted at the front > end/Fabric layer, current code introduce premature free or > cause Fabric to double free. > > When command is intercepted at Fabric layer, it means a > check_stop_free(cmd_kref--) has been called. The extra > check_stop_free in the Lun Reset cleanup code causes early > free. When a cmd in the Fabric layer is completed, the normal > free code adds another another free which introduce a double free. > > To fix the issue: > - add a new flag/CMD_T_SENT_STATUS to track command that have > made it down to fabric layer after back end good/bad completion. > - if cmd reach Fabric Layer at Lun Reset time, add an extra > cmd_kref count to prevent premature free. This seems lke something solved by Bart's abort rewrite in a much nicer way. All this special casing based on magic flags isn't maintainable in the long run. -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 1/3] badblocks: Add core badblock management code
Take the core badblocks implementation from md, and make it generally available. This follows the same style as kernel implementations of linked lists, rb-trees etc, where you can have a structure that can be embedded anywhere, and accessor functions to manipulate the data. The only changes in this copy of the code are ones to generalize function/variable names from md-specific ones. Also add init and free functions. Signed-off-by: Vishal Verma--- block/Makefile| 2 +- block/badblocks.c | 576 ++ include/linux/badblocks.h | 53 + 3 files changed, 630 insertions(+), 1 deletion(-) create mode 100644 block/badblocks.c create mode 100644 include/linux/badblocks.h diff --git a/block/Makefile b/block/Makefile index 00ecc97..db5f622 100644 --- a/block/Makefile +++ b/block/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_BLOCK) := bio.o elevator.o blk-core.o blk-tag.o blk-sysfs.o \ blk-iopoll.o blk-lib.o blk-mq.o blk-mq-tag.o \ blk-mq-sysfs.o blk-mq-cpu.o blk-mq-cpumap.o ioctl.o \ genhd.o scsi_ioctl.o partition-generic.o ioprio.o \ - partitions/ + badblocks.o partitions/ obj-$(CONFIG_BOUNCE) += bounce.o obj-$(CONFIG_BLK_DEV_BSG) += bsg.o diff --git a/block/badblocks.c b/block/badblocks.c new file mode 100644 index 000..f0ac279 --- /dev/null +++ b/block/badblocks.c @@ -0,0 +1,576 @@ +/* + * Bad block management + * + * - Heavily based on MD badblocks code from Neil Brown + * + * Copyright (c) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * badblocks_check() - check a given range for bad sectors + * @bb:the badblocks structure that holds all badblock information + * @s: sector (start) at which to check for badblocks + * @sectors: number of sectors to check for badblocks + * @first_bad: pointer to store location of the first badblock + * @bad_sectors: pointer to store number of badblocks after @first_bad + * + * We can record which blocks on each device are 'bad' and so just + * fail those blocks, or that stripe, rather than the whole device. + * Entries in the bad-block table are 64bits wide. This comprises: + * Length of bad-range, in sectors: 0-511 for lengths 1-512 + * Start of bad-range, sector offset, 54 bits (allows 8 exbibytes) + * A 'shift' can be set so that larger blocks are tracked and + * consequently larger devices can be covered. + * 'Acknowledged' flag - 1 bit. - the most significant bit. + * + * Locking of the bad-block table uses a seqlock so badblocks_check + * might need to retry if it is very unlucky. + * We will sometimes want to check for bad blocks in a bi_end_io function, + * so we use the write_seqlock_irq variant. + * + * When looking for a bad block we specify a range and want to + * know if any block in the range is bad. So we binary-search + * to the last range that starts at-or-before the given endpoint, + * (or "before the sector after the target range") + * then see if it ends after the given start. + * + * Return: + * 0: there are no known bad blocks in the range + * 1: there are known bad block which are all acknowledged + * -1: there are bad blocks which have not yet been acknowledged in metadata. + * plus the start/length of the first bad section we overlap. + */ +int badblocks_check(struct badblocks *bb, sector_t s, int sectors, + sector_t *first_bad, int *bad_sectors) +{ + int hi; + int lo; + u64 *p = bb->page; + int rv; + sector_t target = s + sectors; + unsigned seq; + + if (bb->shift > 0) { + /* round the start down, and the end up */ + s >>= bb->shift; + target += (1 >= bb->shift; + sectors = target - s; + } + /* 'target' is now the first block after the bad range */ + +retry: + seq = read_seqbegin(>lock); + lo = 0; + rv = 0; + hi = bb->count; + + /* Binary search between lo and hi for 'target' +* i.e. for the last range that starts before 'target' +*/ + /* INVARIANT: ranges before 'lo' and at-or-after 'hi' +* are known not to be the last range before target. +* VARIANT: hi-lo is the number of possible +* ranges, and decreases until it reaches 1 +*/ +
[PATCH v2 3/3] md: convert to use the generic badblocks code
Retain badblocks as part of rdev, but use the accessor functions from include/linux/badblocks for all manipulation. Signed-off-by: Vishal Verma--- drivers/md/md.c | 516 +++- drivers/md/md.h | 40 + 2 files changed, 28 insertions(+), 528 deletions(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index c702de1..afdc3ea 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -707,8 +708,7 @@ void md_rdev_clear(struct md_rdev *rdev) put_page(rdev->bb_page); rdev->bb_page = NULL; } - kfree(rdev->badblocks.page); - rdev->badblocks.page = NULL; + badblocks_free(>badblocks); } EXPORT_SYMBOL_GPL(md_rdev_clear); @@ -1358,8 +1358,6 @@ static __le32 calc_sb_1_csum(struct mdp_superblock_1 *sb) return cpu_to_le32(csum); } -static int md_set_badblocks(struct badblocks *bb, sector_t s, int sectors, - int acknowledged); static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_version) { struct mdp_superblock_1 *sb; @@ -1484,8 +1482,7 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_ count <<= sb->bblog_shift; if (bb + 1 == 0) break; - if (md_set_badblocks(>badblocks, -sector, count, 1) == 0) + if (badblocks_set(>badblocks, sector, count, 1)) return -EINVAL; } } else if (sb->bblog_offset != 0) @@ -2226,7 +2223,7 @@ repeat: rdev_for_each(rdev, mddev) { if (rdev->badblocks.changed) { rdev->badblocks.changed = 0; - md_ack_all_badblocks(>badblocks); + ack_all_badblocks(>badblocks); md_error(mddev, rdev); } clear_bit(Blocked, >flags); @@ -2352,7 +2349,7 @@ repeat: clear_bit(Blocked, >flags); if (any_badblocks_changed) - md_ack_all_badblocks(>badblocks); + ack_all_badblocks(>badblocks); clear_bit(BlockedBadBlocks, >flags); wake_up(>blocked_wait); } @@ -2944,11 +2941,17 @@ static ssize_t recovery_start_store(struct md_rdev *rdev, const char *buf, size_ static struct rdev_sysfs_entry rdev_recovery_start = __ATTR(recovery_start, S_IRUGO|S_IWUSR, recovery_start_show, recovery_start_store); -static ssize_t -badblocks_show(struct badblocks *bb, char *page, int unack); -static ssize_t -badblocks_store(struct badblocks *bb, const char *page, size_t len, int unack); - +/* sysfs access to bad-blocks list. + * We present two files. + * 'bad-blocks' lists sector numbers and lengths of ranges that + *are recorded as bad. The list is truncated to fit within + *the one-page limit of sysfs. + *Writing "sector length" to this file adds an acknowledged + *bad block list. + * 'unacknowledged-bad-blocks' lists bad blocks that have not yet + *been acknowledged. Writing to this file adds bad blocks + *without acknowledging them. This is largely for testing. + */ static ssize_t bb_show(struct md_rdev *rdev, char *page) { return badblocks_show(>badblocks, page, 0); @@ -3063,14 +3066,7 @@ int md_rdev_init(struct md_rdev *rdev) * This reserves the space even on arrays where it cannot * be used - I wonder if that matters */ - rdev->badblocks.count = 0; - rdev->badblocks.shift = -1; /* disabled until explicitly enabled */ - rdev->badblocks.page = kmalloc(PAGE_SIZE, GFP_KERNEL); - seqlock_init(>badblocks.lock); - if (rdev->badblocks.page == NULL) - return -ENOMEM; - - return 0; + return badblocks_init(>badblocks, 0); } EXPORT_SYMBOL_GPL(md_rdev_init); /* @@ -8348,254 +8344,9 @@ void md_finish_reshape(struct mddev *mddev) } EXPORT_SYMBOL(md_finish_reshape); -/* Bad block management. - * We can record which blocks on each device are 'bad' and so just - * fail those blocks, or that stripe, rather than the whole device. - * Entries in the bad-block table are 64bits wide. This comprises: - * Length of bad-range, in sectors: 0-511 for lengths 1-512 - * Start of bad-range, sector offset, 54 bits (allows 8 exbibytes) - * A 'shift' can be set so that larger blocks are tracked and - * consequently larger devices can be covered. - * 'Acknowledged' flag - 1 bit. - the most significant bit. - * - * Locking of the bad-block table uses a seqlock so md_is_badblock - * might need to retry if
[PATCH v2 0/3] Badblock tracking for gendisks
v3: - Add kernel-doc style comments to all exported functions in badblocks.c (James) - Make return values from badblocks functions consistent with themselves and the kernel style. Change the polarity of badblocks_set, and update all callers accordingly (James) - In gendisk, don't unconditionally allocate badblocks, export the initializer. This also allows the initializer to be a non-void return type, so that the badblocks user can act upon failures better (James) v2: - In badblocks_free, make 'page' NULL (patch 1) - Move the core badblocks code to a new .c file (patch 1) (Jens) - Fix a sizeof usage in disk_alloc_badblocks (patch 2) (Dan) - Since disk_alloc_badblocks can fail, check disk->bb for NULL in the genhd wrappers (patch 2) (Jeff) - Update the md conversion to also ise the badblocks init and free functions (patch 3) - Remove the BB_* macros from md.h as they are now in badblocks.h (patch 3) Patch 1 copies badblock management code into a header of its own, making it generally available. It follows common libraries of code such as linked lists, where anyone may embed a core data structure in another place, and use the provided accessor functions to manipulate the data. Patch 2 adds badblock tracking to gendisks (in preparation for use by NVDIMM devices). Patch 3 converts md over to use the new badblocks 'library'. I have done some pretty simple testing on this - created a raid 1 device, made sure the sysfs entries show up, and can be used to add and view badblocks. A closer look by the md folks would be nice here. Vishal Verma (3): badblocks: Add core badblock management code block: Add badblock management for gendisks md: convert to use the generic badblocks code block/Makefile| 2 +- block/badblocks.c | 576 ++ block/genhd.c | 76 ++ drivers/md/md.c | 516 ++--- drivers/md/md.h | 40 +--- include/linux/badblocks.h | 53 + include/linux/genhd.h | 7 + 7 files changed, 741 insertions(+), 529 deletions(-) create mode 100644 block/badblocks.c create mode 100644 include/linux/badblocks.h -- 2.5.0 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v2 2/3] block: Add badblock management for gendisks
NVDIMM devices, which can behave more like DRAM rather than block devices, may develop bad cache lines, or 'poison'. A block device exposed by the pmem driver can then consume poison via a read (or write), and cause a machine check. On platforms without machine check recovery features, this would mean a crash. The block device maintaining a runtime list of all known sectors that have poison can directly avoid this, and also provide a path forward to enable proper handling/recovery for DAX faults on such a device. Use the new badblock management interfaces to add a badblocks list to gendisks. Signed-off-by: Vishal Verma--- block/genhd.c | 76 +++ include/linux/genhd.h | 7 + 2 files changed, 83 insertions(+) diff --git a/block/genhd.c b/block/genhd.c index 0c706f3..809e3e2 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "blk.h" @@ -505,6 +506,16 @@ static int exact_lock(dev_t devt, void *data) return 0; } +int disk_alloc_badblocks(struct gendisk *disk) +{ + disk->bb = kzalloc(sizeof(*(disk->bb)), GFP_KERNEL); + if (!disk->bb) + return -ENOMEM; + + return badblocks_init(disk->bb, 1); +} +EXPORT_SYMBOL(disk_alloc_badblocks); + static void register_disk(struct gendisk *disk) { struct device *ddev = disk_to_dev(disk); @@ -657,6 +668,11 @@ void del_gendisk(struct gendisk *disk) blk_unregister_queue(disk); blk_unregister_region(disk_devt(disk), disk->minors); + if (disk->bb) { + badblocks_free(disk->bb); + kfree(disk->bb); + } + part_stat_set_all(>part0, 0); disk->part0.stamp = 0; @@ -670,6 +686,63 @@ void del_gendisk(struct gendisk *disk) } EXPORT_SYMBOL(del_gendisk); +/* + * The gendisk usage of badblocks does not track acknowledgements for + * badblocks. We always assume they are acknowledged. + */ +int disk_check_badblocks(struct gendisk *disk, sector_t s, int sectors, + sector_t *first_bad, int *bad_sectors) +{ + if (!disk->bb) + return 0; + + return badblocks_check(disk->bb, s, sectors, first_bad, bad_sectors); +} +EXPORT_SYMBOL(disk_check_badblocks); + +int disk_set_badblocks(struct gendisk *disk, sector_t s, int sectors) +{ + if (!disk->bb) + return 0; + + return badblocks_set(disk->bb, s, sectors, 1); +} +EXPORT_SYMBOL(disk_set_badblocks); + +int disk_clear_badblocks(struct gendisk *disk, sector_t s, int sectors) +{ + if (!disk->bb) + return 0; + + return badblocks_clear(disk->bb, s, sectors); +} +EXPORT_SYMBOL(disk_clear_badblocks); + +/* sysfs access to bad-blocks list. */ +static ssize_t disk_badblocks_show(struct device *dev, + struct device_attribute *attr, + char *page) +{ + struct gendisk *disk = dev_to_disk(dev); + + if (!disk->bb) + return 0; + + return badblocks_show(disk->bb, page, 0); +} + +static ssize_t disk_badblocks_store(struct device *dev, + struct device_attribute *attr, + const char *page, size_t len) +{ + struct gendisk *disk = dev_to_disk(dev); + + if (!disk->bb) + return 0; + + return badblocks_store(disk->bb, page, len, 0); +} + /** * get_gendisk - get partitioning information for a given device * @devt: device to get partitioning information for @@ -988,6 +1061,8 @@ static DEVICE_ATTR(discard_alignment, S_IRUGO, disk_discard_alignment_show, static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL); static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL); static DEVICE_ATTR(inflight, S_IRUGO, part_inflight_show, NULL); +static DEVICE_ATTR(badblocks, S_IRUGO | S_IWUSR, disk_badblocks_show, + disk_badblocks_store); #ifdef CONFIG_FAIL_MAKE_REQUEST static struct device_attribute dev_attr_fail = __ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store); @@ -1009,6 +1084,7 @@ static struct attribute *disk_attrs[] = { _attr_capability.attr, _attr_stat.attr, _attr_inflight.attr, + _attr_badblocks.attr, #ifdef CONFIG_FAIL_MAKE_REQUEST _attr_fail.attr, #endif diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 2adbfa6..985eb94 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -162,6 +162,7 @@ struct disk_part_tbl { }; struct disk_events; +struct badblocks; struct gendisk { /* major, first_minor and minors are input parameters only, @@ -201,6 +202,7 @@ struct gendisk { struct blk_integrity *integrity; #endif int node_id; + struct badblocks *bb; }; static inline struct gendisk *part_to_disk(struct hd_struct *part) @@ -421,6
Re: [PATCH v2 0/3] Badblock tracking for gendisks
Oops, sorry, should've been PATCH v3.. The contents are right, just the subject line is off. -Vishal On Mon, 2015-12-07 at 19:52 -0700, Vishal Verma wrote: > v3: >  - Add kernel-doc style comments to all exported functions in > badblocks.c (James) >  - Make return values from badblocks functions consistent with > themselves > and the kernel style. Change the polarity of badblocks_set, and > update > all callers accordingly (James) >  - In gendisk, don't unconditionally allocate badblocks, export the > initializer. > This also allows the initializer to be a non-void return type, so > that the > badblocks user can act upon failures better (James) > > > v2: >  - In badblocks_free, make 'page' NULL (patch 1) >  - Move the core badblocks code to a new .c file (patch 1) (Jens) >  - Fix a sizeof usage in disk_alloc_badblocks (patch 2) (Dan) >  - Since disk_alloc_badblocks can fail, check disk->bb for NULL in > the > genhd wrappers (patch 2) (Jeff) >  - Update the md conversion to also ise the badblocks init and free > functions (patch 3) >  - Remove the BB_* macros from md.h as they are now in badblocks.h > (patch 3) > > Patch 1 copies badblock management code into a header of its own, > making it generally available. It follows common libraries of code > such as linked lists, where anyone may embed a core data structure > in another place, and use the provided accessor functions to > manipulate the data. > > Patch 2 adds badblock tracking to gendisks (in preparation for use > by NVDIMM devices). > > Patch 3 converts md over to use the new badblocks 'library'. I have > done some pretty simple testing on this - created a raid 1 device, > made sure the sysfs entries show up, and can be used to add and view > badblocks. A closer look by the md folks would be nice here. > > Vishal Verma (3): >  badblocks: Add core badblock management code >  block: Add badblock management for gendisks >  md: convert to use the generic badblocks code > >  block/Makefile|   2 +- >  block/badblocks.c | 576 > ++ >  block/genhd.c |  76 ++ >  drivers/md/md.c   | 516 ++--- > >  drivers/md/md.h   |  40 +--- >  include/linux/badblocks.h |  53 + >  include/linux/genhd.h |   7 + >  7 files changed, 741 insertions(+), 529 deletions(-) >  create mode 100644 block/badblocks.c >  create mode 100644 include/linux/badblocks.h > N�r��yb�X��ǧv�^�)޺{.n�+{���"�{ay�ʇڙ�,j��f���h���z��w��� > ���j:+v���w�j�mzZ+�ݢj"��!�i
[PATCH 10/13] IB/srp: use the new CQ API
This also moves recv completion handling from hardirq context into softirq context. Signed-off-by: Christoph Hellwig--- drivers/infiniband/ulp/srp/ib_srp.c | 173 +--- drivers/infiniband/ulp/srp/ib_srp.h | 7 +- 2 files changed, 86 insertions(+), 94 deletions(-) diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 784dd97..d61489e 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -132,8 +132,9 @@ MODULE_PARM_DESC(ch_count, static void srp_add_one(struct ib_device *device); static void srp_remove_one(struct ib_device *device, void *client_data); -static void srp_recv_completion(struct ib_cq *cq, void *ch_ptr); -static void srp_send_completion(struct ib_cq *cq, void *ch_ptr); +static void srp_recv_done(struct ib_cq *cq, struct ib_wc *wc); +static void srp_handle_qp_err(struct ib_cq *cq, struct ib_wc *wc, + const char *opname); static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event); static struct scsi_transport_template *ib_srp_transport_template; @@ -445,6 +446,17 @@ static struct srp_fr_pool *srp_alloc_fr_pool(struct srp_target_port *target) dev->max_pages_per_mr); } +static void srp_drain_done(struct ib_cq *cq, struct ib_wc *wc) +{ + struct srp_rdma_ch *ch = cq->cq_context; + + complete(>done); +} + +static struct ib_cqe srp_drain_cqe = { + .done = srp_drain_done, +}; + /** * srp_destroy_qp() - destroy an RDMA queue pair * @ch: SRP RDMA channel. @@ -461,7 +473,7 @@ static void srp_destroy_qp(struct srp_rdma_ch *ch) struct ib_recv_wr *bad_wr; int ret; - wr.wr_id = SRP_LAST_WR_ID; + wr.wr_cqe = _drain_cqe; /* Destroying a QP and reusing ch->done is only safe if not connected */ WARN_ON_ONCE(ch->connected); @@ -490,34 +502,27 @@ static int srp_create_ch_ib(struct srp_rdma_ch *ch) struct ib_fmr_pool *fmr_pool = NULL; struct srp_fr_pool *fr_pool = NULL; const int m = 1 + dev->use_fast_reg; - struct ib_cq_init_attr cq_attr = {}; int ret; init_attr = kzalloc(sizeof *init_attr, GFP_KERNEL); if (!init_attr) return -ENOMEM; - /* + 1 for SRP_LAST_WR_ID */ - cq_attr.cqe = target->queue_size + 1; - cq_attr.comp_vector = ch->comp_vector; - recv_cq = ib_create_cq(dev->dev, srp_recv_completion, NULL, ch, - _attr); + /* queue_size + 1 for ib_drain_qp */ + recv_cq = ib_alloc_cq(dev->dev, ch, target->queue_size + 1, + ch->comp_vector, IB_POLL_SOFTIRQ); if (IS_ERR(recv_cq)) { ret = PTR_ERR(recv_cq); goto err; } - cq_attr.cqe = m * target->queue_size; - cq_attr.comp_vector = ch->comp_vector; - send_cq = ib_create_cq(dev->dev, srp_send_completion, NULL, ch, - _attr); + send_cq = ib_alloc_cq(dev->dev, ch, m * target->queue_size, + ch->comp_vector, IB_POLL_DIRECT); if (IS_ERR(send_cq)) { ret = PTR_ERR(send_cq); goto err_recv_cq; } - ib_req_notify_cq(recv_cq, IB_CQ_NEXT_COMP); - init_attr->event_handler = srp_qp_event; init_attr->cap.max_send_wr = m * target->queue_size; init_attr->cap.max_recv_wr = target->queue_size + 1; @@ -559,9 +564,9 @@ static int srp_create_ch_ib(struct srp_rdma_ch *ch) if (ch->qp) srp_destroy_qp(ch); if (ch->recv_cq) - ib_destroy_cq(ch->recv_cq); + ib_free_cq(ch->recv_cq); if (ch->send_cq) - ib_destroy_cq(ch->send_cq); + ib_free_cq(ch->send_cq); ch->qp = qp; ch->recv_cq = recv_cq; @@ -581,13 +586,13 @@ static int srp_create_ch_ib(struct srp_rdma_ch *ch) return 0; err_qp: - ib_destroy_qp(qp); + srp_destroy_qp(ch); err_send_cq: - ib_destroy_cq(send_cq); + ib_free_cq(send_cq); err_recv_cq: - ib_destroy_cq(recv_cq); + ib_free_cq(recv_cq); err: kfree(init_attr); @@ -623,9 +628,10 @@ static void srp_free_ch_ib(struct srp_target_port *target, if (ch->fmr_pool) ib_destroy_fmr_pool(ch->fmr_pool); } + srp_destroy_qp(ch); - ib_destroy_cq(ch->send_cq); - ib_destroy_cq(ch->recv_cq); + ib_free_cq(ch->send_cq); + ib_free_cq(ch->recv_cq); /* * Avoid that the SCSI error handler tries to use this channel after @@ -1038,7 +1044,13 @@ static int srp_connect_ch(struct srp_rdma_ch *ch, bool multich) } } -static int srp_inv_rkey(struct srp_rdma_ch *ch, u32 rkey) +static void srp_inv_rkey_err_done(struct ib_cq *cq, struct ib_wc *wc) +{ +
[PATCH 09/13] IB/srpt: use the new CQ API
Signed-off-by: Christoph Hellwig--- drivers/infiniband/ulp/srpt/ib_srpt.c | 327 +- drivers/infiniband/ulp/srpt/ib_srpt.h | 28 +-- 2 files changed, 88 insertions(+), 267 deletions(-) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index b5544b2..98282e9 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -93,6 +93,8 @@ MODULE_PARM_DESC(srpt_service_guid, static struct ib_client srpt_client; static void srpt_release_channel(struct srpt_rdma_ch *ch); static int srpt_queue_status(struct se_cmd *cmd); +static void srpt_recv_done(struct ib_cq *cq, struct ib_wc *wc); +static void srpt_send_done(struct ib_cq *cq, struct ib_wc *wc); /** * opposite_dma_dir() - Swap DMA_TO_DEVICE and DMA_FROM_DEVICE. @@ -778,12 +780,12 @@ static int srpt_post_recv(struct srpt_device *sdev, struct ib_recv_wr wr, *bad_wr; BUG_ON(!sdev); - wr.wr_id = encode_wr_id(SRPT_RECV, ioctx->ioctx.index); - list.addr = ioctx->ioctx.dma; list.length = srp_max_req_size; list.lkey = sdev->pd->local_dma_lkey; + ioctx->ioctx.cqe.done = srpt_recv_done; + wr.wr_cqe = >ioctx.cqe; wr.next = NULL; wr.sg_list = wr.num_sge = 1; @@ -819,8 +821,9 @@ static int srpt_post_send(struct srpt_rdma_ch *ch, list.length = len; list.lkey = sdev->pd->local_dma_lkey; + ioctx->ioctx.cqe.done = srpt_send_done; wr.next = NULL; - wr.wr_id = encode_wr_id(SRPT_SEND, ioctx->ioctx.index); + wr.wr_cqe = >ioctx.cqe; wr.sg_list = wr.num_sge = 1; wr.opcode = IB_WR_SEND; @@ -1383,116 +1386,44 @@ out: } /** - * srpt_handle_send_err_comp() - Process an IB_WC_SEND error completion. - */ -static void srpt_handle_send_err_comp(struct srpt_rdma_ch *ch, u64 wr_id) -{ - struct srpt_send_ioctx *ioctx; - enum srpt_command_state state; - u32 index; - - atomic_inc(>sq_wr_avail); - - index = idx_from_wr_id(wr_id); - ioctx = ch->ioctx_ring[index]; - state = srpt_get_cmd_state(ioctx); - - WARN_ON(state != SRPT_STATE_CMD_RSP_SENT - && state != SRPT_STATE_MGMT_RSP_SENT - && state != SRPT_STATE_NEED_DATA - && state != SRPT_STATE_DONE); - - /* If SRP_RSP sending failed, undo the ch->req_lim change. */ - if (state == SRPT_STATE_CMD_RSP_SENT - || state == SRPT_STATE_MGMT_RSP_SENT) - atomic_dec(>req_lim); - - srpt_abort_cmd(ioctx); -} - -/** - * srpt_handle_send_comp() - Process an IB send completion notification. - */ -static void srpt_handle_send_comp(struct srpt_rdma_ch *ch, - struct srpt_send_ioctx *ioctx) -{ - enum srpt_command_state state; - - atomic_inc(>sq_wr_avail); - - state = srpt_set_cmd_state(ioctx, SRPT_STATE_DONE); - - if (WARN_ON(state != SRPT_STATE_CMD_RSP_SENT - && state != SRPT_STATE_MGMT_RSP_SENT - && state != SRPT_STATE_DONE)) - pr_debug("state = %d\n", state); - - if (state != SRPT_STATE_DONE) { - srpt_unmap_sg_to_ib_sge(ch, ioctx); - transport_generic_free_cmd(>cmd, 0); - } else { - pr_err("IB completion has been received too late for" - " wr_id = %u.\n", ioctx->ioctx.index); - } -} - -/** - * srpt_handle_rdma_comp() - Process an IB RDMA completion notification. - * * XXX: what is now target_execute_cmd used to be asynchronous, and unmapping * the data that has been transferred via IB RDMA had to be postponed until the * check_stop_free() callback. None of this is necessary anymore and needs to * be cleaned up. */ -static void srpt_handle_rdma_comp(struct srpt_rdma_ch *ch, - struct srpt_send_ioctx *ioctx, - enum srpt_opcode opcode) +static void srpt_rdma_read_done(struct ib_cq *cq, struct ib_wc *wc) { + struct srpt_rdma_ch *ch = cq->cq_context; + struct srpt_send_ioctx *ioctx = + container_of(wc->wr_cqe, struct srpt_send_ioctx, ioctx.cqe); + WARN_ON(ioctx->n_rdma <= 0); atomic_add(ioctx->n_rdma, >sq_wr_avail); - if (opcode == SRPT_RDMA_READ_LAST) { - if (srpt_test_and_set_cmd_state(ioctx, SRPT_STATE_NEED_DATA, - SRPT_STATE_DATA_IN)) - target_execute_cmd(>cmd); - else - pr_err("%s[%d]: wrong state = %d\n", __func__, - __LINE__, srpt_get_cmd_state(ioctx)); - } else { - WARN(true, "unexpected opcode %d\n", opcode); + if (unlikely(wc->status != IB_WC_SUCCESS)) { + pr_info("RDMA_READ for ioctx 0x%p failed with status %d\n", + ioctx,
[PATCH 08/13] IB/srpt: chain RDMA READ/WRITE requests
Remove struct rdma_iu and instead allocate the struct ib_rdma_wr array early and fill out directly. This allows us to chain the WRs, and thus archive both less lock contention on the HCA workqueue as well as much simpler error handling. Signed-off-by: Christoph Hellwig--- drivers/infiniband/ulp/srpt/ib_srpt.c | 100 +- drivers/infiniband/ulp/srpt/ib_srpt.h | 14 + 2 files changed, 39 insertions(+), 75 deletions(-) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index b8b654c..b5544b2 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -1055,7 +1055,7 @@ static void srpt_unmap_sg_to_ib_sge(struct srpt_rdma_ch *ch, BUG_ON(ioctx->n_rdma && !ioctx->rdma_ius); while (ioctx->n_rdma) - kfree(ioctx->rdma_ius[--ioctx->n_rdma].sge); + kfree(ioctx->rdma_ius[--ioctx->n_rdma].wr.sg_list); kfree(ioctx->rdma_ius); ioctx->rdma_ius = NULL; @@ -1082,7 +1082,7 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, struct scatterlist *sg, *sg_orig; int sg_cnt; enum dma_data_direction dir; - struct rdma_iu *riu; + struct ib_rdma_wr *riu; struct srp_direct_buf *db; dma_addr_t dma_addr; struct ib_sge *sge; @@ -1115,7 +1115,8 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, nrdma = (count + SRPT_DEF_SG_PER_WQE - 1) / SRPT_DEF_SG_PER_WQE + ioctx->n_rbuf; - ioctx->rdma_ius = kzalloc(nrdma * sizeof *riu, GFP_KERNEL); + ioctx->rdma_ius = kcalloc(nrdma, sizeof(*ioctx->rdma_ius), + GFP_KERNEL); if (!ioctx->rdma_ius) goto free_mem; @@ -1139,9 +1140,9 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, j < count && i < ioctx->n_rbuf && tsize > 0; ++i, ++riu, ++db) { rsize = be32_to_cpu(db->len); raddr = be64_to_cpu(db->va); - riu->raddr = raddr; + riu->remote_addr = raddr; riu->rkey = be32_to_cpu(db->key); - riu->sge_cnt = 0; + riu->wr.num_sge = 0; /* calculate how many sge required for this remote_buf */ while (rsize > 0 && tsize > 0) { @@ -1165,27 +1166,29 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, rsize = 0; } - ++riu->sge_cnt; + ++riu->wr.num_sge; - if (rsize > 0 && riu->sge_cnt == SRPT_DEF_SG_PER_WQE) { + if (rsize > 0 && + riu->wr.num_sge == SRPT_DEF_SG_PER_WQE) { ++ioctx->n_rdma; - riu->sge = - kmalloc(riu->sge_cnt * sizeof *riu->sge, - GFP_KERNEL); - if (!riu->sge) + riu->wr.sg_list = kmalloc_array(riu->wr.num_sge, + sizeof(*riu->wr.sg_list), + GFP_KERNEL); + if (!riu->wr.sg_list) goto free_mem; ++riu; - riu->sge_cnt = 0; - riu->raddr = raddr; + riu->wr.num_sge = 0; + riu->remote_addr = raddr; riu->rkey = be32_to_cpu(db->key); } } ++ioctx->n_rdma; - riu->sge = kmalloc(riu->sge_cnt * sizeof *riu->sge, - GFP_KERNEL); - if (!riu->sge) + riu->wr.sg_list = kmalloc_array(riu->wr.num_sge, + sizeof(*riu->wr.sg_list), + GFP_KERNEL); + if (!riu->wr.sg_list) goto free_mem; } @@ -1200,7 +1203,7 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, for (i = 0, j = 0; j < count && i < ioctx->n_rbuf && tsize > 0; ++i, ++riu, ++db) { rsize = be32_to_cpu(db->len); - sge = riu->sge; + sge = riu->wr.sg_list; k = 0; while (rsize > 0 && tsize > 0) { @@ -1232,9 +1235,9 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, } ++k; - if (k == riu->sge_cnt && rsize > 0 && tsize > 0) { + if (k == riu->wr.num_sge && rsize > 0 && tsize > 0) { ++riu; -
[PATCH 11/13] IB/iser: Use a dedicated descriptor for login
From: Sagi GrimbergWe'll need it later with the new CQ abstraction. also switch login bufs to void pointers. Signed-off-by: Sagi Grimberg Signed-off-by: Christoph Hellwig --- drivers/infiniband/ulp/iser/iscsi_iser.h | 30 +-- drivers/infiniband/ulp/iser/iser_initiator.c | 128 +-- drivers/infiniband/ulp/iser/iser_verbs.c | 14 +-- 3 files changed, 89 insertions(+), 83 deletions(-) diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h index 502063b..5648409 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.h +++ b/drivers/infiniband/ulp/iser/iscsi_iser.h @@ -326,6 +326,25 @@ struct iser_rx_desc { char pad[ISER_RX_PAD_SIZE]; } __attribute__((packed)); + +/** + * struct iser_login_desc - iSER login descriptor + * + * @req: pointer to login request buffer + * @resp: pointer to login response buffer + * @req_dma: DMA address of login request buffer + * @rsp_dma: DMA address of login response buffer + * @sge: IB sge for login post recv + */ +struct iser_login_desc { + void *req; + void *rsp; + u64 req_dma; + u64 rsp_dma; + struct ib_sgesge; +} __attribute__((packed)); + + struct iser_conn; struct ib_conn; struct iscsi_iser_task; @@ -512,11 +531,7 @@ struct ib_conn { * @up_completion:connection establishment completed *(state is ISER_CONN_UP) * @conn_list:entry in ig conn list - * @login_buf:login data buffer (stores login parameters) - * @login_req_buf:login request buffer - * @login_req_dma:login request buffer dma address - * @login_resp_buf: login response buffer - * @login_resp_dma: login response buffer dma address + * @login_desc: login descriptor * @rx_desc_head: head of rx_descs cyclic buffer * @rx_descs: rx buffers array (cyclic buffer) * @num_rx_descs: number of rx descriptors @@ -539,10 +554,7 @@ struct iser_conn { struct completionib_completion; struct completionup_completion; struct list_head conn_list; - - char *login_buf; - char *login_req_buf, *login_resp_buf; - u64 login_req_dma, login_resp_dma; + struct iser_login_desc login_desc; unsigned int rx_desc_head; struct iser_rx_desc *rx_descs; u32 num_rx_descs; diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c index ffd00c4..21f28c8 100644 --- a/drivers/infiniband/ulp/iser/iser_initiator.c +++ b/drivers/infiniband/ulp/iser/iser_initiator.c @@ -174,73 +174,63 @@ static void iser_create_send_desc(struct iser_conn *iser_conn, static void iser_free_login_buf(struct iser_conn *iser_conn) { struct iser_device *device = iser_conn->ib_conn.device; + struct iser_login_desc *desc = _conn->login_desc; - if (!iser_conn->login_buf) + if (!desc->req) return; - if (iser_conn->login_req_dma) - ib_dma_unmap_single(device->ib_device, - iser_conn->login_req_dma, - ISCSI_DEF_MAX_RECV_SEG_LEN, DMA_TO_DEVICE); + ib_dma_unmap_single(device->ib_device, desc->req_dma, + ISCSI_DEF_MAX_RECV_SEG_LEN, DMA_TO_DEVICE); - if (iser_conn->login_resp_dma) - ib_dma_unmap_single(device->ib_device, - iser_conn->login_resp_dma, - ISER_RX_LOGIN_SIZE, DMA_FROM_DEVICE); + ib_dma_unmap_single(device->ib_device, desc->rsp_dma, + ISER_RX_LOGIN_SIZE, DMA_FROM_DEVICE); - kfree(iser_conn->login_buf); + kfree(desc->req); + kfree(desc->rsp); /* make sure we never redo any unmapping */ - iser_conn->login_req_dma = 0; - iser_conn->login_resp_dma = 0; - iser_conn->login_buf = NULL; + desc->req = NULL; + desc->rsp = NULL; } static int iser_alloc_login_buf(struct iser_conn *iser_conn) { struct iser_device *device = iser_conn->ib_conn.device; - int req_err, resp_err; - - BUG_ON(device == NULL); - - iser_conn->login_buf = kmalloc(ISCSI_DEF_MAX_RECV_SEG_LEN + -ISER_RX_LOGIN_SIZE, GFP_KERNEL); - if (!iser_conn->login_buf) - goto out_err; - - iser_conn->login_req_buf = iser_conn->login_buf; - iser_conn->login_resp_buf = iser_conn->login_buf + -
[PATCH 13/13] IB/iser: Convert to CQ abstraction
From: Sagi GrimbergUse the new CQ abstraction to simplify completions in the iSER initiator. Signed-off-by: Sagi Grimberg Signed-off-by: Christoph Hellwig --- drivers/infiniband/ulp/iser/iscsi_iser.h | 72 +--- drivers/infiniband/ulp/iser/iser_initiator.c | 142 ++- drivers/infiniband/ulp/iser/iser_memory.c| 21 ++- drivers/infiniband/ulp/iser/iser_verbs.c | 258 ++- 4 files changed, 210 insertions(+), 283 deletions(-) diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h index cf4c4ce..d3f5255 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.h +++ b/drivers/infiniband/ulp/iser/iscsi_iser.h @@ -151,16 +151,12 @@ - ISER_MAX_RX_MISC_PDUS) / \ (1 + ISER_INFLIGHT_DATAOUTS)) -#define ISER_WC_BATCH_COUNT 16 #define ISER_SIGNAL_CMD_COUNT 32 #define ISER_VER 0x10 #define ISER_WSV 0x08 #define ISER_RSV 0x04 -#define ISER_FASTREG_LI_WRID 0xULL -#define ISER_BEACON_WRID 0xfffeULL - /** * struct iser_hdr - iSER header * @@ -269,7 +265,7 @@ enum iser_desc_type { #define ISER_MAX_WRS 7 /** - * struct iser_tx_desc - iSER TX descriptor (for send wr_id) + * struct iser_tx_desc - iSER TX descriptor * * @iser_header: iser header * @iscsi_header: iscsi header @@ -293,6 +289,7 @@ struct iser_tx_desc { u64 dma_addr; struct ib_sgetx_sg[2]; int num_sge; + struct ib_cqecqe; bool mapped; u8 wr_idx; union iser_wr { @@ -306,9 +303,10 @@ struct iser_tx_desc { }; #define ISER_RX_PAD_SIZE (256 - (ISER_RX_PAYLOAD_SIZE + \ - sizeof(u64) + sizeof(struct ib_sge))) +sizeof(u64) + sizeof(struct ib_sge) + \ +sizeof(struct ib_cqe))) /** - * struct iser_rx_desc - iSER RX descriptor (for recv wr_id) + * struct iser_rx_desc - iSER RX descriptor * * @iser_header: iser header * @iscsi_header: iscsi header @@ -323,9 +321,9 @@ struct iser_rx_desc { char data[ISER_RECV_DATA_SEG_LEN]; u64 dma_addr; struct ib_sgerx_sg; + struct ib_cqecqe; char pad[ISER_RX_PAD_SIZE]; -} __attribute__((packed)); - +} __packed; /** * struct iser_login_desc - iSER login descriptor @@ -335,6 +333,7 @@ struct iser_rx_desc { * @req_dma: DMA address of login request buffer * @rsp_dma: DMA address of login response buffer * @sge: IB sge for login post recv + * @cqe: completion handler */ struct iser_login_desc { void *req; @@ -342,9 +341,9 @@ struct iser_login_desc { u64 req_dma; u64 rsp_dma; struct ib_sgesge; + struct ib_cqecqe; } __attribute__((packed)); - struct iser_conn; struct ib_conn; struct iscsi_iser_task; @@ -352,18 +351,12 @@ struct iscsi_iser_task; /** * struct iser_comp - iSER completion context * - * @device: pointer to device handle * @cq: completion queue - * @wcs:work completion array - * @tasklet:Tasklet handle * @active_qps: Number of active QPs attached * to completion context */ struct iser_comp { - struct iser_device *device; struct ib_cq*cq; - struct ib_wc wcs[ISER_WC_BATCH_COUNT]; - struct tasklet_structtasklet; int active_qps; }; @@ -492,10 +485,11 @@ struct iser_fr_pool { * @rx_wr: receive work request for batch posts * @device: reference to iser device * @comp:iser completion context - * @pi_support: Indicate device T10-PI support - * @beacon: beacon send wr to signal all flush errors were drained - * @flush_comp: completes when all connection completions consumed * @fr_pool: connection fast registration poool + * @pi_support: Indicate device T10-PI support + * @last:last send wr to signal all flush errors were drained + * @last_cqe:cqe handler for last wr + * @last_comp: completes when all connection completions consumed */ struct ib_conn { struct rdma_cm_id *cma_id; @@ -505,10 +499,12 @@ struct ib_conn { struct ib_recv_wrrx_wr[ISER_MIN_POSTED_RX]; struct iser_device *device; struct
[PATCH 12/13] IB/iser: Use helper for container_of
From: Sagi GrimbergNicer this way. Signed-off-by: Sagi Grimberg Signed-off-by: Christoph Hellwig --- drivers/infiniband/ulp/iser/iscsi_iser.h | 6 ++ drivers/infiniband/ulp/iser/iser_initiator.c | 3 +-- drivers/infiniband/ulp/iser/iser_verbs.c | 6 ++ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h index 5648409..cf4c4ce 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.h +++ b/drivers/infiniband/ulp/iser/iscsi_iser.h @@ -729,4 +729,10 @@ iser_tx_next_wr(struct iser_tx_desc *tx_desc) return cur_wr; } +static inline struct iser_conn * +to_iser_conn(struct ib_conn *ib_conn) +{ + return container_of(ib_conn, struct iser_conn, ib_conn); +} + #endif diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c index 21f28c8..21148b6 100644 --- a/drivers/infiniband/ulp/iser/iser_initiator.c +++ b/drivers/infiniband/ulp/iser/iser_initiator.c @@ -559,8 +559,7 @@ void iser_rcv_completion(struct iser_rx_desc *rx_desc, unsigned long rx_xfer_len, struct ib_conn *ib_conn) { - struct iser_conn *iser_conn = container_of(ib_conn, struct iser_conn, - ib_conn); + struct iser_conn *iser_conn = to_iser_conn(ib_conn); struct iscsi_hdr *hdr; char *data; u64 rx_dma; diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index ee4cebc..f75ef0c 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -455,8 +455,7 @@ void iser_free_fastreg_pool(struct ib_conn *ib_conn) */ static int iser_create_ib_conn_res(struct ib_conn *ib_conn) { - struct iser_conn *iser_conn = container_of(ib_conn, struct iser_conn, - ib_conn); + struct iser_conn *iser_conn = to_iser_conn(ib_conn); struct iser_device *device; struct ib_device*ib_dev; struct ib_qp_init_attr init_attr; @@ -1160,9 +1159,8 @@ static void iser_handle_comp_error(struct ib_conn *ib_conn, struct ib_wc *wc) { + struct iser_conn *iser_conn = to_iser_conn(ib_conn); void *wr_id = (void *)(uintptr_t)wc->wr_id; - struct iser_conn *iser_conn = container_of(ib_conn, struct iser_conn, - ib_conn); if (wc->status != IB_WC_WR_FLUSH_ERR) if (iser_conn->iscsi_conn) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 05/13] irq_poll: mark __irq_poll_complete static
Signed-off-by: Christoph Hellwig--- include/linux/irq_poll.h | 1 - lib/irq_poll.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/include/linux/irq_poll.h b/include/linux/irq_poll.h index c3145c7..ce9e1db 100644 --- a/include/linux/irq_poll.h +++ b/include/linux/irq_poll.h @@ -21,7 +21,6 @@ enum { extern void irq_poll_sched(struct irq_poll *); extern void irq_poll_init(struct irq_poll *, int, irq_poll_fn *); extern void irq_poll_complete(struct irq_poll *); -extern void __irq_poll_complete(struct irq_poll *); extern void irq_poll_enable(struct irq_poll *); extern void irq_poll_disable(struct irq_poll *); diff --git a/lib/irq_poll.c b/lib/irq_poll.c index 2c7b8e6..652ef4d 100644 --- a/lib/irq_poll.c +++ b/lib/irq_poll.c @@ -47,13 +47,12 @@ EXPORT_SYMBOL(irq_poll_sched); * See irq_poll_complete(). This function must be called with interrupts * disabled. **/ -void __irq_poll_complete(struct irq_poll *iop) +static void __irq_poll_complete(struct irq_poll *iop) { list_del(>list); smp_mb__before_atomic(); clear_bit_unlock(IRQ_POLL_F_SCHED, >state); } -EXPORT_SYMBOL(__irq_poll_complete); /** * irq_poll_complete - Mark this @iop as un-polled again -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 06/13] irq_poll: remove unused data and max fields
Signed-off-by: Christoph Hellwig--- include/linux/irq_poll.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/irq_poll.h b/include/linux/irq_poll.h index ce9e1db..7527c03 100644 --- a/include/linux/irq_poll.h +++ b/include/linux/irq_poll.h @@ -7,9 +7,7 @@ typedef int (irq_poll_fn)(struct irq_poll *, int); struct irq_poll { struct list_head list; unsigned long state; - unsigned long data; int weight; - int max; irq_poll_fn *poll; }; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 02/13] irq_poll: don't disable new irq_poll instances
There is no good reason to start out disabled - drivers can control if the poll instance can be scheduled by simply not scheduling it yet. Signed-off-by: Christoph Hellwig--- drivers/scsi/be2iscsi/be_main.c | 2 -- drivers/scsi/ipr.c | 2 -- lib/irq_poll.c | 4 +--- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 1d879ef..471e2b9 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -5581,7 +5581,6 @@ static void beiscsi_eeh_resume(struct pci_dev *pdev) pbe_eq = _context->be_eq[i]; irq_poll_init(_eq->iopoll, be_iopoll_budget, be_iopoll); - irq_poll_enable(_eq->iopoll); } i = (phba->msix_enabled) ? i : 0; @@ -5754,7 +5753,6 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, pbe_eq = _context->be_eq[i]; irq_poll_init(_eq->iopoll, be_iopoll_budget, be_iopoll); - irq_poll_enable(_eq->iopoll); } i = (phba->msix_enabled) ? i : 0; diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 6b9c738..402e4ca 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3708,7 +3708,6 @@ static ssize_t ipr_store_iopoll_weight(struct device *dev, for (i = 1; i < ioa_cfg->hrrq_num; i++) { irq_poll_init(_cfg->hrrq[i].iopoll, ioa_cfg->iopoll_weight, ipr_iopoll); - irq_poll_enable(_cfg->hrrq[i].iopoll); } } spin_unlock_irqrestore(shost->host_lock, lock_flags); @@ -10407,7 +10406,6 @@ static int ipr_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id) for (i = 1; i < ioa_cfg->hrrq_num; i++) { irq_poll_init(_cfg->hrrq[i].iopoll, ioa_cfg->iopoll_weight, ipr_iopoll); - irq_poll_enable(_cfg->hrrq[i].iopoll); } } diff --git a/lib/irq_poll.c b/lib/irq_poll.c index e6fd1dc..88af879 100644 --- a/lib/irq_poll.c +++ b/lib/irq_poll.c @@ -170,8 +170,7 @@ EXPORT_SYMBOL(irq_poll_enable); * @poll_fn: The handler to invoke * * Description: - * Initialize this irq_poll structure. Before being actively used, the - * driver must call irq_poll_enable(). + * Initialize and enable this irq_poll structure. **/ void irq_poll_init(struct irq_poll *iop, int weight, irq_poll_fn *poll_fn) { @@ -179,7 +178,6 @@ void irq_poll_init(struct irq_poll *iop, int weight, irq_poll_fn *poll_fn) INIT_LIST_HEAD(>list); iop->weight = weight; iop->poll = poll_fn; - set_bit(IRQ_POLL_F_SCHED, >state); } EXPORT_SYMBOL(irq_poll_init); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 03/13] irq_poll: fold irq_poll_sched_prep into irq_poll_sched
There is no good reason to keep them apart, and this makes using the API a bit simpler. Signed-off-by: Christoph Hellwig--- drivers/scsi/be2iscsi/be_main.c | 6 ++ drivers/scsi/ipr.c | 3 +-- include/linux/irq_poll.h| 13 - lib/irq_poll.c | 10 +++--- 4 files changed, 10 insertions(+), 22 deletions(-) diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 471e2b9..cb9072a8 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -910,8 +910,7 @@ static irqreturn_t be_isr_msix(int irq, void *dev_id) num_eq_processed = 0; while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] & EQE_VALID_MASK) { - if (!irq_poll_sched_prep(_eq->iopoll)) - irq_poll_sched(_eq->iopoll); + irq_poll_sched(_eq->iopoll); AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); queue_tail_inc(eq); @@ -972,8 +971,7 @@ static irqreturn_t be_isr(int irq, void *dev_id) spin_unlock_irqrestore(>isr_lock, flags); num_mcceq_processed++; } else { - if (!irq_poll_sched_prep(_eq->iopoll)) - irq_poll_sched(_eq->iopoll); + irq_poll_sched(_eq->iopoll); num_ioeq_processed++; } AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0); diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 402e4ca..82031e0 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -5692,8 +5692,7 @@ static irqreturn_t ipr_isr_mhrrq(int irq, void *devp) if (ioa_cfg->iopoll_weight && ioa_cfg->sis64 && ioa_cfg->nvectors > 1) { if ((be32_to_cpu(*hrrq->hrrq_curr) & IPR_HRRQ_TOGGLE_BIT) == hrrq->toggle_bit) { - if (!irq_poll_sched_prep(>iopoll)) - irq_poll_sched(>iopoll); + irq_poll_sched(>iopoll); spin_unlock_irqrestore(hrrq->lock, hrrq_flags); return IRQ_HANDLED; } diff --git a/include/linux/irq_poll.h b/include/linux/irq_poll.h index 0cf7c26..73d7c20 100644 --- a/include/linux/irq_poll.h +++ b/include/linux/irq_poll.h @@ -18,19 +18,6 @@ enum { IRQ_POLL_F_DISABLE = 1, }; -/* - * Returns 0 if we successfully set the IRQ_POLL_F_SCHED bit, indicating - * that we were the first to acquire this iop for scheduling. If this iop - * is currently disabled, return "failure". - */ -static inline int irq_poll_sched_prep(struct irq_poll *iop) -{ - if (!test_bit(IRQ_POLL_F_DISABLE, >state)) - return test_and_set_bit(IRQ_POLL_F_SCHED, >state); - - return 1; -} - static inline int irq_poll_disable_pending(struct irq_poll *iop) { return test_bit(IRQ_POLL_F_DISABLE, >state); diff --git a/lib/irq_poll.c b/lib/irq_poll.c index 88af879..13cb149 100644 --- a/lib/irq_poll.c +++ b/lib/irq_poll.c @@ -21,13 +21,17 @@ static DEFINE_PER_CPU(struct list_head, blk_cpu_iopoll); * * Description: * Add this irq_poll structure to the pending poll list and trigger the - * raise of the blk iopoll softirq. The driver must already have gotten a - * successful return from irq_poll_sched_prep() before calling this. + * raise of the blk iopoll softirq. **/ void irq_poll_sched(struct irq_poll *iop) { unsigned long flags; + if (test_bit(IRQ_POLL_F_DISABLE, >state)) + return; + if (!test_and_set_bit(IRQ_POLL_F_SCHED, >state)) + return; + local_irq_save(flags); list_add_tail(>list, this_cpu_ptr(_cpu_iopoll)); __raise_softirq_irqoff(IRQ_POLL_SOFTIRQ); @@ -58,7 +62,7 @@ EXPORT_SYMBOL(__irq_poll_complete); * Description: * If a driver consumes less than the assigned budget in its run of the * iopoll handler, it'll end the polled mode by calling this function. The - * iopoll handler will not be invoked again before irq_poll_sched_prep() + * iopoll handler will not be invoked again before irq_poll_schedp() * is called. **/ void irq_poll_complete(struct irq_poll *iop) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 04/13] irq_poll: fold irq_poll_disable_pending into irq_poll_softirq
Signed-off-by: Christoph Hellwig--- include/linux/irq_poll.h | 5 - lib/irq_poll.c | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/include/linux/irq_poll.h b/include/linux/irq_poll.h index 73d7c20..c3145c7 100644 --- a/include/linux/irq_poll.h +++ b/include/linux/irq_poll.h @@ -18,11 +18,6 @@ enum { IRQ_POLL_F_DISABLE = 1, }; -static inline int irq_poll_disable_pending(struct irq_poll *iop) -{ - return test_bit(IRQ_POLL_F_DISABLE, >state); -} - extern void irq_poll_sched(struct irq_poll *); extern void irq_poll_init(struct irq_poll *, int, irq_poll_fn *); extern void irq_poll_complete(struct irq_poll *); diff --git a/lib/irq_poll.c b/lib/irq_poll.c index 13cb149..2c7b8e6 100644 --- a/lib/irq_poll.c +++ b/lib/irq_poll.c @@ -122,7 +122,7 @@ static void irq_poll_softirq(struct softirq_action *h) * move the instance around on the list at-will. */ if (work >= weight) { - if (irq_poll_disable_pending(iop)) + if (test_bit(IRQ_POLL_F_DISABLE, >state)) __irq_poll_complete(iop); else list_move_tail(>list, list); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 01/13] irq_poll: make blk-iopoll available outside the block layer
The new name is irq_poll as iopoll is already taken. Better suggestions welcome. Signed-off-by: Christoph Hellwig--- Documentation/kernel-per-CPU-kthreads.txt | 2 +- block/Makefile| 2 +- block/blk-iopoll.c| 224 -- drivers/scsi/Kconfig | 1 + drivers/scsi/be2iscsi/Kconfig | 1 + drivers/scsi/be2iscsi/be.h| 4 +- drivers/scsi/be2iscsi/be_iscsi.c | 4 +- drivers/scsi/be2iscsi/be_main.c | 24 ++-- drivers/scsi/ipr.c| 28 ++-- drivers/scsi/ipr.h| 4 +- include/linux/blk-iopoll.h| 46 -- include/linux/interrupt.h | 2 +- include/linux/irq_poll.h | 46 ++ include/trace/events/irq.h| 2 +- lib/Kconfig | 5 + lib/Makefile | 1 + lib/irq_poll.c| 221 + tools/lib/traceevent/event-parse.c| 2 +- tools/perf/util/trace-event-parse.c | 2 +- 19 files changed, 313 insertions(+), 308 deletions(-) delete mode 100644 block/blk-iopoll.c delete mode 100644 include/linux/blk-iopoll.h create mode 100644 include/linux/irq_poll.h create mode 100644 lib/irq_poll.c diff --git a/Documentation/kernel-per-CPU-kthreads.txt b/Documentation/kernel-per-CPU-kthreads.txt index f4cbfe0..edec3a3 100644 --- a/Documentation/kernel-per-CPU-kthreads.txt +++ b/Documentation/kernel-per-CPU-kthreads.txt @@ -90,7 +90,7 @@ BLOCK_SOFTIRQ: Do all of the following: from being initiated from tasks that might run on the CPU to be de-jittered. (It is OK to force this CPU offline and then bring it back online before you start your application.) -BLOCK_IOPOLL_SOFTIRQ: Do all of the following: +IRQ_POLL_SOFTIRQ: Do all of the following: 1. Force block-device interrupts onto some other CPU. 2. Initiate any block I/O and block-I/O polling on other CPUs. 3. Once your application has started, prevent CPU-hotplug operations diff --git a/block/Makefile b/block/Makefile index 00ecc97..e850474 100644 --- a/block/Makefile +++ b/block/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_BLOCK) := bio.o elevator.o blk-core.o blk-tag.o blk-sysfs.o \ blk-flush.o blk-settings.o blk-ioc.o blk-map.o \ blk-exec.o blk-merge.o blk-softirq.o blk-timeout.o \ - blk-iopoll.o blk-lib.o blk-mq.o blk-mq-tag.o \ + blk-lib.o blk-mq.o blk-mq-tag.o \ blk-mq-sysfs.o blk-mq-cpu.o blk-mq-cpumap.o ioctl.o \ genhd.o scsi_ioctl.o partition-generic.o ioprio.o \ partitions/ diff --git a/block/blk-iopoll.c b/block/blk-iopoll.c deleted file mode 100644 index 0736729..000 --- a/block/blk-iopoll.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Functions related to interrupt-poll handling in the block layer. This - * is similar to NAPI for network devices. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "blk.h" - -static unsigned int blk_iopoll_budget __read_mostly = 256; - -static DEFINE_PER_CPU(struct list_head, blk_cpu_iopoll); - -/** - * blk_iopoll_sched - Schedule a run of the iopoll handler - * @iop: The parent iopoll structure - * - * Description: - * Add this blk_iopoll structure to the pending poll list and trigger the - * raise of the blk iopoll softirq. The driver must already have gotten a - * successful return from blk_iopoll_sched_prep() before calling this. - **/ -void blk_iopoll_sched(struct blk_iopoll *iop) -{ - unsigned long flags; - - local_irq_save(flags); - list_add_tail(>list, this_cpu_ptr(_cpu_iopoll)); - __raise_softirq_irqoff(BLOCK_IOPOLL_SOFTIRQ); - local_irq_restore(flags); -} -EXPORT_SYMBOL(blk_iopoll_sched); - -/** - * __blk_iopoll_complete - Mark this @iop as un-polled again - * @iop: The parent iopoll structure - * - * Description: - * See blk_iopoll_complete(). This function must be called with interrupts - * disabled. - **/ -void __blk_iopoll_complete(struct blk_iopoll *iop) -{ - list_del(>list); - smp_mb__before_atomic(); - clear_bit_unlock(IOPOLL_F_SCHED, >state); -} -EXPORT_SYMBOL(__blk_iopoll_complete); - -/** - * blk_iopoll_complete - Mark this @iop as un-polled again - * @iop: The parent iopoll structure - * - * Description: - * If a driver consumes less than the assigned budget in its run of the - * iopoll handler, it'll end the polled mode by calling this function. The - * iopoll handler will not be invoked again before blk_iopoll_sched_prep() - * is called. - **/ -void blk_iopoll_complete(struct blk_iopoll *iop) -{ - unsigned long flags; - -
RE: [PATCH 08/10] aacraid: Disable device ID wildcard
Hello Christoph, > -Original Message- > From: Christoph Hellwig [mailto:h...@infradead.org] > Sent: Friday, December 4, 2015 12:33 AM > To: Raghava Aditya Renukunta > Cc: Tomas Henzl; jbottom...@parallels.com; linux-scsi@vger.kernel.org; > Mahesh Rajashekhara; Murthy Bhat; Santosh Akula; Gana Sridaran; > aacr...@pmc-sierra.com; Rich Bono > Subject: Re: [PATCH 08/10] aacraid: Disable device ID wildcard > > On Thu, Dec 03, 2015 at 09:32:18PM +, Raghava Aditya Renukunta wrote: > > This will enable us to prevent aacraid from loading for PCI devices that > match > > device ID wildcards. Enabling us to use say a new driver for future devices. > > This looks like a bogus reason. The same PCI ID should always be > compatible and mathed by the same driver. Even if you add a new driver > to expose additional feature and break these semantics there is no point > to do a) reject them conditionally on a module option and b) do this > before said driver is merged. I have spoken with my team and it does make more sense to submit it when the new driver is merged. I will withdraw this patch. Regards, Raghava Aditya -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
RE: [PATCH 04/10] aacraid: Fix memory leak in aac_fib_map_free
Hello Tomas, > -Original Message- > From: Tomas Henzl [mailto:the...@redhat.com] > Sent: Monday, December 7, 2015 6:06 AM > To: Raghava Aditya Renukunta; jbottom...@parallels.com; linux- > s...@vger.kernel.org > Cc: Mahesh Rajashekhara; Murthy Bhat; Santosh Akula; Gana Sridaran; > aacr...@pmc-sierra.com; Rich Bono > Subject: Re: [PATCH 04/10] aacraid: Fix memory leak in aac_fib_map_free > > On 5.12.2015 01:40, Raghava Aditya Renukunta wrote: > > Hello Tomas, > > > > > >> -Original Message- > >> From: Tomas Henzl [mailto:the...@redhat.com] > >> Sent: Friday, December 4, 2015 6:35 AM > >> To: Raghava Aditya Renukunta; jbottom...@parallels.com; linux- > >> s...@vger.kernel.org > >> Cc: Mahesh Rajashekhara; Murthy Bhat; Santosh Akula; Gana Sridaran; > >> aacr...@pmc-sierra.com; Rich Bono > >> Subject: Re: [PATCH 04/10] aacraid: Fix memory leak in aac_fib_map_free > >> > >> On 1.12.2015 13:39, Raghava Aditya Renukunta wrote: > >>> From: Raghava Aditya Renukunta >> >>> > >>> aac_fib_map_free() calls pci_free_consistent() without checking that > >>> dev->hw_fib_va is not NULL and dev->max_fib_size is not zero.If they > >>> are indeed NULL/0, this will result in a hang as pci_free_consistent() > >>> will attempt to invalidate cache for the entire 64-bit address space > >>> (which would take a very long time). > >>> > >>> Fixed by adding a check to make sure that dev->hw_fib_va and > >>> dev->max_fib_size are not NULL and 0 respectively. > >>> > >>> Signed-off-by: Raghava Aditya Renukunta > >> > >> > >> Reviewed-by: Tomas Henzl > >> > >> Is the can_queue constant during the driver's life, or is it possible > >> to manipulate it (aac_change_queue_depth)? > >> > >> Tomas > > can_queue is only changed in aac_init_adapter. Do you want to save > > (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB) in a variable > > So that the whole can_queue dereference need not be used? > > It's fine as it is, (I thought it may change elsewhere in your code > but now I think that I was wrong). > > --tm I will leave it as it is then. > > > > > Regards, > > Raghava Aditya > > > >>> --- > >>> drivers/scsi/aacraid/commsup.c | 9 ++--- > >>> 1 file changed, 6 insertions(+), 3 deletions(-) > >>> > >>> diff --git a/drivers/scsi/aacraid/commsup.c > >> b/drivers/scsi/aacraid/commsup.c > >>> index b257d3b..9533f47 100644 > >>> --- a/drivers/scsi/aacraid/commsup.c > >>> +++ b/drivers/scsi/aacraid/commsup.c > >>> @@ -83,9 +83,12 @@ static int fib_map_alloc(struct aac_dev *dev) > >>> > >>> void aac_fib_map_free(struct aac_dev *dev) > >>> { > >>> - pci_free_consistent(dev->pdev, > >>> - dev->max_fib_size * (dev->scsi_host_ptr->can_queue + > >> AAC_NUM_MGT_FIB), > >>> - dev->hw_fib_va, dev->hw_fib_pa); > >>> + if (dev->hw_fib_va && dev->max_fib_size) { > >>> + pci_free_consistent(dev->pdev, > >>> + (dev->max_fib_size * > >>> + (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB)), > >>> + dev->hw_fib_va, dev->hw_fib_pa); > >>> + } > >>> dev->hw_fib_va = NULL; > >>> dev->hw_fib_pa = 0; > >>> } > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-scsi" in > > the body of a message to majord...@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH] scsi_dh_alua: Remove stale variables
> "Hannes" == Hannes Reineckewrites: Hannes> With commit 83ea0e5e3501 these variables became obsolete, but Hannes> weren't removed. Applied to 4.5/scsi-queue. -- Martin K. Petersen Oracle Linux Engineering -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 09/20] qla2xxx: Change check_stop_free to always return 1
From: Quinn Tranchange tcm_qla2xxx_check_stop_free to always return 1 to prevent transport_cmd_finish_abort from accidently taking extra kref_put. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/tcm_qla2xxx.c |3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 74c6e9b..366142a 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -314,7 +314,8 @@ static int tcm_qla2xxx_check_stop_free(struct se_cmd *se_cmd) cmd->cmd_flags |= BIT_14; } - return target_put_sess_cmd(se_cmd); + target_put_sess_cmd(se_cmd); + return 1; } /* tcm_qla2xxx_release_cmd - Callback from TCM Core to release underlying -- 1.7.7 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 06/20] qla2xxx: Delete session if initiator is gone from FW
From: Alexei Potashnik1. Initiator A is logged in with fc_id(1)/loop_id(1) 2. Initiator A re-logs in with fc_id(2)/loop_id(2) 3. Part of old session deletion async logoout for 1/1 is queued 4. Initiator B logs in with fc_id(1)/loop_id(1), starts passing data and creates session. 5. Async logo from 3 is processed by DPC and sent to FW Now initiator B has the session but is logged out from FW. This condition is detected first with CTIO error 29 at which point we should delete current session. During session deletion we will send LOGO to initiator to force re-login. Under rare circumstances initiator might be logged out of FW, not have driver session, but still think it's logged in. E.g. the above sequence plus session deletion due to re-config. Incoming commands will fail to create local session because initiator is not found in FW. In this case we also issue LOGO to initiator to force him re-login. Finally this patch fixes exchange leak when commands where received in logged out state. In this case loop_id must be set to when corresponding exchange is terminated. The patch modifies exchange termination to always use , since in certain scenarios it's impossible to tell whether command was received in logged in or logged out state. Signed-off-by: Alexei Potashnik Acked-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_def.h|2 + drivers/scsi/qla2xxx/qla_os.c |1 + drivers/scsi/qla2xxx/qla_target.c | 108 +++-- drivers/scsi/qla2xxx/qla_target.h |9 +++ 4 files changed, 104 insertions(+), 16 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 366f65b..1050fc2 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -3652,6 +3652,8 @@ typedef struct scsi_qla_host { atomic_tgeneration_tick; /* Time when global fcport update has been scheduled */ int total_fcport_update_gen; + /* List of pending LOGOs, protected by tgt_mutex */ + struct list_headlogo_list; uint32_tvp_abort_cnt; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index a63ce6e..c02cbc6 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -3918,6 +3918,7 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, INIT_LIST_HEAD(>list); INIT_LIST_HEAD(>qla_cmd_list); INIT_LIST_HEAD(>qla_sess_op_cmd_list); + INIT_LIST_HEAD(>logo_list); spin_lock_init(>work_lock); spin_lock_init(>cmd_list_lock); diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 661124a..5ef9d4c 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -389,6 +389,52 @@ void qlt_response_pkt_all_vps(struct scsi_qla_host *vha, response_t *pkt) } +typedef struct { + /* These fields must be initialized by the caller */ + port_id_t id; + /* +* number of cmds dropped while we were waiting for +* initiator to ack LOGO initialize to 1 if LOGO is +* triggered by a command, otherwise, to 0 +*/ + int cmd_count; + + /* These fields are used by callee */ + struct list_head list; +} qlt_port_logo_t; + +static void +qlt_send_first_logo(struct scsi_qla_host *vha, qlt_port_logo_t *logo) +{ + qlt_port_logo_t *tmp; + int res; + + mutex_lock(>vha_tgt.tgt_mutex); + + list_for_each_entry(tmp, >logo_list, list) { + if (tmp->id.b24 == logo->id.b24) { + tmp->cmd_count += logo->cmd_count; + mutex_unlock(>vha_tgt.tgt_mutex); + return; + } + } + + list_add_tail(>list, >logo_list); + + mutex_unlock(>vha_tgt.tgt_mutex); + + res = qla24xx_els_dcmd_iocb(vha, ELS_DCMD_LOGO, logo->id); + + mutex_lock(>vha_tgt.tgt_mutex); + list_del(>list); + mutex_unlock(>vha_tgt.tgt_mutex); + + dev_info(>hw->pdev->dev, +"Finished LOGO to %02x:%02x:%02x, dropped %d cmds, res = %#x\n", +logo->id.b.domain, logo->id.b.area, logo->id.b.al_pa, +logo->cmd_count, res); +} + static void qlt_free_session_done(struct work_struct *work) { struct qla_tgt_sess *sess = container_of(work, struct qla_tgt_sess, @@ -402,14 +448,21 @@ static void qlt_free_session_done(struct work_struct *work) ql_dbg(ql_dbg_tgt_mgt, vha, 0xf084, "%s: se_sess %p / sess %p from port %8phC loop_id %#04x" - " s_id %02x:%02x:%02x logout %d keep %d plogi %d\n", + " s_id %02x:%02x:%02x logout %d keep %d plogi %d els_logo %d\n", __func__, sess->se_sess, sess,
[PATCH 10/20] qla2xxx: Fix interaction issue between qla2xxx and Target Core Module
From: Quinn TranDuring lun reset, TMR thread from TCM would issue abort to qla driver. At abort time, each command is in different state. Depending on the state, qla will use the TMR thread to trigger a command free(cmd_kref--) if command is not down at firmware. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_target.c | 60 + drivers/scsi/qla2xxx/qla_target.h | 59 + drivers/scsi/qla2xxx/tcm_qla2xxx.c | 73 ++- 3 files changed, 147 insertions(+), 45 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 638940f..4d42b79 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -105,7 +105,7 @@ static void qlt_response_pkt(struct scsi_qla_host *ha, response_t *pkt); static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun, int fn, void *iocb, int flags); static void qlt_send_term_exchange(struct scsi_qla_host *ha, struct qla_tgt_cmd - *cmd, struct atio_from_isp *atio, int ha_locked); + *cmd, struct atio_from_isp *atio, int ha_locked, int ul_abort); static void qlt_reject_free_srr_imm(struct scsi_qla_host *ha, struct qla_tgt_srr_imm *imm, int ha_lock); static void qlt_abort_cmd_on_host_reset(struct scsi_qla_host *vha, @@ -2646,7 +2646,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, /* no need to terminate. FW already freed exchange. */ qlt_abort_cmd_on_host_reset(cmd->vha, cmd); else - qlt_send_term_exchange(vha, cmd, >atio, 1); + qlt_send_term_exchange(vha, cmd, >atio, 1, 0); spin_unlock_irqrestore(>hardware_lock, flags); return 0; } @@ -3154,7 +3154,8 @@ static int __qlt_send_term_exchange(struct scsi_qla_host *vha, } static void qlt_send_term_exchange(struct scsi_qla_host *vha, - struct qla_tgt_cmd *cmd, struct atio_from_isp *atio, int ha_locked) + struct qla_tgt_cmd *cmd, struct atio_from_isp *atio, int ha_locked, + int ul_abort) { unsigned long flags = 0; int rc; @@ -3174,8 +3175,7 @@ static void qlt_send_term_exchange(struct scsi_qla_host *vha, qlt_alloc_qfull_cmd(vha, atio, 0, 0); done: - if (cmd && (!cmd->aborted || - !cmd->cmd_sent_to_fw)) { + if (cmd && !ul_abort && !cmd->aborted) { if (cmd->sg_mapped) qlt_unmap_sg(vha, cmd); vha->hw->tgt.tgt_ops->free_cmd(cmd); @@ -3234,21 +3234,43 @@ static void qlt_chk_exch_leak_thresh_hold(struct scsi_qla_host *vha) } -void qlt_abort_cmd(struct qla_tgt_cmd *cmd) +int qlt_abort_cmd(struct qla_tgt_cmd *cmd) { struct qla_tgt *tgt = cmd->tgt; struct scsi_qla_host *vha = tgt->vha; struct se_cmd *se_cmd = >se_cmd; + unsigned long flags,refcount; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf014, "qla_target(%d): terminating exchange for aborted cmd=%p " "(se_cmd=%p, tag=%llu)", vha->vp_idx, cmd, >se_cmd, se_cmd->tag); +spin_lock_irqsave(>cmd_lock, flags); +if (cmd->aborted) { +spin_unlock_irqrestore(>cmd_lock, flags); + +/* It's normal to see 2 calls in this path: + * 1) XFER Rdy completion + CMD_T_ABORT + * 2) TCM TMR - drain_state_list + */ +refcount = atomic_read(>se_cmd.cmd_kref.refcount); +ql_dbg(ql_dbg_tgt_mgt, vha, 0x, + "multiple abort. %p refcount %lx" + "transport_state %x, t_state %x, se_cmd_flags %x \n", + cmd, refcount,cmd->se_cmd.transport_state, + cmd->se_cmd.t_state,cmd->se_cmd.se_cmd_flags); + +return EIO; +} + cmd->aborted = 1; cmd->cmd_flags |= BIT_6; +spin_unlock_irqrestore(>cmd_lock, flags); + + qlt_send_term_exchange(vha, cmd, >atio, 0, 1); - qlt_send_term_exchange(vha, cmd, >atio, 0); + return 0; } EXPORT_SYMBOL(qlt_abort_cmd); @@ -3263,6 +3285,9 @@ void qlt_free_cmd(struct qla_tgt_cmd *cmd) BUG_ON(cmd->cmd_in_wq); + if (cmd->sg_mapped) + qlt_unmap_sg(cmd->vha, cmd); + if (!cmd->q_full) qlt_decr_num_pend_cmds(cmd->vha); @@ -3380,7 +3405,7 @@ static int qlt_term_ctio_exchange(struct scsi_qla_host *vha, void *ctio, term = 1; if (term) - qlt_send_term_exchange(vha, cmd, >atio, 1); + qlt_send_term_exchange(vha, cmd, >atio, 1, 0); return term; } @@ -3735,6 +3760,7 @@ static void __qlt_do_work(struct qla_tgt_cmd *cmd) goto out_term; } + spin_lock_init(>cmd_lock); cdb = >u.isp24.fcp_cmnd.cdb[0];
[PATCH 03/20] qla2xxx: Enable Target counters in DebugFS.
Following counters are added in target mode to help debugging efforts. Target Counters qla_core_sbt_cmd = 0 qla_core_ret_sta_ctio = 0 qla_core_ret_ctio = 0 core_qla_que_buf = 0 core_qla_snd_status = 0 core_qla_free_cmd = 0 num alloc iocb failed = 0 num term exchange sent = 0 num Q full sent = 0 Signed-off-by: Himanshu MadhaniSigned-off-by: Giridhar Malavali --- drivers/scsi/qla2xxx/qla_dbg.c |2 +- drivers/scsi/qla2xxx/qla_def.h | 15 + drivers/scsi/qla2xxx/qla_dfs.c | 56 drivers/scsi/qla2xxx/qla_iocb.c|1 + drivers/scsi/qla2xxx/qla_target.c |7 drivers/scsi/qla2xxx/tcm_qla2xxx.c |4 ++ 6 files changed, 84 insertions(+), 1 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index e25f5ac..07451bb 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -60,7 +60,7 @@ * | || 0xb13c-0xb140 | * | || 0xb149 | * | MultiQ | 0xc00c | | - * | Misc | 0xd300 | 0xd031-0xd0ff | + * | Misc | 0xd301 | 0xd031-0xd0ff | * | || 0xd101-0xd1fe | * | || 0xd214-0xd2fe | * | Target Mode | 0xe080 || diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index be9a674..1d2a51a 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -3342,6 +3342,8 @@ struct qla_hw_data { uint32_tchain_offset; struct dentry *dfs_dir; struct dentry *dfs_fce; + struct dentry *dfs_tgt_counters; + dma_addr_t fce_dma; void*fce; uint32_tfce_bufs; @@ -3499,6 +3501,18 @@ struct qla_hw_data { int allow_cna_fw_dump; }; +struct qla_tgt_counters { + uint64_t qla_core_sbt_cmd; + uint64_t core_qla_que_buf; + uint64_t qla_core_ret_ctio; + uint64_t core_qla_snd_status; + uint64_t qla_core_ret_sta_ctio; + uint64_t core_qla_free_cmd; + uint64_t num_q_full_sent; + uint64_t num_alloc_iocb_failed; + uint64_t num_term_xchg_sent; +}; + /* * Qlogic scsi host structure */ @@ -3651,6 +3665,7 @@ typedef struct scsi_qla_host { atomic_tvref_count; struct qla8044_reset_template reset_tmplt; + struct qla_tgt_counters tgt_counters; } scsi_qla_host_t; #define SET_VP_IDX 1 diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index 15cf074..449541f 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -13,6 +13,48 @@ static struct dentry *qla2x00_dfs_root; static atomic_t qla2x00_dfs_root_count; static int +qla_dfs_tgt_counters_show(struct seq_file *s, void *unused) +{ + struct scsi_qla_host *vha = s->private; + + seq_puts(s, "Target Counters\n"); + seq_printf(s, "qla_core_sbt_cmd = %lld\n", + vha->tgt_counters.qla_core_sbt_cmd); + seq_printf(s, "qla_core_ret_sta_ctio = %lld\n", + vha->tgt_counters.qla_core_ret_sta_ctio); + seq_printf(s, "qla_core_ret_ctio = %lld\n", + vha->tgt_counters.qla_core_ret_ctio); + seq_printf(s, "core_qla_que_buf = %lld\n", + vha->tgt_counters.core_qla_que_buf); + seq_printf(s, "core_qla_snd_status = %lld\n", + vha->tgt_counters.core_qla_snd_status); + seq_printf(s, "core_qla_free_cmd = %lld\n", + vha->tgt_counters.core_qla_free_cmd); + seq_printf(s, "num alloc iocb failed = %lld\n", + vha->tgt_counters.num_alloc_iocb_failed); + seq_printf(s, "num term exchange sent = %lld\n", + vha->tgt_counters.num_term_xchg_sent); + seq_printf(s, "num Q full sent = %lld\n", + vha->tgt_counters.num_q_full_sent); + + return 0; +} + +static int +qla_dfs_tgt_counters_open(struct inode *inode, struct file *file) +{ + struct scsi_qla_host *vha = inode->i_private; + return single_open(file, qla_dfs_tgt_counters_show, vha); +} + +static const struct file_operations dfs_tgt_counters_ops = { + .open = qla_dfs_tgt_counters_open, + .read = seq_read, + .llseek = seq_lseek, + .release= single_release, +}; + +static int qla2x00_dfs_fce_show(struct seq_file *s, void *unused) { scsi_qla_host_t *vha = s->private; @@ -146,6 +188,14 @@ create_dir: atomic_inc(_dfs_root_count); create_nodes: + ha->dfs_tgt_counters = debugfs_create_file("tgt_counters", S_IRUSR, + ha->dfs_dir, vha,
[PATCH 18/20] qla2xxx: Set all queues to 4k
From: Quinn Transet ATIO/Request/Response Queues and Default number of outstanding command to 4k. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_def.h |3 ++- drivers/scsi/qla2xxx/qla_os.c |6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 0ddb2de..141a6ba 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -259,7 +259,7 @@ #define LOOP_DOWN_TIME 255 /* 240 */ #defineLOOP_DOWN_RESET (LOOP_DOWN_TIME - 30) -#define DEFAULT_OUTSTANDING_COMMANDS 1024 +#define DEFAULT_OUTSTANDING_COMMANDS 4096 #define MIN_OUTSTANDING_COMMANDS 128 /* ISP request and response entry counts (37-65535) */ @@ -267,6 +267,7 @@ #define REQUEST_ENTRY_CNT_2200 2048/* Number of request entries. */ #define REQUEST_ENTRY_CNT_24XX 2048/* Number of request entries. */ #define REQUEST_ENTRY_CNT_83XX 8192/* Number of request entries. */ +#define RESPONSE_ENTRY_CNT_83XX4096/* Number of response entries.*/ #define RESPONSE_ENTRY_CNT_210064 /* Number of response entries.*/ #define RESPONSE_ENTRY_CNT_2300512 /* Number of response entries.*/ #define RESPONSE_ENTRY_CNT_MQ 128 /* Number of response entries.*/ diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index b350905..0fcc838 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -2483,7 +2483,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; ha->mbx_count = MAILBOX_REGISTER_COUNT; req_length = REQUEST_ENTRY_CNT_83XX; - rsp_length = RESPONSE_ENTRY_CNT_2300; + rsp_length = RESPONSE_ENTRY_CNT_83XX; ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX; ha->max_loop_id = SNS_LAST_LOOP_ID_2300; ha->init_cb_size = sizeof(struct mid_init_cb_81xx); @@ -2513,8 +2513,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->portnum = PCI_FUNC(ha->pdev->devfn); ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; ha->mbx_count = MAILBOX_REGISTER_COUNT; - req_length = REQUEST_ENTRY_CNT_24XX; - rsp_length = RESPONSE_ENTRY_CNT_2300; + req_length = REQUEST_ENTRY_CNT_83XX; + rsp_length = RESPONSE_ENTRY_CNT_83XX; ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX; ha->max_loop_id = SNS_LAST_LOOP_ID_2300; ha->init_cb_size = sizeof(struct mid_init_cb_81xx); -- 1.7.7 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 19/20] qla2xxx: Add bulk send for atio & ctio completion paths.
From: Quinn TranAt high traffic, the work queue can become a bottle neck. Instead of putting each command on the work queue as 1 work element, the fix would daisy chain a list of commands that came from FW/interrupt under 1 work element to reduce lock contention. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_def.h |3 + drivers/scsi/qla2xxx/qla_isr.c |3 + drivers/scsi/qla2xxx/qla_target.c | 65 ++- drivers/scsi/qla2xxx/qla_target.h |2 + drivers/scsi/qla2xxx/tcm_qla2xxx.c | 75 +-- 5 files changed, 133 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 141a6ba..b731eef 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2937,6 +2937,9 @@ struct qlt_hw_data { uint32_t leak_exchg_thresh_hold; spinlock_t sess_lock; int rspq_vector_cpuid; + + void *ctio_for_bulk_process; + void *atio_for_bulk_process; spinlock_t atio_lock cacheline_aligned; }; diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 72d1cdc..d2bbcbb 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -2646,6 +2646,9 @@ process_err: WRT_REG_DWORD(>rsp_q_out[0], rsp->ring_index); } else WRT_REG_DWORD(rsp->rsp_q_out, rsp->ring_index); + + if (ha->tgt.ctio_for_bulk_process) + vha->hw->tgt.tgt_ops->handle_bulk(vha); } static void diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index f8f2b8a..9cf812f 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -3834,12 +3834,23 @@ static void qlt_do_work(struct work_struct *work) struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work); scsi_qla_host_t *vha = cmd->vha; unsigned long flags; + struct list_head h; + struct qla_tgt_cmd *c = NULL, *tc = NULL; spin_lock_irqsave(>cmd_list_lock, flags); list_del(>cmd_list); spin_unlock_irqrestore(>cmd_list_lock, flags); + INIT_LIST_HEAD(); + if (!list_empty(>bulk_process_list)) + list_splice_init(>bulk_process_list, ); + __qlt_do_work(cmd); + + list_for_each_entry_safe(c, tc, , bulk_process_list) { + list_del_init(>bulk_process_list); + c->work.func(>work); + } } static struct qla_tgt_cmd *qlt_get_tag(scsi_qla_host_t *vha, @@ -3871,6 +3882,7 @@ static struct qla_tgt_cmd *qlt_get_tag(scsi_qla_host_t *vha, cmd->jiffies_at_alloc = get_jiffies_64(); cmd->reset_count = vha->hw->chip_reset; + INIT_LIST_HEAD(>bulk_process_list); return cmd; } @@ -3930,6 +3942,7 @@ static void qlt_create_sess_from_atio(struct work_struct *work) kfree(op); return; } + /* * __qlt_do_work() will call ha->tgt.tgt_ops->put_sess() to release * the extra reference taken above by qlt_make_local_sess() @@ -3946,6 +3959,40 @@ out_term: } +static void qlt_add_cmd_to_bulk_list(struct qla_tgt_cmd *cmd) +{ + struct qla_hw_data *ha = cmd->tgt->ha; + struct qla_tgt_cmd *hc = (struct qla_tgt_cmd *) + ha->tgt.atio_for_bulk_process; + + if (IS_QLA27XX(ha) || IS_QLA83XX(ha)) + /* We are running under atio_lock protection here. */ + assert_spin_locked(>tgt.atio_lock); + else + assert_spin_locked(>hardware_lock); + + if (hc) + list_add_tail(>bulk_process_list, >bulk_process_list); + else + ha->tgt.atio_for_bulk_process = (void *)cmd; +} + +static void qlt_send_atio_bulk(struct qla_hw_data *ha) +{ + struct qla_tgt_cmd *cmd = + (struct qla_tgt_cmd *)ha->tgt.atio_for_bulk_process; + + if (IS_QLA27XX(ha) || IS_QLA83XX(ha)) + /*We are running under atio_lock protection here */ + assert_spin_locked(>tgt.atio_lock); + else + assert_spin_locked(>hardware_lock); + + ha->tgt.atio_for_bulk_process = NULL; + queue_work_on(smp_processor_id(), qla_tgt_wq, >work); +} + + /* ha->hardware_lock supposed to be held on entry */ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, struct atio_from_isp *atio) @@ -4011,17 +4058,11 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, spin_unlock(>cmd_list_lock); INIT_WORK(>work, qlt_do_work); - if (ha->msix_count) { + if (ha->msix_count) cmd->se_cmd.cpuid = ha->tgt.rspq_vector_cpuid; - if (cmd->atio.u.isp24.fcp_cmnd.rddata) - queue_work_on(smp_processor_id(),
[PATCH 14/20] qla2xxx: Add irq affinity notification
From: Quinn TranRegister to receive notification of when irq setting change occured. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_def.h|6 +++ drivers/scsi/qla2xxx/qla_isr.c| 76 - drivers/scsi/qla2xxx/qla_target.c | 11 + 3 files changed, 92 insertions(+), 1 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 7c6369b..9165732 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2714,11 +2714,16 @@ struct isp_operations { struct scsi_qla_host; + +#define QLA83XX_RSPQ_MSIX_ENTRY_NUMBER 1 /* refer to qla83xx_msix_entries */ + struct qla_msix_entry { int have_irq; uint32_t vector; uint16_t entry; struct rsp_que *rsp; + struct irq_affinity_notify irq_notify; + int cpuid; }; #defineWATCH_INTERVAL 1 /* number of seconds */ @@ -2930,6 +2935,7 @@ struct qlt_hw_data { spinlock_t q_full_lock; uint32_t leak_exchg_thresh_hold; spinlock_t sess_lock; + int rspq_vector_cpuid; }; #define MAX_QFULL_CMDS_ALLOC 8192 diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index ea7e8e8..e879802d 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -18,6 +18,10 @@ static void qla2x00_status_entry(scsi_qla_host_t *, struct rsp_que *, void *); static void qla2x00_status_cont_entry(struct rsp_que *, sts_cont_entry_t *); static void qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *, sts_entry_t *); +static void qla_irq_affinity_notify(struct irq_affinity_notify *, +const cpumask_t *); +static void qla_irq_affinity_release(struct kref *); + /** * qla2100_intr_handler() - Process interrupts for the ISP2100 and ISP2200. @@ -2548,6 +2552,14 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, if (!vha->flags.online) return; + if (rsp->msix->cpuid != smp_processor_id()) { + /* if kernel does not notify qla of IRQ's CPU change, +* then set it here. +*/ + rsp->msix->cpuid = smp_processor_id(); + ha->tgt.rspq_vector_cpuid = rsp->msix->cpuid; + } + while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) { pkt = (struct sts_entry_24xx *)rsp->ring_ptr; @@ -2979,8 +2991,11 @@ qla24xx_disable_msix(struct qla_hw_data *ha) for (i = 0; i < ha->msix_count; i++) { qentry = >msix_entries[i]; - if (qentry->have_irq) + if (qentry->have_irq) { + /* un-register irq cpu affinity notification */ + irq_set_affinity_notifier(qentry->vector, NULL); free_irq(qentry->vector, qentry->rsp); + } } pci_disable_msix(ha->pdev); kfree(ha->msix_entries); @@ -3043,6 +3058,9 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) qentry->entry = entries[i].entry; qentry->have_irq = 0; qentry->rsp = NULL; + qentry->irq_notify.notify = qla_irq_affinity_notify; + qentry->irq_notify.release = qla_irq_affinity_release; + qentry->cpuid = -1; } /* Enable MSI-X vectors for the base queue */ @@ -3061,6 +3079,18 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) qentry->have_irq = 1; qentry->rsp = rsp; rsp->msix = qentry; + + /* Register for CPU affinity notification. */ + irq_set_affinity_notifier(qentry->vector, >irq_notify); + + /* Schedule work (ie. trigger a notification) to read cpu +* mask for this specific irq. +* kref_get is required because + * irq_affinity_notify() will do + * kref_put(). + */ + kref_get(>irq_notify.kref); + schedule_work(>irq_notify.work); } /* @@ -3240,3 +3270,47 @@ int qla25xx_request_irq(struct rsp_que *rsp) msix->rsp = rsp; return ret; } + + +/* irq_set_affinity/irqbalance will trigger notification of cpu mask update */ +static void qla_irq_affinity_notify(struct irq_affinity_notify *notify, + const cpumask_t *mask) +{ + struct qla_msix_entry *e = + container_of(notify, struct qla_msix_entry, irq_notify); + struct qla_hw_data *ha; + struct scsi_qla_host *base_vha; + + /* user is recommended to set mask to just 1 cpu */ + e->cpuid = cpumask_first(mask); + + ha = e->rsp->hw; + base_vha = pci_get_drvdata(ha->pdev); + + ql_dbg(ql_dbg_init, base_vha, 0x, + "%s:
[PATCH 07/20] qla2xxx: Wait for all conflicts before ack'ing PLOGI
From: Alexei PotashnikUntil now ack'ing of a new PLOGI has only been delayed if there was an existing session for the same WWN. Ack was released when the session deletion completed. If there was another WWN session with the same fc_id/loop_id pair (aka "conflicting session"), PLOGI was still ack'ed immediately. This potentially caused a problem when old session deletion logged fc_id/loop_id out of FW after new session has been established. Two work-arounds were attempted before: 1. Dropping PLOGIs until conflicting session goes away. 2. Detecting initiator being logged out of FW and issuing LOGO to force re-login. This patch introduces proper solution to the problem where PLOGI is held until either existing session with same WWN or any conflicting session goes away. Mechanism supports one session holding two PLOGI acks as well as one PLOGI ack being held by many sessions. Signed-off-by: Alexei Potashnik Acked-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_dbg.c|4 +- drivers/scsi/qla2xxx/qla_def.h|2 + drivers/scsi/qla2xxx/qla_os.c |1 + drivers/scsi/qla2xxx/qla_target.c | 202 + drivers/scsi/qla2xxx/qla_target.h | 18 +++- 5 files changed, 177 insertions(+), 50 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 02f01be..b303027 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -26,7 +26,7 @@ * | || 0x3036,0x3038 | * | || 0x303a | * | DPC Thread | 0x4023 | 0x4002,0x4013 | - * | Async Events | 0x508a | 0x502b-0x502f | + * | Async Events | 0x5089 | 0x502b-0x502f | * | || 0x5084,0x5075 | * | || 0x503d,0x5044 | * | || 0x507b,0x505f | @@ -63,7 +63,7 @@ * | || 0xd101-0xd1fe | * | || 0xd214-0xd2fe | * | Target Mode | 0xe080 || - * | Target Mode Management | 0xf096 | 0xf002 | + * | Target Mode Management | 0xf09b | 0xf002 | * | || 0xf046-0xf049 | * | Target Mode Task Management | 0x1000d || * -- diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 1050fc2..46abba8 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -3654,6 +3654,8 @@ typedef struct scsi_qla_host { int total_fcport_update_gen; /* List of pending LOGOs, protected by tgt_mutex */ struct list_headlogo_list; + /* List of pending PLOGI acks, protected by hw lock */ + struct list_headplogi_ack_list; uint32_tvp_abort_cnt; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index c02cbc6..eb47272 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -3919,6 +3919,7 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, INIT_LIST_HEAD(>qla_cmd_list); INIT_LIST_HEAD(>qla_sess_op_cmd_list); INIT_LIST_HEAD(>logo_list); + INIT_LIST_HEAD(>plogi_ack_list); spin_lock_init(>work_lock); spin_lock_init(>cmd_list_lock); diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 5ef9d4c..57b4294 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -118,10 +118,13 @@ static void qlt_send_notify_ack(struct scsi_qla_host *vha, struct imm_ntfy_from_isp *ntfy, uint32_t add_flags, uint16_t resp_code, int resp_code_valid, uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan); +static void qlt_send_term_imm_notif(struct scsi_qla_host *vha, + struct imm_ntfy_from_isp *imm, int ha_locked); /* * Global Variables */ static struct kmem_cache *qla_tgt_mgmt_cmd_cachep; +static struct kmem_cache *qla_tgt_plogi_cachep; static mempool_t *qla_tgt_mgmt_cmd_mempool; static struct workqueue_struct *qla_tgt_wq; static DEFINE_MUTEX(qla_tgt_mutex); @@ -389,6 +392,85 @@ void qlt_response_pkt_all_vps(struct scsi_qla_host *vha, response_t *pkt) } +/* + * All qlt_plogi_ack_t operations are protected by hardware_lock + */ + +/* + * This is a zero-base ref-counting solution, since hardware_lock + * guarantees that ref_count is
[PATCH 05/20] qla2xxx: Added interface to send ELS commands from driver.
Signed-off-by: Himanshu MadhaniSigned-off-by: Giridhar Malavali --- drivers/scsi/qla2xxx/qla_attr.c | 36 +++ drivers/scsi/qla2xxx/qla_dbg.c|5 +- drivers/scsi/qla2xxx/qla_def.h| 19 - drivers/scsi/qla2xxx/qla_gbl.h|2 + drivers/scsi/qla2xxx/qla_inline.h |2 + drivers/scsi/qla2xxx/qla_iocb.c | 189 + drivers/scsi/qla2xxx/qla_isr.c|6 + 7 files changed, 255 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 6b942d9..6992ebc 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -824,6 +824,41 @@ static struct bin_attribute sysfs_reset_attr = { }; static ssize_t +qla2x00_issue_logo(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, + struct device, kobj))); + int type; + int rval = 0; + port_id_t did; + + type = simple_strtol(buf, NULL, 10); + + did.b.domain = (type & 0x00ff) >> 16; + did.b.area = (type & 0xff00) >> 8; + did.b.al_pa = (type & 0x00ff); + + ql_log(ql_log_info, vha, 0x70e3, "portid=%02x%02x%02x done\n", + did.b.domain, did.b.area, did.b.al_pa); + + ql_log(ql_log_info, vha, 0x70e4, "%s: %d\n", __func__, type); + + rval = qla24xx_els_dcmd_iocb(vha, ELS_DCMD_LOGO, did); + return count; +} + +static struct bin_attribute sysfs_issue_logo_attr = { + .attr = { + .name = "issue_logo", + .mode = S_IWUSR, + }, + .size = 0, + .write = qla2x00_issue_logo, +}; + +static ssize_t qla2x00_sysfs_read_xgmac_stats(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) @@ -937,6 +972,7 @@ static struct sysfs_entry { { "vpd", _vpd_attr, 1 }, { "sfp", _sfp_attr, 1 }, { "reset", _reset_attr, }, + { "issue_logo", _issue_logo_attr, }, { "xgmac_stats", _xgmac_stats_attr, 3 }, { "dcbx_tlv", _dcbx_tlv_attr, 3 }, { NULL }, diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 07451bb..02f01be 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -19,7 +19,7 @@ * | Device Discovery | 0x2016 | 0x2020-0x2022, | * | || 0x2011-0x2012, | * | || 0x2099-0x20a4 | - * | Queue Command and IO tracing | 0x3075 | 0x300b | + * | Queue Command and IO tracing | 0x3074 | 0x300b | * | || 0x3027-0x3028 | * | || 0x303d-0x3041 | * | || 0x302d,0x3033 | @@ -27,12 +27,11 @@ * | || 0x303a | * | DPC Thread | 0x4023 | 0x4002,0x4013 | * | Async Events | 0x508a | 0x502b-0x502f | - * | || 0x5047 | * | || 0x5084,0x5075 | * | || 0x503d,0x5044 | * | || 0x507b,0x505f | * | Timer Routines | 0x6012 || - * | User Space Interactions | 0x70e2 | 0x7018,0x702e | + * | User Space Interactions | 0x70e65 | 0x7018,0x702e | * | || 0x7020,0x7024 | * | || 0x7039,0x7045 | * | || 0x7073-0x7075 | diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index cf32fb9..366f65b 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -310,6 +310,14 @@ struct srb_cmd { /* To identify if a srb is of T10-CRC type. @sp => srb_t pointer */ #define IS_PROT_IO(sp) (sp->flags & SRB_CRC_CTX_DSD_VALID) +struct els_logo_payload { + uint8_t opcode; + uint8_t rsvd[3]; + uint8_t s_id[3]; + uint8_t rsvd1[1]; + uint8_t wwpn[WWN_SIZE]; +}; + /* * SRB extensions. */ @@ -323,6 +331,15 @@ struct srb_iocb { uint16_t data[2]; } logio; struct { +#define ELS_DCMD_TIMEOUT 20 +#define ELS_DCMD_LOGO 0x5 + uint32_t flags; + uint32_t els_cmd; +
[PATCH 04/20] qla2xxx: Add FW resource count in DebugFS.
From: Quinn TranDebugFS now will show fw_resource_count node. FW Resource count Original TGT exchg count[0] current TGT exchg count[0] original Initiator Exchange count[2048] Current Initiator Exchange count[2048] Original IOCB count[2078] Current IOCB count[2067] MAX VP count[254] MAX FCF count[0] Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_def.h | 13 +++-- drivers/scsi/qla2xxx/qla_dfs.c | 50 drivers/scsi/qla2xxx/qla_gbl.h |3 +- drivers/scsi/qla2xxx/qla_init.c| 12 +++- drivers/scsi/qla2xxx/qla_mbx.c | 28 --- drivers/scsi/qla2xxx/qla_target.c |4 +- drivers/scsi/qla2xxx/tcm_qla2xxx.c |2 +- 7 files changed, 81 insertions(+), 31 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 1d2a51a..cf32fb9 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2917,7 +2917,7 @@ struct qlt_hw_data { #define MAX_QFULL_CMDS_ALLOC 8192 #define Q_FULL_THRESH_HOLD_PERCENT 90 #define Q_FULL_THRESH_HOLD(ha) \ - ((ha->fw_xcb_count/100) * Q_FULL_THRESH_HOLD_PERCENT) + ((ha->cur_fw_xcb_count/100) * Q_FULL_THRESH_HOLD_PERCENT) #define LEAK_EXCHG_THRESH_HOLD_PERCENT 75 /* 75 percent */ @@ -3298,8 +3298,14 @@ struct qla_hw_data { #define RISC_START_ADDRESS_2100 0x1000 #define RISC_START_ADDRESS_2300 0x800 #define RISC_START_ADDRESS_2400 0x10 - uint16_tfw_xcb_count; - uint16_tfw_iocb_count; + + uint16_torig_fw_tgt_xcb_count; + uint16_tcur_fw_tgt_xcb_count; + uint16_torig_fw_xcb_count; + uint16_tcur_fw_xcb_count; + uint16_torig_fw_iocb_count; + uint16_tcur_fw_iocb_count; + uint16_tfw_max_fcf_count; uint32_tfw_shared_ram_start; uint32_tfw_shared_ram_end; @@ -3343,6 +3349,7 @@ struct qla_hw_data { struct dentry *dfs_dir; struct dentry *dfs_fce; struct dentry *dfs_tgt_counters; + struct dentry *dfs_fw_resource_cnt; dma_addr_t fce_dma; void*fce; diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index 449541f..cd8b96a 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -13,6 +13,43 @@ static struct dentry *qla2x00_dfs_root; static atomic_t qla2x00_dfs_root_count; static int +qla_dfs_fw_resource_cnt_show(struct seq_file *s, void *unused) +{ + struct scsi_qla_host *vha = s->private; + struct qla_hw_data *ha = vha->hw; + + seq_puts(s, "FW Resource count\n\n"); + seq_printf(s, "Original TGT exchg count[%d]\n", + ha->orig_fw_tgt_xcb_count); + seq_printf(s, "current TGT exchg count[%d]\n", + ha->cur_fw_tgt_xcb_count); + seq_printf(s, "original Initiator Exchange count[%d]\n", + ha->orig_fw_xcb_count); + seq_printf(s, "Current Initiator Exchange count[%d]\n", + ha->cur_fw_xcb_count); + seq_printf(s, "Original IOCB count[%d]\n", ha->orig_fw_iocb_count); + seq_printf(s, "Current IOCB count[%d]\n", ha->cur_fw_iocb_count); + seq_printf(s, "MAX VP count[%d]\n", ha->max_npiv_vports); + seq_printf(s, "MAX FCF count[%d]\n", ha->fw_max_fcf_count); + + return 0; +} + +static int +qla_dfs_fw_resource_cnt_open(struct inode *inode, struct file *file) +{ + struct scsi_qla_host *vha = inode->i_private; + return single_open(file, qla_dfs_fw_resource_cnt_show, vha); +} + +static const struct file_operations dfs_fw_resource_cnt_ops = { + .open = qla_dfs_fw_resource_cnt_open, + .read = seq_read, + .llseek = seq_lseek, + .release= single_release, +}; + +static int qla_dfs_tgt_counters_show(struct seq_file *s, void *unused) { struct scsi_qla_host *vha = s->private; @@ -188,6 +225,14 @@ create_dir: atomic_inc(_dfs_root_count); create_nodes: + ha->dfs_fw_resource_cnt = debugfs_create_file("fw_resource_count", + S_IRUSR, ha->dfs_dir, vha, _fw_resource_cnt_ops); + if (!ha->dfs_fw_resource_cnt) { + ql_log(ql_log_warn, vha, 0x00fd, + "Unable to create debugFS fw_resource_count node.\n"); + goto out; + } + ha->dfs_tgt_counters = debugfs_create_file("tgt_counters", S_IRUSR, ha->dfs_dir, vha, _tgt_counters_ops); if (!ha->dfs_tgt_counters) { @@ -212,6 +257,11 @@ qla2x00_dfs_remove(scsi_qla_host_t *vha) { struct qla_hw_data *ha = vha->hw; + if (ha->dfs_fw_resource_cnt) { + debugfs_remove(ha->dfs_fw_resource_cnt); + ha->dfs_fw_resource_cnt = NULL; + } + if (ha->dfs_tgt_counters) {
[PATCH 01/20] qla2xxx: Enable Extended Login support
Signed-off-by: Himanshu MadhaniSigned-off-by: Giridhar Malavali --- drivers/scsi/qla2xxx/qla_dbg.c |9 +-- drivers/scsi/qla2xxx/qla_def.h | 13 - drivers/scsi/qla2xxx/qla_gbl.h |7 ++ drivers/scsi/qla2xxx/qla_init.c |7 ++ drivers/scsi/qla2xxx/qla_mbx.c | 119 +++ drivers/scsi/qla2xxx/qla_os.c | 76 + 6 files changed, 224 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 34dc9a3..5766640 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -14,7 +14,7 @@ * | Module Init and Probe| 0x017f | 0x0146 | * | || 0x015b-0x0160 | * | || 0x016e-0x0170 | - * | Mailbox commands | 0x118d | 0x1115-0x1116 | + * | Mailbox commands | 0x1192 | 0x1018-0x1019 | * | || 0x111a-0x111b | * | Device Discovery | 0x2016 | 0x2020-0x2022, | * | || 0x2011-0x2012, | @@ -60,10 +60,9 @@ * | || 0xb13c-0xb140 | * | || 0xb149 | * | MultiQ | 0xc00c | | - * | Misc | 0xd300 | 0xd016-0xd017 | - * | || 0xd021,0xd024 | - * | || 0xd025,0xd029 | - * | || 0xd02a,0xd02e | + * | Misc | 0xd300 | 0xd012-0xd014 | + * | || 0xd016-0xd017 | + * | || 0xd02e | * | || 0xd031-0xd0ff | * | || 0xd101-0xd1fe | * | || 0xd214-0xd2fe | diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 388d790..8f465b7 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -891,6 +891,7 @@ struct mbx_cmd_32 { #define MBC_DISABLE_VI 0x24/* Disable VI operation. */ #define MBC_ENABLE_VI 0x25/* Enable VI operation. */ #define MBC_GET_FIRMWARE_OPTION0x28/* Get Firmware Options. */ +#define MBC_GET_MEM_OFFLOAD_CNTRL_STAT 0x34/* Memory Offload ctrl/Stat*/ #define MBC_SET_FIRMWARE_OPTION0x38/* Set Firmware Options. */ #define MBC_LOOP_PORT_BYPASS 0x40/* Loop Port Bypass. */ #define MBC_LOOP_PORT_ENABLE 0x41/* Loop Port Enable. */ @@ -2962,11 +2963,12 @@ struct qla_hw_data { uint32_tisp82xx_no_md_cap:1; uint32_thost_shutting_down:1; uint32_tidc_compl_status:1; - uint32_tmr_reset_hdlr_active:1; uint32_tmr_intr_valid:1; + uint32_tfawwpn_enabled:1; - /* 35 bits */ + uint32_texlogins_enabled:1; + /* 34 bits */ } flags; /* This spinlock is used to protect "io transactions", you must @@ -3237,6 +3239,13 @@ struct qla_hw_data { void*async_pd; dma_addr_t async_pd_dma; +#define ENABLE_EXTENDED_LOGIN BIT_7 + + /* Extended Logins */ + void*exlogin_buf; + dma_addr_t exlogin_buf_dma; + int exlogin_size; + void*swl; /* These are used by mailbox operations. */ diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 7686bfe..d396c49 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -117,6 +117,7 @@ extern int ql2xdontresethba; extern uint64_t ql2xmaxlun; extern int ql2xmdcapmask; extern int ql2xmdenable; +extern int ql2xexlogins; extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); @@ -135,6 +136,8 @@ extern int qla2x00_post_async_adisc_work(struct scsi_qla_host *, fc_port_t *, uint16_t *); extern int qla2x00_post_async_adisc_done_work(struct scsi_qla_host *, fc_port_t *, uint16_t *); +extern int qla2x00_set_exlogins_buffer(struct scsi_qla_host *); +extern void qla2x00_free_exlogin_buffer(struct qla_hw_data *); extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *); @@ -766,4 +769,8 @@ extern int qla8044_abort_isp(scsi_qla_host_t *); extern int
[PATCH 20/20] qla2xxx: Check for online flag instead of active reset when transmitting responses
From: Dilip Kumar UppugandlaDriver has following initialization sequence for Target mode 1. Driver initialization starts 2. ISP Abort is scheduled when the target is enabled. qla2xxx [:04:00.0]-4807:25: ISP abort scheduled qla2xxx [:04:00.0]-00af:25: Performing ISP error recovery - ha=880caa9e. 3. DPC thread starts the ISP Abort 4. While DPC is resetting the chip and initializing the firmware, we get async events from the firmware about P2P mode, LOOP UP and PORT UPDATE. 5. PRLI from a initiator is delivered to us followed by a PLOGI and then a SCSI command which creates a session. 6. If the SCSI command is a WRITE in this case, we issue XFR RDY and it gets dropped as can be seen with messages RESET-XFR because ISP Abort is still active qla2xxx [:04:00.0]-e902:25: RESET-XFR active/old-count/new-count = 1/1/1. 7. If the SCSI command is a READ, we issue RESPONSE and they get dropped as well because Abort is still active. qla2xxx [:04:00.0]-e901:25: RESET-RSP active/old-count/new-count = 1/1/1 8. Now eventually, ISP Abort finishes clearing the DPC flags. qla2xxx [:04:00.0]-8822:25: qla2x00_abort_isp succeeded. qla2xxx [:04:00.0]-4808:25: ISP abort end. 9. Since we dropped SCSI commands silently (without any responses sent to the initiator) initiator waits for a SCSI timeout (which is 60 seconds in our case), Sends an ABTS which fails since there no se_cmd found for the tag that ABTS is referencing as the commands were cleaned up in Step 6 and 7. 10. Initiator send an IO after the ABTS which succeed fine. To fix the above case, the following changes have been made: - To prevent target from dropping commands silently, use the online flag instead to check for an active chip reset. Once the port is online during a chip reset phase, we are good to process the commands. - Clean up qla2x00_restart_isp to not set the online flag and process ATIO as it is unnecessary. During a chip reset, interrupts are enabled only after setting the online flag to 1, so ATIO's won't be missed and hence no need to process ATIO's after setting the online flag. Signed-off-by: Dilip Kumar Uppugandla Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_init.c | 34 -- drivers/scsi/qla2xxx/qla_target.c | 34 +- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 993dd25..52a8765 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -3065,6 +3065,26 @@ qla2x00_configure_loop(scsi_qla_host_t *vha) atomic_set(>loop_state, LOOP_READY); ql_dbg(ql_dbg_disc, vha, 0x2069, "LOOP READY.\n"); + + /* +* Process any ATIO queue entries that came in +* while we weren't online. +*/ + if (qla_tgt_mode_enabled(vha)) { + if (IS_QLA27XX(ha) || IS_QLA83XX(ha)) { + spin_lock_irqsave(>tgt.atio_lock, + flags); + qlt_24xx_process_atio_queue(vha, 0); + spin_unlock_irqrestore( + >tgt.atio_lock, flags); + } else { + spin_lock_irqsave(>hardware_lock, + flags); + qlt_24xx_process_atio_queue(vha, 1); + spin_unlock_irqrestore( + >hardware_lock, flags); + } + } } } @@ -4919,7 +4939,6 @@ qla2x00_restart_isp(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct req_que *req = ha->req_q_map[0]; struct rsp_que *rsp = ha->rsp_q_map[0]; - unsigned long flags, flags2; /* If firmware needs to be loaded */ if (qla2x00_isp_firmware(vha)) { @@ -4941,19 +4960,6 @@ qla2x00_restart_isp(scsi_qla_host_t *vha) /* Issue a marker after FW becomes ready. */ qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL); - vha->flags.online = 1; - - /* -* Process any ATIO queue entries that came in -* while we weren't online. -*/ - spin_lock_irqsave(>hardware_lock, flags); - spin_lock_irqsave(>tgt.atio_lock, flags2); - if
[PATCH 02/20] qla2xxx: Enable Exchange offload support.
Signed-off-by: Himanshu MadhaniSigned-off-by: Giridhar Malavali --- drivers/scsi/qla2xxx/qla_dbg.c |9 +-- drivers/scsi/qla2xxx/qla_def.h | 12 - drivers/scsi/qla2xxx/qla_gbl.h |6 ++ drivers/scsi/qla2xxx/qla_init.c |7 ++ drivers/scsi/qla2xxx/qla_mbx.c | 117 +++ drivers/scsi/qla2xxx/qla_os.c | 76 + 6 files changed, 220 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 5766640..e25f5ac 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -14,8 +14,8 @@ * | Module Init and Probe| 0x017f | 0x0146 | * | || 0x015b-0x0160 | * | || 0x016e-0x0170 | - * | Mailbox commands | 0x1192 | 0x1018-0x1019 | - * | || 0x111a-0x111b | + * | Mailbox commands | 0x1192 | | + * | || | * | Device Discovery | 0x2016 | 0x2020-0x2022, | * | || 0x2011-0x2012, | * | || 0x2099-0x20a4 | @@ -60,10 +60,7 @@ * | || 0xb13c-0xb140 | * | || 0xb149 | * | MultiQ | 0xc00c | | - * | Misc | 0xd300 | 0xd012-0xd014 | - * | || 0xd016-0xd017 | - * | || 0xd02e | - * | || 0xd031-0xd0ff | + * | Misc | 0xd300 | 0xd031-0xd0ff | * | || 0xd101-0xd1fe | * | || 0xd214-0xd2fe | * | Target Mode | 0xe080 || diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 8f465b7..be9a674 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -272,6 +272,7 @@ #define RESPONSE_ENTRY_CNT_MQ 128 /* Number of response entries.*/ #define ATIO_ENTRY_CNT_24XX4096/* Number of ATIO entries. */ #define RESPONSE_ENTRY_CNT_FX00256 /* Number of response entries.*/ +#define EXTENDED_EXCH_ENTRY_CNT32768 /* Reuqest Entries for offload case*/ struct req_que; struct qla_tgt_sess; @@ -2968,7 +2969,8 @@ struct qla_hw_data { uint32_tfawwpn_enabled:1; uint32_texlogins_enabled:1; - /* 34 bits */ + uint32_texchoffld_enabled:1; + /* 35 bits */ } flags; /* This spinlock is used to protect "io transactions", you must @@ -3246,6 +3248,14 @@ struct qla_hw_data { dma_addr_t exlogin_buf_dma; int exlogin_size; +#define ENABLE_EXCHANGE_OFFLD BIT_2 + + /* Exchange Offload */ + void*exchoffld_buf; + dma_addr_t exchoffld_buf_dma; + int exchoffld_size; + int exchoffld_count; + void*swl; /* These are used by mailbox operations. */ diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index d396c49..f9ebfcf 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -118,6 +118,7 @@ extern uint64_t ql2xmaxlun; extern int ql2xmdcapmask; extern int ql2xmdenable; extern int ql2xexlogins; +extern int ql2xexchoffld; extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); @@ -138,6 +139,8 @@ extern int qla2x00_post_async_adisc_done_work(struct scsi_qla_host *, fc_port_t *, uint16_t *); extern int qla2x00_set_exlogins_buffer(struct scsi_qla_host *); extern void qla2x00_free_exlogin_buffer(struct qla_hw_data *); +extern int qla2x00_set_exchoffld_buffer(struct scsi_qla_host *); +extern void qla2x00_free_exchoffld_buffer(struct qla_hw_data *); extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *); @@ -772,5 +775,8 @@ extern void qlt_host_reset_handler(struct qla_hw_data *ha); extern int qla_get_exlogin_status(scsi_qla_host_t *, uint16_t *, uint16_t *); extern int qla_set_exlogin_mem_cfg(scsi_qla_host_t *vha, dma_addr_t phys_addr); +extern int qla_get_exchoffld_status(scsi_qla_host_t *, uint16_t *, uint16_t *); +extern int qla_set_exchoffld_mem_cfg(scsi_qla_host_t *vha, +dma_addr_t phys_addr);
[PATCH 00/20] qla2xxx: Patches for target-pending branch
Hi Nic, This patch series adds performance improvement for qla2xxx target mode driver. Please apply this series to target-pending for next mainline merge window. Thanks, Himanshu Alexei Potashnik (2): qla2xxx: Delete session if initiator is gone from FW qla2xxx: Wait for all conflicts before ack'ing PLOGI Dilip Kumar Uppugandla (1): qla2xxx: Check for online flag instead of active reset when transmitting responses Himanshu Madhani (4): qla2xxx: Enable Extended Login support qla2xxx: Enable Exchange offload support. qla2xxx: Enable Target counters in DebugFS. qla2xxx: Added interface to send ELS commands from driver. Quinn Tran (13): qla2xxx: Add FW resource count in DebugFS. qla2xxx: Replace QLA_TGT_STATE_ABORTED with a bit. qla2xxx: Change check_stop_free to always return 1 qla2xxx: Fix interaction issue between qla2xxx and Target Core Module qla2xxx: Add TAS detection for kernel 3.15 n newer target/tmr: LUN reset cause cmd premature free. qla2xxx: Remove dependency on hardware_lock to reduce lock contention. qla2xxx: Add irq affinity notification qla2xxx: Add selective command queuing qla2xxx: Move atioq to a different lock to reduce lock contention qla2xxx: Disable ZIO at start time. qla2xxx: Set all queues to 4k qla2xxx: Add bulk send for atio & ctio completion paths. drivers/scsi/qla2xxx/qla_attr.c| 36 ++ drivers/scsi/qla2xxx/qla_dbg.c | 19 +- drivers/scsi/qla2xxx/qla_def.h | 86 - drivers/scsi/qla2xxx/qla_dfs.c | 106 + drivers/scsi/qla2xxx/qla_gbl.h | 19 +- drivers/scsi/qla2xxx/qla_init.c| 58 ++- drivers/scsi/qla2xxx/qla_inline.h |2 + drivers/scsi/qla2xxx/qla_iocb.c| 190 drivers/scsi/qla2xxx/qla_isr.c | 129 +- drivers/scsi/qla2xxx/qla_mbx.c | 264 +++- drivers/scsi/qla2xxx/qla_os.c | 163 +++- drivers/scsi/qla2xxx/qla_target.c | 737 +--- drivers/scsi/qla2xxx/qla_target.h | 95 +++-- drivers/scsi/qla2xxx/tcm_qla2xxx.c | 205 - drivers/target/target_core_tmr.c | 33 ++- drivers/target/target_core_transport.c | 35 ++- include/target/target_core_base.h |2 + 17 files changed, 1894 insertions(+), 285 deletions(-) -- 1.7.7 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 17/20] qla2xxx: Disable ZIO at start time.
From: Quinn TranSigned-off-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_target.c | 20 1 files changed, 20 insertions(+), 0 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 69f88d3..f8f2b8a 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -6563,10 +6563,21 @@ qlt_24xx_config_nvram_stage2(struct scsi_qla_host *vha, { struct qla_hw_data *ha = vha->hw; + if (!QLA_TGT_MODE_ENABLED()) + return; + if (ha->tgt.node_name_set) { memcpy(icb->node_name, ha->tgt.tgt_node_name, WWN_SIZE); icb->firmware_options_1 |= cpu_to_le32(BIT_14); } + + /* disable ZIO at start time. */ + if (!vha->flags.init_done) { + uint32_t tmp; + tmp = le32_to_cpu(icb->firmware_options_2); + tmp &= ~(BIT_3 | BIT_2 | BIT_1 | BIT_0); + icb->firmware_options_2 = cpu_to_le32(tmp); + } } void @@ -6657,6 +6668,15 @@ qlt_81xx_config_nvram_stage2(struct scsi_qla_host *vha, memcpy(icb->node_name, ha->tgt.tgt_node_name, WWN_SIZE); icb->firmware_options_1 |= cpu_to_le32(BIT_14); } + + /* disable ZIO at start time. */ + if (!vha->flags.init_done) { + uint32_t tmp; + tmp = le32_to_cpu(icb->firmware_options_2); + tmp &= ~(BIT_3 | BIT_2 | BIT_1 | BIT_0); + icb->firmware_options_2 = cpu_to_le32(tmp); + } + } void -- 1.7.7 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 12/20] target/tmr: LUN reset cause cmd premature free.
From: Quinn TranDuring LUN/Target reset, the TMR code attempt to intercept cmds and try to aborted them. Current code assume cmds are always intercepted at the back end device. The cleanup code would issue a "queue_status() & check_stop_free()" to terminate the command. However, when a cmd is intercepted at the front end/Fabric layer, current code introduce premature free or cause Fabric to double free. When command is intercepted at Fabric layer, it means a check_stop_free(cmd_kref--) has been called. The extra check_stop_free in the Lun Reset cleanup code causes early free. When a cmd in the Fabric layer is completed, the normal free code adds another another free which introduce a double free. To fix the issue: - add a new flag/CMD_T_SENT_STATUS to track command that have made it down to fabric layer after back end good/bad completion. - if cmd reach Fabric Layer at Lun Reset time, add an extra cmd_kref count to prevent premature free. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/target/target_core_tmr.c | 33 +++- drivers/target/target_core_transport.c | 30 + include/target/target_core_base.h |1 + 3 files changed, 63 insertions(+), 1 deletions(-) diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 28fb301..41f8b57 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -243,7 +243,9 @@ static void core_tmr_drain_state_list( { LIST_HEAD(drain_task_list); struct se_cmd *cmd, *next; - unsigned long flags; + unsigned long flags, flags2; + int rmkref; + struct se_session *se_sess; /* * Complete outstanding commands with TASK_ABORTED SAM status. @@ -282,6 +284,16 @@ static void core_tmr_drain_state_list( if (prout_cmd == cmd) continue; + se_sess = cmd->se_sess; + /* take an extra kref to prevent cmd free race condition. */ + spin_lock_irqsave(_sess->sess_cmd_lock, flags2); + if (!kref_get_unless_zero(>cmd_kref)) { + /* cmd is already in the free process */ + spin_unlock_irqrestore(_sess->sess_cmd_lock, flags2); + continue; + } + spin_unlock_irqrestore(_sess->sess_cmd_lock, flags2); + list_move_tail(>state_list, _task_list); cmd->state_active = false; } @@ -320,9 +332,28 @@ static void core_tmr_drain_state_list( target_stop_cmd(cmd, ); cmd->transport_state |= CMD_T_ABORTED; + + /* CMD_T_SENT_STATUS: cmd is down in fabric layer. +* A check stop has been called. Keep the extra kref +* from above because core_tmr_handle_tas_abort will +* generate another check_stop. +* +* !CMD_T_SENT_STATUS: cmd intercepted at back end. +* Remove the extra kref from above because only +* 1 check_stop is required or generated by +* core_tmr_handle_tas_abort() +*/ + rmkref = 0; + if (!((cmd->t_state == TRANSPORT_COMPLETE) && + (cmd->transport_state & CMD_T_SENT_STATUS))) + rmkref = 1; + spin_unlock_irqrestore(>t_state_lock, flags); core_tmr_handle_tas_abort(tmr_nacl, cmd, tas); + + if (rmkref) + target_put_sess_cmd(cmd); } } diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 4fdcee2..cdd18bf 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -639,9 +639,14 @@ void transport_cmd_finish_abort(struct se_cmd *cmd, int remove) static void target_complete_failure_work(struct work_struct *work) { struct se_cmd *cmd = container_of(work, struct se_cmd, work); + unsigned long flags; transport_generic_request_failure(cmd, TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE); + + spin_lock_irqsave(>t_state_lock, flags); + cmd->transport_state |= CMD_T_SENT_STATUS; + spin_unlock_irqrestore(>t_state_lock, flags); } /* @@ -1659,6 +1664,7 @@ void transport_generic_request_failure(struct se_cmd *cmd, sense_reason_t sense_reason) { int ret = 0, post_ret = 0; + unsigned long flags; pr_debug("-[ Storage Engine Exception for cmd: %p ITT: 0x%08llx" " CDB: 0x%02x\n", cmd, cmd->tag, cmd->t_task_cdb[0]); @@ -1670,6 +1676,10 @@ void transport_generic_request_failure(struct se_cmd *cmd, (cmd->transport_state & CMD_T_STOP) != 0,
[PATCH 15/20] qla2xxx: Add selective command queuing
From: Quinn Tranqueue work element to specific process lessen cache miss Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_isr.c |2 +- drivers/scsi/qla2xxx/qla_target.c | 13 - drivers/scsi/qla2xxx/tcm_qla2xxx.c |4 ++-- drivers/target/target_core_transport.c |5 - include/target/target_core_base.h |1 + 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index e879802d..26fa4d8 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -3302,7 +3302,7 @@ static void qla_irq_affinity_notify(struct irq_affinity_notify *notify, } } -void qla_irq_affinity_release(struct kref *ref) +static void qla_irq_affinity_release(struct kref *ref) { struct irq_affinity_notify *notify = container_of(ref, struct irq_affinity_notify, kref); diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index dc6ac4e..b33c018 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -4004,13 +4004,24 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, cmd->cmd_in_wq = 1; cmd->cmd_flags |= BIT_0; + cmd->se_cmd.cpuid = -1; spin_lock(>cmd_list_lock); list_add_tail(>cmd_list, >qla_cmd_list); spin_unlock(>cmd_list_lock); INIT_WORK(>work, qlt_do_work); - queue_work(qla_tgt_wq, >work); + if (ha->msix_count) { + cmd->se_cmd.cpuid = ha->tgt.rspq_vector_cpuid; + if (cmd->atio.u.isp24.fcp_cmnd.rddata) + queue_work_on(smp_processor_id(), qla_tgt_wq, + >work); + else + queue_work_on(cmd->se_cmd.cpuid, qla_tgt_wq, + >work); + } else { + queue_work(qla_tgt_wq, >work); + } return 0; } diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 833dec2..d7a34d1 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -303,7 +303,7 @@ static void tcm_qla2xxx_free_cmd(struct qla_tgt_cmd *cmd) cmd->cmd_flags |= BIT_20; INIT_WORK(>work, tcm_qla2xxx_complete_free); - queue_work(tcm_qla2xxx_free_wq, >work); + queue_work_on(smp_processor_id(), tcm_qla2xxx_free_wq, >work); } /* @@ -535,7 +535,7 @@ static void tcm_qla2xxx_handle_data(struct qla_tgt_cmd *cmd) cmd->cmd_flags |= BIT_10; cmd->cmd_in_wq = 1; INIT_WORK(>work, tcm_qla2xxx_handle_data_work); - queue_work(tcm_qla2xxx_free_wq, >work); + queue_work_on(smp_processor_id(), tcm_qla2xxx_free_wq, >work); } static void tcm_qla2xxx_handle_dif_work(struct work_struct *work) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index cdd18bf..a37db6d 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -720,7 +720,10 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status) cmd->transport_state |= (CMD_T_COMPLETE | CMD_T_ACTIVE); spin_unlock_irqrestore(>t_state_lock, flags); - queue_work(target_completion_wq, >work); + if (cmd->cpuid == -1) + queue_work(target_completion_wq, >work); + else + queue_work_on(cmd->cpuid, target_completion_wq, >work); } EXPORT_SYMBOL(target_complete_cmd); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index efccd71..ce26a3a 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -527,6 +527,7 @@ struct se_cmd { unsigned intt_prot_nents; sense_reason_t pi_err; sector_tbad_sector; + int cpuid; }; struct se_ua { -- 1.7.7 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 11/20] qla2xxx: Add TAS detection for kernel 3.15 n newer
From: Quinn TranFor kernel 3.15 and newer with TCM API change, add detection for TCM support of TAS. Instead of default command terminate for LUN/TARGET reset error handling, allow SCSI status to go out if we know sequece Initiative is own by FW (cmd_sent_to_fw=0) Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_target.c | 36 +--- drivers/scsi/qla2xxx/tcm_qla2xxx.c | 20 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 4d42b79..5fca846 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -3239,36 +3239,34 @@ int qlt_abort_cmd(struct qla_tgt_cmd *cmd) struct qla_tgt *tgt = cmd->tgt; struct scsi_qla_host *vha = tgt->vha; struct se_cmd *se_cmd = >se_cmd; - unsigned long flags,refcount; + unsigned long flags, refcount; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf014, "qla_target(%d): terminating exchange for aborted cmd=%p " "(se_cmd=%p, tag=%llu)", vha->vp_idx, cmd, >se_cmd, se_cmd->tag); -spin_lock_irqsave(>cmd_lock, flags); -if (cmd->aborted) { -spin_unlock_irqrestore(>cmd_lock, flags); + spin_lock_irqsave(>cmd_lock, flags); -/* It's normal to see 2 calls in this path: - * 1) XFER Rdy completion + CMD_T_ABORT - * 2) TCM TMR - drain_state_list - */ -refcount = atomic_read(>se_cmd.cmd_kref.refcount); -ql_dbg(ql_dbg_tgt_mgt, vha, 0x, - "multiple abort. %p refcount %lx" - "transport_state %x, t_state %x, se_cmd_flags %x \n", - cmd, refcount,cmd->se_cmd.transport_state, - cmd->se_cmd.t_state,cmd->se_cmd.se_cmd_flags); + if (cmd->aborted) { + spin_unlock_irqrestore(>cmd_lock, flags); + /* It's normal to see 2 calls in this path: +* 1) XFER Rdy completion + CMD_T_ABORT +* 2) TCM TMR - drain_state_list +*/ + refcount = atomic_read(>se_cmd.cmd_kref.refcount); + ql_dbg(ql_dbg_tgt_mgt, vha, 0x, + "multiple abort. %p refcount %lx" + "transport_state %x, t_state %x, se_cmd_flags %x \n", + cmd, refcount, cmd->se_cmd.transport_state, + cmd->se_cmd.t_state, cmd->se_cmd.se_cmd_flags); -return EIO; -} + return EIO; + } cmd->aborted = 1; cmd->cmd_flags |= BIT_6; -spin_unlock_irqrestore(>cmd_lock, flags); - - qlt_send_term_exchange(vha, cmd, >atio, 0, 1); + spin_unlock_irqrestore(>cmd_lock, flags); return 0; } diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 842fcca..2e9c194 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -617,6 +617,26 @@ static int tcm_qla2xxx_queue_status(struct se_cmd *se_cmd) struct qla_tgt_cmd, se_cmd); int xmit_type = QLA_TGT_XMIT_STATUS; + if (se_cmd->transport_state & CMD_T_ABORTED) { + /* For TCM TAS support n kernel >= 3.15: +* This cmd is attempting to respond with "Task Aborted Status". +*/ + if (cmd->aborted) { + return 0; + } else if ((cmd->state == QLA_TGT_STATE_NEED_DATA) && + cmd->cmd_sent_to_fw) { + qlt_abort_cmd(cmd); + return 0; + } else if (cmd->state == QLA_TGT_STATE_PROCESSED) { + if (cmd->cmd_sent_to_fw) { + qlt_abort_cmd(cmd); + return 0; + } else {/* about to be free */ + return 0; + } + } + } + cmd->bufflen = se_cmd->data_length; cmd->sg = NULL; cmd->sg_cnt = 0; -- 1.7.7 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 16/20] qla2xxx: Move atioq to a different lock to reduce lock contention
From: Quinn Tran99% of the time the ATIOQ has SCSI command. The other 1% of time is something else. Most of the time this interrupt does not need to hold the hardware_lock. We're moving the ATIO interrupt thread to a different lock to reduce lock contention. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_def.h|1 + drivers/scsi/qla2xxx/qla_gbl.h|1 + drivers/scsi/qla2xxx/qla_init.c |6 ++- drivers/scsi/qla2xxx/qla_isr.c| 44 + drivers/scsi/qla2xxx/qla_os.c |2 + drivers/scsi/qla2xxx/qla_target.c | 97 +++-- drivers/scsi/qla2xxx/qla_target.h |4 +- 7 files changed, 127 insertions(+), 28 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 9165732..0ddb2de 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2936,6 +2936,7 @@ struct qlt_hw_data { uint32_t leak_exchg_thresh_hold; spinlock_t sess_lock; int rspq_vector_cpuid; + spinlock_t atio_lock cacheline_aligned; }; #define MAX_QFULL_CMDS_ALLOC 8192 diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 2e91966..5496e0a 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -779,5 +779,6 @@ extern int qla_set_exlogin_mem_cfg(scsi_qla_host_t *vha, dma_addr_t phys_addr); extern int qla_get_exchoffld_status(scsi_qla_host_t *, uint16_t *, uint16_t *); extern int qla_set_exchoffld_mem_cfg(scsi_qla_host_t *vha, dma_addr_t phys_addr); +extern void qlt_handle_abts_recv(struct scsi_qla_host *, response_t *); #endif /* _QLA_GBL_H */ diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 35d1ea8..993dd25 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -4919,7 +4919,7 @@ qla2x00_restart_isp(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct req_que *req = ha->req_q_map[0]; struct rsp_que *rsp = ha->rsp_q_map[0]; - unsigned long flags; + unsigned long flags, flags2; /* If firmware needs to be loaded */ if (qla2x00_isp_firmware(vha)) { @@ -4948,8 +4948,10 @@ qla2x00_restart_isp(scsi_qla_host_t *vha) * while we weren't online. */ spin_lock_irqsave(>hardware_lock, flags); + spin_lock_irqsave(>tgt.atio_lock, flags2); if (qla_tgt_mode_enabled(vha)) - qlt_24xx_process_atio_queue(vha); + qlt_24xx_process_atio_queue(vha, 1); + spin_unlock_irqrestore(>tgt.atio_lock, flags2); spin_unlock_irqrestore(>hardware_lock, flags); set_bit(LOOP_RESYNC_NEEDED, >dpc_flags); diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 26fa4d8..72d1cdc 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -2605,8 +2605,14 @@ process_err: qla24xx_els_ct_entry(vha, rsp->req, pkt, ELS_IOCB_TYPE); break; case ABTS_RECV_24XX: - /* ensure that the ATIO queue is empty */ - qlt_24xx_process_atio_queue(vha); + if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) { + /* ensure that the ATIO queue is empty */ + qlt_handle_abts_recv(vha, (response_t *)pkt); + break; + } else { + /* drop through */ + qlt_24xx_process_atio_queue(vha, 1); + } case ABTS_RESP_24XX: case CTIO_TYPE7: case NOTIFY_ACK_TYPE: @@ -2773,13 +2779,22 @@ qla24xx_intr_handler(int irq, void *dev_id) case INTR_RSP_QUE_UPDATE_83XX: qla24xx_process_response_queue(vha, rsp); break; - case INTR_ATIO_QUE_UPDATE: - qlt_24xx_process_atio_queue(vha); + case INTR_ATIO_QUE_UPDATE:{ + unsigned long flags2; + spin_lock_irqsave(>tgt.atio_lock, flags2); + qlt_24xx_process_atio_queue(vha, 1); + spin_unlock_irqrestore(>tgt.atio_lock, flags2); break; - case INTR_ATIO_RSP_QUE_UPDATE: - qlt_24xx_process_atio_queue(vha); + } + case INTR_ATIO_RSP_QUE_UPDATE: { + unsigned long flags2; + spin_lock_irqsave(>tgt.atio_lock, flags2); +
[PATCH 08/20] qla2xxx: Replace QLA_TGT_STATE_ABORTED with a bit.
From: Quinn TranReplace QLA_TGT_STATE_ABORTED state with a bit because the current state of the command is lost when an abort is requested by upper layer. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_target.c | 23 ++- drivers/scsi/qla2xxx/qla_target.h |3 ++- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 57b4294..638940f 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -1468,7 +1468,7 @@ static int abort_cmd_for_tag(struct scsi_qla_host *vha, uint32_t tag) list_for_each_entry(cmd, >qla_cmd_list, cmd_list) { if (tag == cmd->atio.u.isp24.exchange_addr) { - cmd->state = QLA_TGT_STATE_ABORTED; + cmd->aborted = 1; spin_unlock(>cmd_list_lock); return 1; } @@ -1510,7 +1510,7 @@ static void abort_cmds_for_lun(struct scsi_qla_host *vha, cmd_lun = scsilun_to_int( (struct scsi_lun *)>atio.u.isp24.fcp_cmnd.lun); if (cmd_key == key && cmd_lun == lun) - cmd->state = QLA_TGT_STATE_ABORTED; + cmd->aborted = 1; } spin_unlock(>cmd_list_lock); } @@ -3174,7 +3174,7 @@ static void qlt_send_term_exchange(struct scsi_qla_host *vha, qlt_alloc_qfull_cmd(vha, atio, 0, 0); done: - if (cmd && ((cmd->state != QLA_TGT_STATE_ABORTED) || + if (cmd && (!cmd->aborted || !cmd->cmd_sent_to_fw)) { if (cmd->sg_mapped) qlt_unmap_sg(vha, cmd); @@ -3245,7 +3245,7 @@ void qlt_abort_cmd(struct qla_tgt_cmd *cmd) "(se_cmd=%p, tag=%llu)", vha->vp_idx, cmd, >se_cmd, se_cmd->tag); - cmd->state = QLA_TGT_STATE_ABORTED; + cmd->aborted = 1; cmd->cmd_flags |= BIT_6; qlt_send_term_exchange(vha, cmd, >atio, 0); @@ -3465,9 +3465,6 @@ qlt_abort_cmd_on_host_reset(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd) ha->tgt.tgt_ops->handle_data(cmd); return; - } else if (cmd->state == QLA_TGT_STATE_ABORTED) { - ql_dbg(ql_dbg_io, vha, 0xff02, - "HOST-ABORT: handle=%d, state=ABORTED.\n", handle); } else { ql_dbg(ql_dbg_io, vha, 0xff03, "HOST-ABORT: handle=%d, state=BAD(%d).\n", handle, @@ -3632,14 +3629,14 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, } - /* "cmd->state == QLA_TGT_STATE_ABORTED" means + /* "cmd->aborted" means * cmd is already aborted/terminated, we don't * need to terminate again. The exchange is already * cleaned up/freed at FW level. Just cleanup at driver * level. */ if ((cmd->state != QLA_TGT_STATE_NEED_DATA) && - (cmd->state != QLA_TGT_STATE_ABORTED)) { + (!cmd->aborted)) { cmd->cmd_flags |= BIT_13; if (qlt_term_ctio_exchange(vha, ctio, cmd, status)) return; @@ -3657,7 +3654,7 @@ skip_term: ha->tgt.tgt_ops->handle_data(cmd); return; - } else if (cmd->state == QLA_TGT_STATE_ABORTED) { + } else if (cmd->aborted) { cmd->cmd_flags |= BIT_18; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01e, "Aborted command %p (tag %lld) finished\n", cmd, se_cmd->tag); @@ -3669,7 +3666,7 @@ skip_term: } if (unlikely(status != CTIO_SUCCESS) && - (cmd->state != QLA_TGT_STATE_ABORTED)) { + !cmd->aborted) { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01f, "Finishing failed CTIO\n"); dump_stack(); } @@ -3731,7 +3728,7 @@ static void __qlt_do_work(struct qla_tgt_cmd *cmd) if (tgt->tgt_stop) goto out_term; - if (cmd->state == QLA_TGT_STATE_ABORTED) { + if (cmd->aborted) { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf082, "cmd with tag %u is aborted\n", cmd->atio.u.isp24.exchange_addr); @@ -4289,7 +4286,7 @@ static int abort_cmds_for_s_id(struct scsi_qla_host *vha, port_id_t *s_id) list_for_each_entry(cmd, >qla_cmd_list, cmd_list) { uint32_t cmd_key = sid_to_key(cmd->atio.u.isp24.fcp_hdr.s_id); if (cmd_key == key) { - cmd->state = QLA_TGT_STATE_ABORTED; + cmd->aborted = 1; count++; } } diff --git
[PATCH 13/20] qla2xxx: Remove dependency on hardware_lock to reduce lock contention.
From: Quinn TranSessions management (add, deleted, modify) currently are serialized through the hardware_lock. Hardware_lock is a high traffic lock. This lock is accessed by both the transmit & receive sides. Sessions management is now moved off to another lock call sess_lock. This is done to reduce lock contention and increase traffic throughput. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani --- drivers/scsi/qla2xxx/qla_def.h |1 + drivers/scsi/qla2xxx/qla_os.c |1 + drivers/scsi/qla2xxx/qla_target.c | 131 drivers/scsi/qla2xxx/tcm_qla2xxx.c | 28 4 files changed, 105 insertions(+), 56 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 46abba8..7c6369b 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2929,6 +2929,7 @@ struct qlt_hw_data { uint32_t num_qfull_cmds_dropped; spinlock_t q_full_lock; uint32_t leak_exchg_thresh_hold; + spinlock_t sess_lock; }; #define MAX_QFULL_CMDS_ALLOC 8192 diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index eb47272..8b45642 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -2336,6 +2336,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->tgt.enable_class_2 = ql2xenableclass2; INIT_LIST_HEAD(>tgt.q_full_list); spin_lock_init(>tgt.q_full_lock); + spin_lock_init(>tgt.sess_lock); /* Clear our data area */ ha->bars = bars; diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 5fca846..d4b7e25 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -635,7 +635,7 @@ static void qlt_free_session_done(struct work_struct *work) wake_up_all(>waitQ); } -/* ha->hardware_lock supposed to be held on entry */ +/* ha->tgt.sess_lock supposed to be held on entry */ void qlt_unreg_sess(struct qla_tgt_sess *sess) { struct scsi_qla_host *vha = sess->vha; @@ -651,7 +651,7 @@ void qlt_unreg_sess(struct qla_tgt_sess *sess) } EXPORT_SYMBOL(qlt_unreg_sess); -/* ha->hardware_lock supposed to be held on entry */ + static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd) { struct qla_hw_data *ha = vha->hw; @@ -661,12 +661,15 @@ static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd) int res = 0; struct imm_ntfy_from_isp *n = (struct imm_ntfy_from_isp *)iocb; struct atio_from_isp *a = (struct atio_from_isp *)iocb; + unsigned long flags; loop_id = le16_to_cpu(n->u.isp24.nport_handle); if (loop_id == 0x) { /* Global event */ atomic_inc(>vha_tgt.qla_tgt->tgt_global_resets_count); + spin_lock_irqsave(>tgt.sess_lock, flags); qlt_clear_tgt_db(vha->vha_tgt.qla_tgt); + spin_unlock_irqrestore(>tgt.sess_lock, flags); #if 0 /* FIXME: do we need to choose a session here? */ if (!list_empty(>tgt.qla_tgt->sess_list)) { sess = list_entry(ha->tgt.qla_tgt->sess_list.next, @@ -693,7 +696,9 @@ static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd) sess = NULL; #endif } else { + spin_lock_irqsave(>tgt.sess_lock, flags); sess = ha->tgt.tgt_ops->find_sess_by_loop_id(vha, loop_id); + spin_unlock_irqrestore(>tgt.sess_lock, flags); } ql_dbg(ql_dbg_tgt, vha, 0xe000, @@ -715,7 +720,7 @@ static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd) iocb, QLA24XX_MGMT_SEND_NACK); } -/* ha->hardware_lock supposed to be held on entry */ +/* ha->tgt.sess_lock supposed to be held on entry */ static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess, bool immediate) { @@ -759,7 +764,7 @@ static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess, sess->expires - jiffies); } -/* ha->hardware_lock supposed to be held on entry */ +/* ha->tgt.sess_lock supposed to be held on entry */ static void qlt_clear_tgt_db(struct qla_tgt *tgt) { struct qla_tgt_sess *sess; @@ -819,7 +824,7 @@ out_free_id_list: return res; } -/* ha->hardware_lock supposed to be held on entry */ +/* ha->tgt.sess_lock supposed to be held on entry */ static void qlt_undelete_sess(struct qla_tgt_sess *sess) { BUG_ON(sess->deleted != QLA_SESS_DELETION_PENDING); @@ -837,7 +842,7 @@ static void qlt_del_sess_work_fn(struct delayed_work *work) struct qla_tgt_sess *sess; unsigned long flags, elapsed; - spin_lock_irqsave(>hardware_lock, flags); + spin_lock_irqsave(>tgt.sess_lock, flags); while
Re: [PATCH 06/20] qla2xxx: Delete session if initiator is gone from FW
Hi Alexei, [auto build test WARNING on target/master] [also build test WARNING on v4.4-rc4 next-20151207] [cannot apply to scsi/for-next] url: https://github.com/0day-ci/linux/commits/Himanshu-Madhani/qla2xxx-Patches-for-target-pending-branch/20151208-093328 base: https://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending.git master config: i386-randconfig-x007-12070758 (attached as .config) reproduce: # save the attached .config to linux build tree make ARCH=i386 All warnings (new ones prefixed by >>): drivers/scsi/qla2xxx/qla_target.c: In function 'qlt_do_ctio_completion': >> drivers/scsi/qla2xxx/qla_target.c:3465:20: warning: comparison of constant >> '41' with boolean expression is always false [-Wbool-compare] (logged_out == CTIO_PORT_LOGGED_OUT) ? ^ vim +/41 +3465 drivers/scsi/qla2xxx/qla_target.c 3449 /* They are OK */ 3450 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf058, 3451 "qla_target(%d): CTIO with " 3452 "status %#x received, state %x, se_cmd %p, " 3453 "(LIP_RESET=e, ABORTED=2, TARGET_RESET=17, " 3454 "TIMEOUT=b, INVALID_RX_ID=8)\n", vha->vp_idx, 3455 status, cmd->state, se_cmd); 3456 break; 3457 3458 case CTIO_PORT_LOGGED_OUT: 3459 case CTIO_PORT_UNAVAILABLE: 3460 { 3461 bool logged_out = (status & 0x); 3462 ql_dbg(ql_dbg_tgt_mgt, vha, 0xf059, 3463 "qla_target(%d): CTIO with %s status %x " 3464 "received (state %x, se_cmd %p)\n", vha->vp_idx, > 3465 (logged_out == CTIO_PORT_LOGGED_OUT) ? 3466 "PORT LOGGED OUT" : "PORT UNAVAILABLE", 3467 status, cmd->state, se_cmd); 3468 3469 if (logged_out && cmd->sess) { 3470 /* 3471 * Session is already logged out, but we need 3472 * to notify initiator, who's not aware of this 3473 */ --- 0-DAY kernel test infrastructureOpen Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation .config.gz Description: Binary data
Re: [PATCH v2 74/71] ncr5380: Enable PDMA for NCR53C400A
On Sun, 6 Dec 2015, Ondrej Zary wrote: > Add I/O register mapping for NCR53C400A and enable PDMA mode to > improve performance and fix non-working IRQ. > > Tested with HP C2502 (and user-space enabler). > > Signed-off-by: Ondrej Zary> --- > drivers/scsi/g_NCR5380.c | 14 +++--- > 1 file changed, 11 insertions(+), 3 deletions(-) > > diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c > index 86740fd..099fdac 100644 > --- a/drivers/scsi/g_NCR5380.c > +++ b/drivers/scsi/g_NCR5380.c > @@ -324,7 +324,7 @@ static int __init generic_NCR5380_detect(struct > scsi_host_template *tpnt) > #endif > break; > case BOARD_NCR53C400A: > - flags = FLAG_NO_PSEUDO_DMA; > + flags = FLAG_NO_DMA_FIXUP; > ports = ncr_53c400a_ports; > break; > case BOARD_DTC3181E: > @@ -406,11 +406,18 @@ static int __init generic_NCR5380_detect(struct > scsi_host_template *tpnt) >* On NCR53C400 boards, NCR5380 registers are mapped 8 past >* the base address. >*/ > - if (overrides[current_override].board == BOARD_NCR53C400) { > + switch (overrides[current_override].board) { > + case BOARD_NCR53C400: > instance->io_port += 8; > hostdata->c400_ctl_status = 0; > hostdata->c400_blk_cnt = 1; > hostdata->c400_host_buf = 4; > + break; > + case BOARD_NCR53C400A: > + hostdata->c400_ctl_status = 9; > + hostdata->c400_blk_cnt = 10; > + hostdata->c400_host_buf = 8; > + break; > } > #else > instance->base = overrides[current_override].NCR5380_map_name; For SCSI_G_NCR5380_MEM and BOARD_NCR53C400A (or BOARD_DTC3181E), you have not assigned c400_ctl_status, c400_blk_cnt and c400_host_buf. Perhaps we should throw an error, something like this? hostdata->iomem = iomem; - if (overrides[current_override].board == BOARD_NCR53C400) { + switch (overrides[current_override].board) { + case BOARD_NCR53C400: hostdata->c400_ctl_status = 0x100; hostdata->c400_blk_cnt = 0x101; hostdata->c400_host_buf = 0x104; + break; + case BOARD_NCR53C400A: + pr_err(DRV_MODULE_NAME ": unknown register offsets\n"); + goto out_unregister; } #endif -- > @@ -425,7 +432,8 @@ static int __init generic_NCR5380_detect(struct > scsi_host_template *tpnt) > if (NCR5380_init(instance, flags)) > goto out_unregister; > > - if (overrides[current_override].board == BOARD_NCR53C400) > + if (overrides[current_override].board == BOARD_NCR53C400 || > + overrides[current_override].board == BOARD_NCR53C400A) > NCR5380_write(hostdata->c400_ctl_status, CSR_BASE); > > NCR5380_maybe_reset_bus(instance); > -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html