diff mbox

[v4,19/43] hpsa: add ioaccel sg chaining for the ioaccel2 path

Message ID 20150416134827.30238.67380.stgit@brunhilda (mailing list archive)
State New, archived
Headers show

Commit Message

Don Brace April 16, 2015, 1:48 p.m. UTC
From: Webb Scales <webbnh@hp.com>

Increase the request size for ioaccel2 path.

The error, if any, returned by hpsa_allocate_ioaccel2_sg_chain_blocks
to hpsa_alloc_ioaccel2_cmd_and_bft should be returned upstream rather
than assumed to be -ENOMEM.

This differs slightly from hpsa_alloc_ioaccel1_cmd_and_bft,
which does not call another hpsa_allocate function and only
has -ENOMEM to return from some kmalloc calls.

Reviewed-by: Scott Teel <scott.teel@pmcs.com>
Reviewed-by: Kevin Barnett <kevin.barnett@pmcs.com>
Signed-off-by: Robert Elliott <elliott@hp.com>
Signed-off-by: Don Brace <don.brace@pmcs.com>
---
 drivers/scsi/hpsa.c |  125 +++++++++++++++++++++++++++++++++++++++++++++++----
 drivers/scsi/hpsa.h |    1 
 2 files changed, 116 insertions(+), 10 deletions(-)


