diff options
author | Stephen M. Cameron <scameron@beardog.cce.hp.com> | 2013-09-23 20:34:12 +0200 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2013-12-19 16:38:56 +0100 |
commit | 0390f0c0dfb540149d7369276b17ec53caf506cb (patch) | |
tree | 2a621fff7b52ca27a9e1a66c168b940d599222f2 | |
parent | [SCSI] hpsa: add MSA 2040 to list of external target devices (diff) | |
download | linux-0390f0c0dfb540149d7369276b17ec53caf506cb.tar.xz linux-0390f0c0dfb540149d7369276b17ec53caf506cb.zip |
[SCSI] hpsa: cap CCISS_PASSTHRU at 20 concurrent commands.
Cap CCISS_BIG_PASSTHRU as well. If an attempt is made
to exceed this, ioctl() will return -1 with errno == EAGAIN.
This is to prevent a userland program from exhausting all of
pci_alloc_consistent memory. I've only seen this problem when
running a special test program designed to provoke it. 20
concurrent commands via the passthru ioctls (not counting SG_IO)
should be more than enough.
Signed-off-by: Stephen M. Cameron <scameron@beardog.cce.hp.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
-rw-r--r-- | drivers/scsi/hpsa.c | 44 | ||||
-rw-r--r-- | drivers/scsi/hpsa.h | 5 |
2 files changed, 47 insertions, 2 deletions
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 6cc91f83aa69..9acfce3bbe78 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -3222,6 +3222,36 @@ static void check_ioctl_unit_attention(struct ctlr_info *h, c->err_info->ScsiStatus != SAM_STAT_CHECK_CONDITION) (void) check_for_unit_attention(h, c); } + +static int increment_passthru_count(struct ctlr_info *h) +{ + unsigned long flags; + + spin_lock_irqsave(&h->passthru_count_lock, flags); + if (h->passthru_count >= HPSA_MAX_CONCURRENT_PASSTHRUS) { + spin_unlock_irqrestore(&h->passthru_count_lock, flags); + return -1; + } + h->passthru_count++; + spin_unlock_irqrestore(&h->passthru_count_lock, flags); + return 0; +} + +static void decrement_passthru_count(struct ctlr_info *h) +{ + unsigned long flags; + + spin_lock_irqsave(&h->passthru_count_lock, flags); + if (h->passthru_count <= 0) { + spin_unlock_irqrestore(&h->passthru_count_lock, flags); + /* not expecting to get here. */ + dev_warn(&h->pdev->dev, "Bug detected, passthru_count seems to be incorrect.\n"); + return; + } + h->passthru_count--; + spin_unlock_irqrestore(&h->passthru_count_lock, flags); +} + /* * ioctl */ @@ -3229,6 +3259,7 @@ static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg) { struct ctlr_info *h; void __user *argp = (void __user *)arg; + int rc; h = sdev_to_hba(dev); @@ -3243,9 +3274,17 @@ static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg) case CCISS_GETDRIVVER: return hpsa_getdrivver_ioctl(h, argp); case CCISS_PASSTHRU: - return hpsa_passthru_ioctl(h, argp); + if (increment_passthru_count(h)) + return -EAGAIN; + rc = hpsa_passthru_ioctl(h, argp); + decrement_passthru_count(h); + return rc; case CCISS_BIG_PASSTHRU: - return hpsa_big_passthru_ioctl(h, argp); + if (increment_passthru_count(h)) + return -EAGAIN; + rc = hpsa_big_passthru_ioctl(h, argp); + decrement_passthru_count(h); + return rc; default: return -ENOTTY; } @@ -4835,6 +4874,7 @@ reinit_after_soft_reset: INIT_LIST_HEAD(&h->reqQ); spin_lock_init(&h->lock); spin_lock_init(&h->scan_lock); + spin_lock_init(&h->passthru_count_lock); rc = hpsa_pci_init(h); if (rc != 0) goto clean1; diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index bc85e7244f40..6eabf08ca672 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -114,6 +114,11 @@ struct ctlr_info { struct TransTable_struct *transtable; unsigned long transMethod; + /* cap concurrent passthrus at some reasonable maximum */ +#define HPSA_MAX_CONCURRENT_PASSTHRUS (20) + spinlock_t passthru_count_lock; /* protects passthru_count */ + int passthru_count; + /* * Performant mode completion buffers */ |