Skip to content

Commit

Permalink
cciss: clean up interrupt handler
Browse files Browse the repository at this point in the history
Simplify the interrupt handler code to more closely match hpsa and to
hopefully make it easier to follow.

Signed-off-by: Mike Miller <mike.miller@hp.com>
Cc: Stephen M. Cameron <scameron@beardog.cce.hp.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
  • Loading branch information
Mike Miller authored and Jens Axboe committed Aug 7, 2010
1 parent 664a717 commit 0c2b390
Showing 1 changed file with 141 additions and 95 deletions.
236 changes: 141 additions & 95 deletions drivers/block/cciss.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ static DEFINE_MUTEX(scan_mutex);
static LIST_HEAD(scan_q);

static void do_cciss_request(struct request_queue *q);
static irqreturn_t do_cciss_intr(int irq, void *dev_id);
static irqreturn_t do_cciss_intx(int irq, void *dev_id);
static irqreturn_t do_cciss_msix_intr(int irq, void *dev_id);
static int cciss_open(struct block_device *bdev, fmode_t mode);
static int cciss_release(struct gendisk *disk, fmode_t mode);
static int cciss_ioctl(struct block_device *bdev, fmode_t mode,
Expand Down Expand Up @@ -197,7 +198,6 @@ static int sendcmd_withirq_core(ctlr_info_t *h, CommandList_struct *c,
int attempt_retry);
static int process_sendcmd_error(ctlr_info_t *h, CommandList_struct *c);

static void fail_all_cmds(unsigned long ctlr);
static int add_to_scan_list(struct ctlr_info *h);
static int scan_thread(void *data);
static int check_for_unit_attention(ctlr_info_t *h, CommandList_struct *c);
Expand Down Expand Up @@ -3124,6 +3124,34 @@ static inline void complete_command(ctlr_info_t *h, CommandList_struct *cmd,
blk_complete_request(cmd->rq);
}

static inline u32 cciss_tag_contains_index(u32 tag)
{
#define DIRECT_LOOKUP_BIT 0x04
return tag & DIRECT_LOOKUP_BIT;
}

static inline u32 cciss_tag_to_index(u32 tag)
{
#define DIRECT_LOOKUP_SHIFT 3
return tag >> DIRECT_LOOKUP_SHIFT;
}

static inline u32 cciss_tag_discard_error_bits(u32 tag)
{
#define CCISS_ERROR_BITS 0x03
return tag & ~CCISS_ERROR_BITS;
}

static inline void cciss_mark_tag_indexed(u32 *tag)
{
*tag |= DIRECT_LOOKUP_BIT;
}

static inline void cciss_set_tag_index(u32 *tag, u32 index)
{
*tag |= (index << DIRECT_LOOKUP_SHIFT);
}

/*
* Get a request and submit it to the controller.
*/
Expand Down Expand Up @@ -3172,8 +3200,8 @@ static void do_cciss_request(struct request_queue *q)
/* got command from pool, so use the command block index instead */
/* for direct lookups. */
/* The first 2 bits are reserved for controller error reporting. */
c->Header.Tag.lower = (c->cmdindex << 3);
c->Header.Tag.lower |= 0x04; /* flag for direct lookup. */
cciss_set_tag_index(&c->Header.Tag.lower, c->cmdindex);
cciss_mark_tag_indexed(&c->Header.Tag.lower);
memcpy(&c->Header.LUN, drv->LunID, sizeof(drv->LunID));
c->Request.CDBLen = 10; /* 12 byte commands not in FW yet; */
c->Request.Type.Type = TYPE_CMD; /* It is a command. */
Expand Down Expand Up @@ -3306,15 +3334,73 @@ static inline int interrupt_pending(ctlr_info_t *h)
static inline long interrupt_not_for_us(ctlr_info_t *h)
{
return (((h->access.intr_pending(h) == 0) ||
(h->interrupts_enabled == 0)));
(h->interrupts_enabled == 0)));
}