--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Hannes Reinecke April 17, 2015, 1:14 p.m. UTC | #1
On 04/16/2015 03:48 PM, Don Brace wrote:
> From: Webb Scales <webbnh@hp.com>
> 
> Increase the request size for ioaccel2 path.
> 
> The error, if any, returned by hpsa_allocate_ioaccel2_sg_chain_blocks
> to hpsa_alloc_ioaccel2_cmd_and_bft should be returned upstream rather
> than assumed to be -ENOMEM.
> 
> This differs slightly from hpsa_alloc_ioaccel1_cmd_and_bft,
> which does not call another hpsa_allocate function and only
> has -ENOMEM to return from some kmalloc calls.
> 
> Reviewed-by: Scott Teel <scott.teel@pmcs.com>
> Reviewed-by: Kevin Barnett <kevin.barnett@pmcs.com>
> Signed-off-by: Robert Elliott <elliott@hp.com>
> Signed-off-by: Don Brace <don.brace@pmcs.com>
> ---
>  drivers/scsi/hpsa.c |  125 +++++++++++++++++++++++++++++++++++++++++++++++----
>  drivers/scsi/hpsa.h |    1 
>  2 files changed, 116 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
> index c9c42e9..1839761 100644
> --- a/drivers/scsi/hpsa.c
> +++ b/drivers/scsi/hpsa.c
> @@ -1704,6 +1704,46 @@ static void hpsa_slave_destroy(struct scsi_device *sdev)
>  	/* nothing to do. */
>  }
>  
> +static void hpsa_free_ioaccel2_sg_chain_blocks(struct ctlr_info *h)
> +{
> +	int i;
> +
> +	if (!h->ioaccel2_cmd_sg_list)
> +		return;
> +	for (i = 0; i < h->nr_cmds; i++) {
> +		kfree(h->ioaccel2_cmd_sg_list[i]);
> +		h->ioaccel2_cmd_sg_list[i] = NULL;
> +	}
> +	kfree(h->ioaccel2_cmd_sg_list);
> +	h->ioaccel2_cmd_sg_list = NULL;
> +}
> +
> +static int hpsa_allocate_ioaccel2_sg_chain_blocks(struct ctlr_info *h)
> +{
> +	int i;
> +
> +	if (h->chainsize <= 0)
> +		return 0;
> +
> +	h->ioaccel2_cmd_sg_list =
> +		kzalloc(sizeof(*h->ioaccel2_cmd_sg_list) * h->nr_cmds,
> +					GFP_KERNEL);
> +	if (!h->ioaccel2_cmd_sg_list)
> +		return -ENOMEM;
> +	for (i = 0; i < h->nr_cmds; i++) {
> +		h->ioaccel2_cmd_sg_list[i] =
> +			kmalloc(sizeof(*h->ioaccel2_cmd_sg_list[i]) *
> +					h->maxsgentries, GFP_KERNEL);
> +		if (!h->ioaccel2_cmd_sg_list[i])
> +			goto clean;
> +	}
> +	return 0;
> +
> +clean:
> +	hpsa_free_ioaccel2_sg_chain_blocks(h);
> +	return -ENOMEM;
> +}
> +
>  static void hpsa_free_sg_chain_blocks(struct ctlr_info *h)
>  {
>  	int i;
Any reason why you didn't use mempools here?

Cheers,

Hannes
brace April 22, 2015, 7:12 p.m. UTC | #2
VGhlIGxhc3QgSSByZWFkIGFib3V0IG1lbXBvb2xzIHdhcyBmcm9tOg0KTGludXggRGV2aWNlIERy
aXZlcnMNCiBCeSBKb25hdGhhbiBDb3JiZXQsIEFsZXNzYW5kcm8gUnViaW5pLCBHcmVnIEtyb2Fo
LUhhcnRtYW4NCg0KaHR0cHM6Ly9sd24ubmV0L2ltYWdlcy9wZGYvTEREMy9jaDA4LnBkZg0KDQoN
CklmICB5b3UgIGFyZSAgY29uc2lkZXJpbmcgIHVzaW5nICBhICBtZW1wb29sICBpbiAgeW91ciAg
ZHJpdmVyLCAgcGxlYXNlICBrZWVwICBvbmUgIHRoaW5nICBpbg0KbWluZDogbWVtcG9vbHMgYWxs
b2NhdGUgYSBjaHVuayBvZiBtZW1vcnkgdGhhdCBzaXRzIGluIGEgbGlzdCwgaWRsZSBhbmQgdW5h
dmFpbGFibGUNCmZvciBhbnkgcmVhbCB1c2UuIEl0IGlzIGVhc3kgdG8gY29uc3VtZSBhIGdyZWF0
IGRlYWwgb2YgbWVtb3J5IHdpdGggbWVtcG9vbHMuIEluDQphbG1vc3QgZXZlcnkgY2FzZSwgdGhl
IHByZWZlcnJlZCBhbHRlcm5hdGl2ZSBpcyB0byBkbyB3aXRob3V0IHRoZSBtZW1wb29sIGFuZCBz
aW1wbHkNCmRlYWwgd2l0aCB0aGUgcG9zc2liaWxpdHkgb2YgYWxsb2NhdGlvbiBmYWlsdXJlcyBp
bnN0ZWFkLiBJZiB0aGVyZSBpcyBhbnkgd2F5IGZvciB5b3VyDQpkcml2ZXIgdG8gcmVzcG9uZCB0
byBhbiBhbGxvY2F0aW9uIGZhaWx1cmUgaW4gYSB3YXkgdGhhdCBkb2VzIG5vdCBlbmRhbmdlciB0
aGUgaW50ZWctDQpyaXR5IG9mIHRoZSBzeXN0ZW0sIGRvIHRoaW5ncyB0aGF0IHdheS4gVXNlIG9m
IG1lbXBvb2xzIGluIGRyaXZlciBjb2RlIHNob3VsZCBiZQ0KcmFyZQ0KDQpCdXQgdGhpcyBib29r
IGlzIG5vdyBxdWl0ZSBvbGQgaW4gTGludXggdGVybXMuDQoNClNvLCBJIGNhbiBpbnZlc3RpZ2F0
ZSB1c2luZyBtZW1wb29scy4gSG93ZXZlciwgSSB3b3VsZCBwcmVmZXIgdG8gY29udGludWUgdXNp
bmcgdGhlIGV4aXN0aW5nIG1ldGhvZHMgaW4gdGhpcyBjdXJyZW50IHBhdGNoc2V0Lg0KDQpJIGhv
cGUgdGhhdCBpcyBvayB3aXRoIHlvdS4uLg0KDQoNCj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0t
LS0NCj4gRnJvbTogSGFubmVzIFJlaW5lY2tlIFttYWlsdG86aGFyZUBzdXNlLmRlXQ0KPiBTZW50
OiBGcmlkYXksIEFwcmlsIDE3LCAyMDE1IDg6MTQgQU0NCj4gVG86IERvbiBCcmFjZTsgU2NvdHQg
VGVlbDsgS2V2aW4gQmFybmV0dDsgamFtZXMuYm90dG9tbGV5QHBhcmFsbGVscy5jb207DQo+IGhj
aEBpbmZyYWRlYWQub3JnOyBKdXN0aW4gTGluZGxleTsgYnJhY2UNCj4gQ2M6IGxpbnV4LXNjc2lA
dmdlci5rZXJuZWwub3JnDQo+IFN1YmplY3Q6IFJlOiBbUEFUQ0ggdjQgMTkvNDNdIGhwc2E6IGFk
ZCBpb2FjY2VsIHNnIGNoYWluaW5nIGZvciB0aGUgaW9hY2NlbDINCj4gcGF0aA0KPiANCj4gT24g
MDQvMTYvMjAxNSAwMzo0OCBQTSwgRG9uIEJyYWNlIHdyb3RlOg0KPiA+IEZyb206IFdlYmIgU2Nh
bGVzIDx3ZWJibmhAaHAuY29tPg0KPiA+DQo+ID4gSW5jcmVhc2UgdGhlIHJlcXVlc3Qgc2l6ZSBm
b3IgaW9hY2NlbDIgcGF0aC4NCj4gPg0KPiA+IFRoZSBlcnJvciwgaWYgYW55LCByZXR1cm5lZCBi
eSBocHNhX2FsbG9jYXRlX2lvYWNjZWwyX3NnX2NoYWluX2Jsb2Nrcw0KPiA+IHRvIGhwc2FfYWxs
b2NfaW9hY2NlbDJfY21kX2FuZF9iZnQgc2hvdWxkIGJlIHJldHVybmVkIHVwc3RyZWFtIHJhdGhl
cg0KPiA+IHRoYW4gYXNzdW1lZCB0byBiZSAtRU5PTUVNLg0KPiA+DQo+ID4gVGhpcyBkaWZmZXJz
IHNsaWdodGx5IGZyb20gaHBzYV9hbGxvY19pb2FjY2VsMV9jbWRfYW5kX2JmdCwNCj4gPiB3aGlj
aCBkb2VzIG5vdCBjYWxsIGFub3RoZXIgaHBzYV9hbGxvY2F0ZSBmdW5jdGlvbiBhbmQgb25seQ0K
PiA+IGhhcyAtRU5PTUVNIHRvIHJldHVybiBmcm9tIHNvbWUga21hbGxvYyBjYWxscy4NCj4gPg0K
PiA+IFJldmlld2VkLWJ5OiBTY290dCBUZWVsIDxzY290dC50ZWVsQHBtY3MuY29tPg0KPiA+IFJl
dmlld2VkLWJ5OiBLZXZpbiBCYXJuZXR0IDxrZXZpbi5iYXJuZXR0QHBtY3MuY29tPg0KPiA+IFNp
Z25lZC1vZmYtYnk6IFJvYmVydCBFbGxpb3R0IDxlbGxpb3R0QGhwLmNvbT4NCj4gPiBTaWduZWQt
b2ZmLWJ5OiBEb24gQnJhY2UgPGRvbi5icmFjZUBwbWNzLmNvbT4NCj4gPiAtLS0NCj4gPiAgZHJp
dmVycy9zY3NpL2hwc2EuYyB8ICAxMjUNCj4gKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr
KysrKysrKysrKysrKysrKystLS0tDQo+ID4gIGRyaXZlcnMvc2NzaS9ocHNhLmggfCAgICAxDQo+
ID4gIDIgZmlsZXMgY2hhbmdlZCwgMTE2IGluc2VydGlvbnMoKyksIDEwIGRlbGV0aW9ucygtKQ0K
PiA+DQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvc2NzaS9ocHNhLmMgYi9kcml2ZXJzL3Njc2kv
aHBzYS5jDQo+ID4gaW5kZXggYzljNDJlOS4uMTgzOTc2MSAxMDA2NDQNCj4gPiAtLS0gYS9kcml2
ZXJzL3Njc2kvaHBzYS5jDQo+ID4gKysrIGIvZHJpdmVycy9zY3NpL2hwc2EuYw0KPiA+IEBAIC0x
NzA0LDYgKzE3MDQsNDYgQEAgc3RhdGljIHZvaWQgaHBzYV9zbGF2ZV9kZXN0cm95KHN0cnVjdCBz
Y3NpX2RldmljZQ0KPiAqc2RldikNCj4gPiAgCS8qIG5vdGhpbmcgdG8gZG8uICovDQo+ID4gIH0N
Cj4gPg0KPiA+ICtzdGF0aWMgdm9pZCBocHNhX2ZyZWVfaW9hY2NlbDJfc2dfY2hhaW5fYmxvY2tz
KHN0cnVjdCBjdGxyX2luZm8gKmgpDQo+ID4gK3sNCj4gPiArCWludCBpOw0KPiA+ICsNCj4gPiAr
CWlmICghaC0+aW9hY2NlbDJfY21kX3NnX2xpc3QpDQo+ID4gKwkJcmV0dXJuOw0KPiA+ICsJZm9y
IChpID0gMDsgaSA8IGgtPm5yX2NtZHM7IGkrKykgew0KPiA+ICsJCWtmcmVlKGgtPmlvYWNjZWwy
X2NtZF9zZ19saXN0W2ldKTsNCj4gPiArCQloLT5pb2FjY2VsMl9jbWRfc2dfbGlzdFtpXSA9IE5V
TEw7DQo+ID4gKwl9DQo+ID4gKwlrZnJlZShoLT5pb2FjY2VsMl9jbWRfc2dfbGlzdCk7DQo+ID4g
KwloLT5pb2FjY2VsMl9jbWRfc2dfbGlzdCA9IE5VTEw7DQo+ID4gK30NCj4gPiArDQo+ID4gK3N0
YXRpYyBpbnQgaHBzYV9hbGxvY2F0ZV9pb2FjY2VsMl9zZ19jaGFpbl9ibG9ja3Moc3RydWN0IGN0
bHJfaW5mbyAqaCkNCj4gPiArew0KPiA+ICsJaW50IGk7DQo+ID4gKw0KPiA+ICsJaWYgKGgtPmNo
YWluc2l6ZSA8PSAwKQ0KPiA+ICsJCXJldHVybiAwOw0KPiA+ICsNCj4gPiArCWgtPmlvYWNjZWwy
X2NtZF9zZ19saXN0ID0NCj4gPiArCQlremFsbG9jKHNpemVvZigqaC0+aW9hY2NlbDJfY21kX3Nn
X2xpc3QpICogaC0+bnJfY21kcywNCj4gPiArCQkJCQlHRlBfS0VSTkVMKTsNCj4gPiArCWlmICgh
aC0+aW9hY2NlbDJfY21kX3NnX2xpc3QpDQo+ID4gKwkJcmV0dXJuIC1FTk9NRU07DQo+ID4gKwlm
b3IgKGkgPSAwOyBpIDwgaC0+bnJfY21kczsgaSsrKSB7DQo+ID4gKwkJaC0+aW9hY2NlbDJfY21k
X3NnX2xpc3RbaV0gPQ0KPiA+ICsJCQlrbWFsbG9jKHNpemVvZigqaC0+aW9hY2NlbDJfY21kX3Nn
X2xpc3RbaV0pICoNCj4gPiArCQkJCQloLT5tYXhzZ2VudHJpZXMsIEdGUF9LRVJORUwpOw0KPiA+
ICsJCWlmICghaC0+aW9hY2NlbDJfY21kX3NnX2xpc3RbaV0pDQo+ID4gKwkJCWdvdG8gY2xlYW47
DQo+ID4gKwl9DQo+ID4gKwlyZXR1cm4gMDsNCj4gPiArDQo+ID4gK2NsZWFuOg0KPiA+ICsJaHBz
YV9mcmVlX2lvYWNjZWwyX3NnX2NoYWluX2Jsb2NrcyhoKTsNCj4gPiArCXJldHVybiAtRU5PTUVN
Ow0KPiA+ICt9DQo+ID4gKw0KPiA+ICBzdGF0aWMgdm9pZCBocHNhX2ZyZWVfc2dfY2hhaW5fYmxv
Y2tzKHN0cnVjdCBjdGxyX2luZm8gKmgpDQo+ID4gIHsNCj4gPiAgCWludCBpOw0KPiBBbnkgcmVh
c29uIHdoeSB5b3UgZGlkbid0IHVzZSBtZW1wb29scyBoZXJlPw0KPiANCj4gQ2hlZXJzLA0KPiAN
Cj4gSGFubmVzDQo+IC0tDQo+IERyLiBIYW5uZXMgUmVpbmVja2UJCSAgICAgICAgICAgICAgIHpT
ZXJpZXMgJiBTdG9yYWdlDQo+IGhhcmVAc3VzZS5kZQkJCSAgICAgICAgICAgICAgICs0OSA5MTEg
NzQwNTMgNjg4DQo+IFNVU0UgTElOVVggR21iSCwgTWF4ZmVsZHN0ci4gNSwgOTA0MDkgTsO8cm5i
ZXJnDQo+IEdGOiBGLiBJbWVuZMO2cmZmZXIsIEouIFNtaXRoYXJkLCBKLiBHdWlsZCwgRC4gVXBt
YW55dSwgRy4gTm9ydG9uDQo+IEhSQiAyMTI4NCAoQUcgTsO8cm5iZXJnKQ0K
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Hannes Reinecke April 23, 2015, 5:50 a.m. UTC | #3
On 04/22/2015 09:12 PM, brace wrote:
> The last I read about mempools was from:
> Linux Device Drivers
>  By Jonathan Corbet, Alessandro Rubini, Greg Kroah-Hartman
> 
> https://lwn.net/images/pdf/LDD3/ch08.pdf
> 
> 
> If  you  are  considering  using  a  mempool  in  your  driver,  please  keep  one  thing  in
> mind: mempools allocate a chunk of memory that sits in a list, idle and unavailable
> for any real use. It is easy to consume a great deal of memory with mempools. In
> almost every case, the preferred alternative is to do without the mempool and simply
> deal with the possibility of allocation failures instead. If there is any way for your
> driver to respond to an allocation failure in a way that does not endanger the integ-
> rity of the system, do things that way. Use of mempools in driver code should be
> rare
> 
> But this book is now quite old in Linux terms.
> 
> So, I can investigate using mempools. However, I would prefer to continue using the existing methods in this current patchset.
> 
> I hope that is ok with you...
> 
Yes, that's okay with me.

Personally I would be using mempools when you need lots of small,
frequently changed allocations (like sg elements), and kzalloc() for
large or infrequently changed bits of memory.

Reviewed-by: Hannes Reinecke <hare@suse.de>

Cheers,

Hannes
Christoph Hellwig April 23, 2015, 7:39 a.m. UTC | #4
On Thu, Apr 23, 2015 at 07:50:41AM +0200, Hannes Reinecke wrote:
> Yes, that's okay with me.
> 
> Personally I would be using mempools when you need lots of small,
> frequently changed allocations (like sg elements), and kzalloc() for
> large or infrequently changed bits of memory.

The array lookup is a small O(1) cost, so it's aways cheaper than
a mempool for this case where we allocate on object per tag.

Even better would be to use the command private data, but that needs
the patches that allow the driver to initializse it that Jens is working
on first.
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index c9c42e9..1839761 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -1704,6 +1704,46 @@  static void hpsa_slave_destroy(struct scsi_device *sdev)
 	/* nothing to do. */
 }
 
