[SCSI] aacraid: fix srb ioctl for 64 bits

Received from Mark Salyzyn,

The raw srb ioctl is supposed to be able to take packets with 32 and 64 bit
virtual address SG elements, it did not handle the frames with 64 bit SG
elements well when communicating with 64 bit DMA capable adapters, and it did
not handle the 32 bit limited DMA adapters at all.  The enclosed patch now
handles all four quadrants (32 bit / 64 bit SG elements in SRB requests + 32
bit or 64 bit DMA capable adapters)

This fix is required before Java based management applications in a 64 bit user
space can submit raw srb requests to the array physical components via the
ioctl mechanism, the allocated user memory pool on 64 bit machines under this
environment forced the management software's hands to submit 64 bit user space
virtual address SG elements in via the ioctl.

Signed-off-by: Mark Haverkamp <markh@linux-foundation.org>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
This commit is contained in:
Mark Haverkamp 2007-03-15 10:27:32 -07:00 committed by James Bottomley
parent 9e7c349c91
commit f2b1a06ad4
1 changed files with 156 additions and 101 deletions

View File

@ -5,7 +5,7 @@
* based on the old aacraid driver that is.. * based on the old aacraid driver that is..
* Adaptec aacraid device driver for Linux. * Adaptec aacraid device driver for Linux.
* *
* Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com) * Copyright (c) 2000-2007 Adaptec, Inc. (aacraid@adaptec.com)
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -468,7 +468,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
void *sg_list[32]; void *sg_list[32];
u32 sg_indx = 0; u32 sg_indx = 0;
u32 byte_count = 0; u32 byte_count = 0;
u32 actual_fibsize = 0; u32 actual_fibsize64, actual_fibsize = 0;
int i; int i;
@ -481,7 +481,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
return -EPERM; return -EPERM;
} }
/* /*
* Allocate and initialize a Fib then setup a BlockWrite command * Allocate and initialize a Fib then setup a SRB command
*/ */
if (!(srbfib = aac_fib_alloc(dev))) { if (!(srbfib = aac_fib_alloc(dev))) {
return -ENOMEM; return -ENOMEM;
@ -548,24 +548,70 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
rcode = -EINVAL; rcode = -EINVAL;
goto cleanup; goto cleanup;
} }
if (dev->dac_support == 1) { actual_fibsize = sizeof(struct aac_srb) - sizeof(struct sgentry) +
((user_srbcmd->sg.count & 0xff) * sizeof(struct sgentry));
actual_fibsize64 = actual_fibsize + (user_srbcmd->sg.count & 0xff) *
(sizeof(struct sgentry64) - sizeof(struct sgentry));
/* User made a mistake - should not continue */
if ((actual_fibsize != fibsize) && (actual_fibsize64 != fibsize)) {
dprintk((KERN_DEBUG"aacraid: Bad Size specified in "
"Raw SRB command calculated fibsize=%lu;%lu "
"user_srbcmd->sg.count=%d aac_srb=%lu sgentry=%lu;%lu "
"issued fibsize=%d\n",
actual_fibsize, actual_fibsize64, user_srbcmd->sg.count,
sizeof(struct aac_srb), sizeof(struct sgentry),
sizeof(struct sgentry64), fibsize));
rcode = -EINVAL;
goto cleanup;
}
if ((data_dir == DMA_NONE) && user_srbcmd->sg.count) {
dprintk((KERN_DEBUG"aacraid: SG with no direction specified in Raw SRB command\n"));
rcode = -EINVAL;
goto cleanup;
}
byte_count = 0;
if (dev->adapter_info.options & AAC_OPT_SGMAP_HOST64) {
struct user_sgmap64* upsg = (struct user_sgmap64*)&user_srbcmd->sg; struct user_sgmap64* upsg = (struct user_sgmap64*)&user_srbcmd->sg;
struct sgmap64* psg = (struct sgmap64*)&srbcmd->sg; struct sgmap64* psg = (struct sgmap64*)&srbcmd->sg;
struct user_sgmap* usg;
byte_count = 0;
/* /*
* This should also catch if user used the 32 bit sgmap * This should also catch if user used the 32 bit sgmap
*/ */
actual_fibsize = sizeof(struct aac_srb) - if (actual_fibsize64 == fibsize) {
sizeof(struct sgentry) + actual_fibsize = actual_fibsize64;
((upsg->count & 0xff) * for (i = 0; i < upsg->count; i++) {
sizeof(struct sgentry)); u64 addr;
if(actual_fibsize != fibsize){ // User made a mistake - should not continue void* p;
dprintk((KERN_DEBUG"aacraid: Bad Size specified in Raw SRB command\n")); /* Does this really need to be GFP_DMA? */
rcode = -EINVAL; p = kmalloc(upsg->sg[i].count,GFP_KERNEL|__GFP_DMA);
if(p == 0) {
dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
upsg->sg[i].count,i,upsg->count));
rcode = -ENOMEM;
goto cleanup; goto cleanup;
} }
addr = (u64)upsg->sg[i].addr[0];
addr += ((u64)upsg->sg[i].addr[1]) << 32;
sg_user[i] = (void __user *)(ptrdiff_t)addr;
sg_list[i] = p; // save so we can clean up later
sg_indx = i;
if( flags & SRB_DataOut ){
if(copy_from_user(p,sg_user[i],upsg->sg[i].count)){
dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
rcode = -EFAULT;
goto cleanup;
}
}
addr = pci_map_single(dev->pdev, p, upsg->sg[i].count, data_dir);
psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff);
psg->sg[i].addr[1] = cpu_to_le32(addr>>32);
byte_count += upsg->sg[i].count;
psg->sg[i].count = cpu_to_le32(upsg->sg[i].count);
}
} else {
struct user_sgmap* usg;
usg = kmalloc(actual_fibsize - sizeof(struct aac_srb) usg = kmalloc(actual_fibsize - sizeof(struct aac_srb)
+ sizeof(struct sgmap), GFP_KERNEL); + sizeof(struct sgmap), GFP_KERNEL);
if (!usg) { if (!usg) {
@ -575,15 +621,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
} }
memcpy (usg, upsg, actual_fibsize - sizeof(struct aac_srb) memcpy (usg, upsg, actual_fibsize - sizeof(struct aac_srb)
+ sizeof(struct sgmap)); + sizeof(struct sgmap));
actual_fibsize = sizeof(struct aac_srb) - actual_fibsize = actual_fibsize64;
sizeof(struct sgentry) + ((usg->count & 0xff) *
sizeof(struct sgentry64));
if ((data_dir == DMA_NONE) && upsg->count) {
kfree (usg);
dprintk((KERN_DEBUG"aacraid: SG with no direction specified in Raw SRB command\n"));
rcode = -EINVAL;
goto cleanup;
}
for (i = 0; i < usg->count; i++) { for (i = 0; i < usg->count; i++) {
u64 addr; u64 addr;
@ -597,7 +635,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
rcode = -ENOMEM; rcode = -ENOMEM;
goto cleanup; goto cleanup;
} }
sg_user[i] = (void __user *)(long)usg->sg[i].addr; sg_user[i] = (void __user *)(ptrdiff_t)usg->sg[i].addr;
sg_list[i] = p; // save so we can clean up later sg_list[i] = p; // save so we can clean up later
sg_indx = i; sg_indx = i;
@ -613,36 +651,51 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff); psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff);
psg->sg[i].addr[1] = cpu_to_le32(addr>>32); psg->sg[i].addr[1] = cpu_to_le32(addr>>32);
psg->sg[i].count = cpu_to_le32(usg->sg[i].count);
byte_count += usg->sg[i].count; byte_count += usg->sg[i].count;
psg->sg[i].count = cpu_to_le32(usg->sg[i].count);
} }
kfree (usg); kfree (usg);
}
srbcmd->count = cpu_to_le32(byte_count); srbcmd->count = cpu_to_le32(byte_count);
psg->count = cpu_to_le32(sg_indx+1); psg->count = cpu_to_le32(sg_indx+1);
status = aac_fib_send(ScsiPortCommand64, srbfib, actual_fibsize, FsaNormal, 1, 1,NULL,NULL); status = aac_fib_send(ScsiPortCommand64, srbfib, actual_fibsize, FsaNormal, 1, 1,NULL,NULL);
} else { } else {
struct user_sgmap* upsg = &user_srbcmd->sg; struct user_sgmap* upsg = &user_srbcmd->sg;
struct sgmap* psg = &srbcmd->sg; struct sgmap* psg = &srbcmd->sg;
byte_count = 0;
actual_fibsize = sizeof (struct aac_srb) + (((user_srbcmd->sg.count & 0xff) - 1) * sizeof (struct sgentry)); if (actual_fibsize64 == fibsize) {
if(actual_fibsize != fibsize){ // User made a mistake - should not continue struct user_sgmap64* usg = (struct user_sgmap64 *)upsg;
dprintk((KERN_DEBUG"aacraid: Bad Size specified in " for (i = 0; i < upsg->count; i++) {
"Raw SRB command calculated fibsize=%d " u64 addr;
"user_srbcmd->sg.count=%d aac_srb=%d sgentry=%d " void* p;
"issued fibsize=%d\n", /* Does this really need to be GFP_DMA? */
actual_fibsize, user_srbcmd->sg.count, p = kmalloc(usg->sg[i].count,GFP_KERNEL|__GFP_DMA);
sizeof(struct aac_srb), sizeof(struct sgentry), if(p == 0) {
fibsize)); dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
rcode = -EINVAL; usg->sg[i].count,i,usg->count));
rcode = -ENOMEM;
goto cleanup; goto cleanup;
} }
if ((data_dir == DMA_NONE) && upsg->count) { addr = (u64)usg->sg[i].addr[0];
dprintk((KERN_DEBUG"aacraid: SG with no direction specified in Raw SRB command\n")); addr += ((u64)usg->sg[i].addr[1]) << 32;
rcode = -EINVAL; sg_user[i] = (void __user *)(ptrdiff_t)addr;
sg_list[i] = p; // save so we can clean up later
sg_indx = i;
if( flags & SRB_DataOut ){
if(copy_from_user(p,sg_user[i],usg->sg[i].count)){
dprintk((KERN_DEBUG"aacraid: Could not copy sg data from user\n"));
rcode = -EFAULT;
goto cleanup; goto cleanup;
} }
}
addr = pci_map_single(dev->pdev, p, usg->sg[i].count, data_dir);
psg->sg[i].addr = cpu_to_le32(addr & 0xffffffff);
byte_count += usg->sg[i].count;
psg->sg[i].count = cpu_to_le32(usg->sg[i].count);
}
} else {
for (i = 0; i < upsg->count; i++) { for (i = 0; i < upsg->count; i++) {
dma_addr_t addr; dma_addr_t addr;
void* p; void* p;
@ -653,7 +706,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
rcode = -ENOMEM; rcode = -ENOMEM;
goto cleanup; goto cleanup;
} }
sg_user[i] = (void __user *)(long)upsg->sg[i].addr; sg_user[i] = (void __user *)(ptrdiff_t)upsg->sg[i].addr;
sg_list[i] = p; // save so we can clean up later sg_list[i] = p; // save so we can clean up later
sg_indx = i; sg_indx = i;
@ -669,8 +722,9 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
upsg->sg[i].count, data_dir); upsg->sg[i].count, data_dir);
psg->sg[i].addr = cpu_to_le32(addr); psg->sg[i].addr = cpu_to_le32(addr);
psg->sg[i].count = cpu_to_le32(upsg->sg[i].count);
byte_count += upsg->sg[i].count; byte_count += upsg->sg[i].count;
psg->sg[i].count = cpu_to_le32(upsg->sg[i].count);
}
} }
srbcmd->count = cpu_to_le32(byte_count); srbcmd->count = cpu_to_le32(byte_count);
psg->count = cpu_to_le32(sg_indx+1); psg->count = cpu_to_le32(sg_indx+1);
@ -689,7 +743,8 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
if( flags & SRB_DataIn ) { if( flags & SRB_DataIn ) {
for(i = 0 ; i <= sg_indx; i++){ for(i = 0 ; i <= sg_indx; i++){
byte_count = le32_to_cpu((dev->dac_support == 1) byte_count = le32_to_cpu(
(dev->adapter_info.options & AAC_OPT_SGMAP_HOST64)
? ((struct sgmap64*)&srbcmd->sg)->sg[i].count ? ((struct sgmap64*)&srbcmd->sg)->sg[i].count
: srbcmd->sg.sg[i].count); : srbcmd->sg.sg[i].count);
if(copy_to_user(sg_user[i], sg_list[i], byte_count)){ if(copy_to_user(sg_user[i], sg_list[i], byte_count)){