From 1db31d778d962b1477bcfc71161e8a1d5ffc4fdd Mon Sep 17 00:00:00 2001 From: "Stephen M. Cameron" Date: Mon, 23 Sep 2013 13:34:12 -0500 Subject: [PATCH] [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 Signed-off-by: James Bottomley --- drivers/scsi/hpsa.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 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 a94b5c3c56a4..4201b0359455 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -3390,6 +3390,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 */ @@ -3397,6 +3427,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); @@ -3411,9 +3442,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; } @@ -5005,6 +5044,7 @@ reinit_after_soft_reset: spin_lock_init(&h->lock); spin_lock_init(&h->scan_lock); spin_lock_init(&h->offline_device_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 4953fe3468d1..839c533b89bb 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -115,6 +115,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 */ -- 2.39.5