+static void hpsa_free_ioaccel2_sg_chain_blocks(struct ctlr_info *h)
+{
+	int i;
+
+	if (!h->ioaccel2_cmd_sg_list)
+		return;
+	for (i = 0; i < h->nr_cmds; i++) {
+		kfree(h->ioaccel2_cmd_sg_list[i]);
+		h->ioaccel2_cmd_sg_list[i] = NULL;
+	}
+	kfree(h->ioaccel2_cmd_sg_list);
+	h->ioaccel2_cmd_sg_list = NULL;
+}
+
+static int hpsa_allocate_ioaccel2_sg_chain_blocks(struct ctlr_info *h)
+{
+	int i;
+
+	if (h->chainsize <= 0)
+		return 0;
+
+	h->ioaccel2_cmd_sg_list =
+		kzalloc(sizeof(*h->ioaccel2_cmd_sg_list) * h->nr_cmds,
+					GFP_KERNEL);
+	if (!h->ioaccel2_cmd_sg_list)
+		return -ENOMEM;
+	for (i = 0; i < h->nr_cmds; i++) {
+		h->ioaccel2_cmd_sg_list[i] =
+			kmalloc(sizeof(*h->ioaccel2_cmd_sg_list[i]) *
+					h->maxsgentries, GFP_KERNEL);
+		if (!h->ioaccel2_cmd_sg_list[i])
+			goto clean;
+	}
+	return 0;
+
+clean:
+	hpsa_free_ioaccel2_sg_chain_blocks(h);
+	return -ENOMEM;
+}
+
 static void hpsa_free_sg_chain_blocks(struct ctlr_info *h)
 {
 	int i;
@@ -1746,6 +1786,39 @@  clean:
 	return -ENOMEM;
 }
 