static irqreturn_t do_cciss_intr(int irq, void *dev_id)
static inline int bad_tag(ctlr_info_t *h, u32 tag_index,
u32 raw_tag)
{
ctlr_info_t *h = dev_id;
if (unlikely(tag_index >= h->nr_cmds)) {
dev_warn(&h->pdev->dev, "bad tag 0x%08x ignored.\n", raw_tag);
return 1;
}
return 0;
}

static inline void finish_cmd(ctlr_info_t *h, CommandList_struct *c,
u32 raw_tag)
{
removeQ(c);
if (likely(c->cmd_type == CMD_RWREQ))
complete_command(h, c, 0);
else if (c->cmd_type == CMD_IOCTL_PEND)
complete(c->waiting);
#ifdef CONFIG_CISS_SCSI_TAPE
else if (c->cmd_type == CMD_SCSI)
complete_scsi_command(c, 0, raw_tag);
#endif
}

/* process completion of an indexed ("direct lookup") command */
static inline u32 process_indexed_cmd(ctlr_info_t *h, u32 raw_tag)
{
u32 tag_index;
CommandList_struct *c;

tag_index = cciss_tag_to_index(raw_tag);
if (bad_tag(h, tag_index, raw_tag))
return get_next_completion(h);
c = h->cmd_pool + tag_index;
finish_cmd(h, c, raw_tag);
return get_next_completion(h);
}

/* process completion of a non-indexed command */
static inline u32 process_nonindexed_cmd(ctlr_info_t *h, u32 raw_tag)
{
u32 tag;
CommandList_struct *c = NULL;
struct hlist_node *tmp;
__u32 busaddr_masked, tag_masked;

tag = cciss_tag_discard_error_bits(raw_tag);
hlist_for_each_entry(c, tmp, &h->cmpQ, list) {
busaddr_masked = cciss_tag_discard_error_bits(c->busaddr);
tag_masked = cciss_tag_discard_error_bits(tag);
if (busaddr_masked == tag_masked) {
finish_cmd(h, c, raw_tag);
return get_next_completion(h);
}
}
bad_tag(h, h->nr_cmds + 1, raw_tag);
return get_next_completion(h);
}