+static int hpsa_map_ioaccel2_sg_chain_block(struct ctlr_info *h,
+	struct io_accel2_cmd *cp, struct CommandList *c)
+{
+	struct ioaccel2_sg_element *chain_block;
+	u64 temp64;
+	u32 chain_size;
+
+	chain_block = h->ioaccel2_cmd_sg_list[c->cmdindex];
+	chain_size = le32_to_cpu(cp->data_len);
+	temp64 = pci_map_single(h->pdev, chain_block, chain_size,
+				PCI_DMA_TODEVICE);
+	if (dma_mapping_error(&h->pdev->dev, temp64)) {
+		/* prevent subsequent unmapping */
+		cp->sg->address = 0;
+		return -1;
+	}
+	cp->sg->address = cpu_to_le64(temp64);
+	return 0;
+}
+
+static void hpsa_unmap_ioaccel2_sg_chain_block(struct ctlr_info *h,
+	struct io_accel2_cmd *cp)
+{
+	struct ioaccel2_sg_element *chain_sg;
+	u64 temp64;
+	u32 chain_size;
+
+	chain_sg = cp->sg;
+	temp64 = le64_to_cpu(chain_sg->address);
+	chain_size = le32_to_cpu(cp->data_len);
+	pci_unmap_single(h->pdev, temp64, chain_size, PCI_DMA_TODEVICE);
+}
+
 static int hpsa_map_sg_chain_block(struct ctlr_info *h,
 	struct CommandList *c)
 {
@@ -1955,6 +2028,7 @@  static void complete_scsi_command(struct CommandList *cp)
 	struct ctlr_info *h;
 	struct ErrorInfo *ei;
 	struct hpsa_scsi_dev_t *dev;
+	struct io_accel2_cmd *c2;
 
 	int sense_key;
 	int asc;      /* additional sense code */
@@ -1965,12 +2039,17 @@  static void complete_scsi_command(struct CommandList *cp)
 	cmd = cp->scsi_cmd;
 	h = cp->h;
 	dev = cmd->device->hostdata;
+	c2 = &h->ioaccel2_cmd_pool[cp->cmdindex];
 
 	scsi_dma_unmap(cmd); /* undo the DMA mappings */
 	if ((cp->cmd_type == CMD_SCSI) &&
 		(le16_to_cpu(cp->Header.SGTotal) > h->max_cmd_sg_entries))
 		hpsa_unmap_sg_chain_block(h, cp);
 
+	if ((cp->cmd_type == CMD_IOACCEL2) &&
+		(c2->sg[0].chain_indicator == IOACCEL2_CHAIN))
+		hpsa_unmap_ioaccel2_sg_chain_block(h, c2);
+
 	cmd->result = (DID_OK << 16); 		/* host byte */
 	cmd->result |= (COMMAND_COMPLETE << 8);	/* msg byte */
 
@@ -3812,10 +3891,7 @@  static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
 	u32 len;
 	u32 total_len = 0;
 
-	if (scsi_sg_count(cmd) > h->ioaccel_maxsg) {
-		atomic_dec(&phys_disk->ioaccel_cmds_out);
-		return IO_ACCEL_INELIGIBLE;
-	}
+	BUG_ON(scsi_sg_count(cmd) > h->maxsgentries);
 
 	if (fixup_ioaccel_cdb(cdb, &cdb_len)) {
 		atomic_dec(&phys_disk->ioaccel_cmds_out);
@@ -3838,8 +3914,19 @@  static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
 	}
 
 	if (use_sg) {
-		BUG_ON(use_sg > IOACCEL2_MAXSGENTRIES);
 		curr_sg = cp->sg;
+		if (use_sg > h->ioaccel_maxsg) {
+			addr64 = le64_to_cpu(
+				h->ioaccel2_cmd_sg_list[c->cmdindex]->address);
+			curr_sg->address = cpu_to_le64(addr64);
+			curr_sg->length = 0;
+			curr_sg->reserved[0] = 0;
+			curr_sg->reserved[1] = 0;
+			curr_sg->reserved[2] = 0;
+			curr_sg->chain_indicator = 0x80;
+
+			curr_sg = h->ioaccel2_cmd_sg_list[c->cmdindex];
+		}
 		scsi_for_each_sg(cmd, sg, use_sg, i) {
 			addr64 = (u64) sg_dma_address(sg);
 			len  = sg_dma_len(sg);
@@ -3884,14 +3971,22 @@  static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h,
 	cp->Tag = cpu_to_le32(c->cmdindex << DIRECT_LOOKUP_SHIFT);
 	memcpy(cp->cdb, cdb, sizeof(cp->cdb));
 
-	/* fill in sg elements */
-	cp->sg_count = (u8) use_sg;
-
 	cp->data_len = cpu_to_le32(total_len);
 	cp->err_ptr = cpu_to_le64(c->busaddr +
 			offsetof(struct io_accel2_cmd, error_data));
 	cp->err_len = cpu_to_le32(sizeof(cp->error_data));
 
+	/* fill in sg elements */
+	if (use_sg > h->ioaccel_maxsg) {
+		cp->sg_count = 1;
+		if (hpsa_map_ioaccel2_sg_chain_block(h, cp, c)) {
+			atomic_dec(&phys_disk->ioaccel_cmds_out);
+			scsi_dma_unmap(cmd);
+			return -1;
+		}
+	} else
+		cp->sg_count = (u8) use_sg;
+
 	enqueue_cmd_and_start_io(h, c);
 	return 0;
 }
@@ -7911,6 +8006,8 @@  clean_up:
 /* Free ioaccel2 mode command blocks and block fetch table */
 static void hpsa_free_ioaccel2_cmd_and_bft(struct ctlr_info *h)
 {
+	hpsa_free_ioaccel2_sg_chain_blocks(h);
+
 	if (h->ioaccel2_cmd_pool)
 		pci_free_consistent(h->pdev,
 			h->nr_cmds * sizeof(*h->ioaccel2_cmd_pool),
@@ -7922,6 +8019,8 @@  static void hpsa_free_ioaccel2_cmd_and_bft(struct ctlr_info *h)
 /* Allocate ioaccel2 mode command blocks and block fetch table */
 static int hpsa_alloc_ioaccel2_cmd_and_bft(struct ctlr_info *h)
 {
+	int rc;
+
 	/* Allocate ioaccel2 mode command blocks and block fetch table */
 
 	h->ioaccel_maxsg =
@@ -7941,7 +8040,13 @@  static int hpsa_alloc_ioaccel2_cmd_and_bft(struct ctlr_info *h)
 				sizeof(u32)), GFP_KERNEL);
 
 	if ((h->ioaccel2_cmd_pool == NULL) ||
-		(h->ioaccel2_blockFetchTable == NULL))
+		(h->ioaccel2_blockFetchTable == NULL)) {
+		rc = -ENOMEM;
+		goto clean_up;
+	}
+
+	rc = hpsa_allocate_ioaccel2_sg_chain_blocks(h);
+	if (rc)
 		goto clean_up;
 
 	memset(h->ioaccel2_cmd_pool, 0,
@@ -7950,7 +8055,7 @@  static int hpsa_alloc_ioaccel2_cmd_and_bft(struct ctlr_info *h)
 
 clean_up:
 	hpsa_free_ioaccel2_cmd_and_bft(h);
-	return 1;
+	return rc;
 }
 
 static void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h
index 87a70b5..3acacf6 100644
--- a/drivers/scsi/hpsa.h
+++ b/drivers/scsi/hpsa.h
@@ -162,6 +162,7 @@  struct ctlr_info {
 	u8 max_cmd_sg_entries;
 	int chainsize;
 	struct SGDescriptor **cmd_sg_list;
+	struct ioaccel2_sg_element **ioaccel2_cmd_sg_list;
 
 	/* pointers to command and error info pool */
 	struct CommandList 	*cmd_pool;