static irqreturn_t do_cciss_intx(int irq, void *dev_id)
{
ctlr_info_t *h = dev_id;
unsigned long flags;
__u32 a, a1, a2;
u32 raw_tag;

if (interrupt_not_for_us(h))
return IRQ_NONE;
Expand All @@ -3324,50 +3410,41 @@ static irqreturn_t do_cciss_intr(int irq, void *dev_id)
*/
spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
while (interrupt_pending(h)) {
while ((a = get_next_completion(h)) != FIFO_EMPTY) {
a1 = a;
if ((a & 0x04)) {
a2 = (a >> 3);
if (a2 >= h->nr_cmds) {
printk(KERN_WARNING
"cciss: controller cciss%d failed, stopping.\n",
h->ctlr);
spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
fail_all_cmds(h->ctlr);
return IRQ_HANDLED;
}
raw_tag = get_next_completion(h);
while (raw_tag != FIFO_EMPTY) {
if (cciss_tag_contains_index(raw_tag))
raw_tag = process_indexed_cmd(h, raw_tag);
else
raw_tag = process_nonindexed_cmd(h, raw_tag);
}
}

c = h->cmd_pool + a2;
a = c->busaddr;
spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
return IRQ_HANDLED;
}

} else {
struct hlist_node *tmp;
/* Add a second interrupt handler for MSI/MSI-X mode. In this mode we never
* check the interrupt pending register because it is not set.
*/
static irqreturn_t do_cciss_msix_intr(int irq, void *dev_id)
{
ctlr_info_t *h = dev_id;
unsigned long flags;
u32 raw_tag;

a &= ~3;
c = NULL;
hlist_for_each_entry(c, tmp, &h->cmpQ, list) {
if (c->busaddr == a)
break;
}
}
/*
* If we've found the command, take it off the
* completion Q and free it
*/
if (c && c->busaddr == a) {
removeQ(c);
if (c->cmd_type == CMD_RWREQ) {
complete_command(h, c, 0);
} else if (c->cmd_type == CMD_IOCTL_PEND) {
complete(c->waiting);
}
# ifdef CONFIG_CISS_SCSI_TAPE
else if (c->cmd_type == CMD_SCSI)
complete_scsi_command(c, 0, a1);
# endif
continue;
}
}
if (interrupt_not_for_us(h))
return IRQ_NONE;
/*
* If there are completed commands in the completion queue,
* we had better do something about it.
*/
spin_lock_irqsave(CCISS_LOCK(h->ctlr), flags);
raw_tag = get_next_completion(h);
while (raw_tag != FIFO_EMPTY) {
if (cciss_tag_contains_index(raw_tag))
raw_tag = process_indexed_cmd(h, raw_tag);
else
raw_tag = process_nonindexed_cmd(h, raw_tag);
}

spin_unlock_irqrestore(CCISS_LOCK(h->ctlr), flags);
Expand Down Expand Up @@ -4230,11 +4307,21 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,

/* make sure the board interrupts are off */
hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF);
if (request_irq(hba[i]->intr[SIMPLE_MODE_INT], do_cciss_intr,
IRQF_DISABLED | IRQF_SHARED, hba[i]->devname, hba[i])) {
printk(KERN_ERR "cciss: Unable to get irq %d for %s\n",
hba[i]->intr[SIMPLE_MODE_INT], hba[i]->devname);
goto clean2;
if (hba[i]->msi_vector || hba[i]->msix_vector) {
if (request_irq(hba[i]->intr[SIMPLE_MODE_INT],
do_cciss_msix_intr,
IRQF_DISABLED, hba[i]->devname, hba[i])) {
printk(KERN_ERR "cciss: Unable to get irq %d for %s\n",
hba[i]->intr[SIMPLE_MODE_INT], hba[i]->devname);
goto clean2;
}
} else {
if (request_irq(hba[i]->intr[SIMPLE_MODE_INT], do_cciss_intx,
IRQF_DISABLED, hba[i]->devname, hba[i])) {
printk(KERN_ERR "cciss: Unable to get irq %d for %s\n",
hba[i]->intr[SIMPLE_MODE_INT], hba[i]->devname);
goto clean2;
}
}

printk(KERN_INFO "%s: <0x%x> at PCI %s IRQ %d%s using DAC\n",
Expand Down Expand Up @@ -4534,46 +4621,5 @@ static void __exit cciss_cleanup(void)
bus_unregister(&cciss_bus_type);
}

static void fail_all_cmds(unsigned long ctlr)
{
/* If we get here, the board is apparently dead. */
ctlr_info_t *h = hba[ctlr];
CommandList_struct *c;
unsigned long flags;

printk(KERN_WARNING "cciss%d: controller not responding.\n", h->ctlr);
h->alive = 0; /* the controller apparently died... */

spin_lock_irqsave(CCISS_LOCK(ctlr), flags);

pci_disable_device(h->pdev); /* Make sure it is really dead. */

/* move everything off the request queue onto the completed queue */
while (!hlist_empty(&h->reqQ)) {
c = hlist_entry(h->reqQ.first, CommandList_struct, list);
removeQ(c);
h->Qdepth--;
addQ(&h->cmpQ, c);
}

/* Now, fail everything on the completed queue with a HW error */
while (!hlist_empty(&h->cmpQ)) {
c = hlist_entry(h->cmpQ.first, CommandList_struct, list);
removeQ(c);
if (c->cmd_type != CMD_MSG_STALE)
c->err_info->CommandStatus = CMD_HARDWARE_ERR;
if (c->cmd_type == CMD_RWREQ) {
complete_command(h, c, 0);
} else if (c->cmd_type == CMD_IOCTL_PEND)
complete(c->waiting);
#ifdef CONFIG_CISS_SCSI_TAPE
else if (c->cmd_type == CMD_SCSI)
complete_scsi_command(c, 0, 0);
#endif
}
spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
return;
}

module_init(cciss_init);
module_exit(cciss_cleanup);

0 comments on commit 0c2b390

Please sign in to comment.