Commit 09b12dd4 authored by Stefan Richter's avatar Stefan Richter

firewire: fw-sbp2: enforce s/g segment size limit

1. We don't need to round the SBP-2 segment size limit down to a
   multiple of 4 kB (0xffff -> 0xf000).  It is only necessary to
   ensure quadlet alignment (0xffff -> 0xfffc).

2. Use dma_set_max_seg_size() to tell the DMA mapping infrastructure
   and the block IO layer about the restriction.  This way we can
   remove the size checks and segment splitting in the queuecommand
   path.

   This assumes that no other code in the firewire stack uses
   dma_map_sg() with conflicting requirements.  It furthermore assumes
   that the controller device's platform actually allows us to set the
   segment size to our liking.  Assert the latter with a BUG_ON().

3. Also use blk_queue_max_segment_size() to tell the block IO layer
   about it.  It cannot know it because our scsi_add_host() does not
   point to the FireWire controller's device.

Thanks to Grant Grundler and FUJITA Tomonori for advice.
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent 1e119fa9
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
*/ */
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <linux/bug.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
...@@ -181,10 +182,16 @@ struct sbp2_target { ...@@ -181,10 +182,16 @@ struct sbp2_target {
#define SBP2_MAX_LOGIN_ORB_TIMEOUT 40000U /* Timeout in ms */ #define SBP2_MAX_LOGIN_ORB_TIMEOUT 40000U /* Timeout in ms */
#define SBP2_ORB_TIMEOUT 2000U /* Timeout in ms */ #define SBP2_ORB_TIMEOUT 2000U /* Timeout in ms */
#define SBP2_ORB_NULL 0x80000000 #define SBP2_ORB_NULL 0x80000000
#define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000
#define SBP2_RETRY_LIMIT 0xf /* 15 retries */ #define SBP2_RETRY_LIMIT 0xf /* 15 retries */
#define SBP2_CYCLE_LIMIT (0xc8 << 12) /* 200 125us cycles */ #define SBP2_CYCLE_LIMIT (0xc8 << 12) /* 200 125us cycles */
/*
* The default maximum s/g segment size of a FireWire controller is
* usually 0x10000, but SBP-2 only allows 0xffff. Since buffers have to
* be quadlet-aligned, we set the length limit to 0xffff & ~3.
*/
#define SBP2_MAX_SEG_SIZE 0xfffc
/* Unit directory keys */ /* Unit directory keys */
#define SBP2_CSR_UNIT_CHARACTERISTICS 0x3a #define SBP2_CSR_UNIT_CHARACTERISTICS 0x3a
#define SBP2_CSR_FIRMWARE_REVISION 0x3c #define SBP2_CSR_FIRMWARE_REVISION 0x3c
...@@ -1099,6 +1106,10 @@ static int sbp2_probe(struct device *dev) ...@@ -1099,6 +1106,10 @@ static int sbp2_probe(struct device *dev)
struct Scsi_Host *shost; struct Scsi_Host *shost;
u32 model, firmware_revision; u32 model, firmware_revision;
if (dma_get_max_seg_size(device->card->device) > SBP2_MAX_SEG_SIZE)
BUG_ON(dma_set_max_seg_size(device->card->device,
SBP2_MAX_SEG_SIZE));
shost = scsi_host_alloc(&scsi_driver_template, sizeof(*tgt)); shost = scsi_host_alloc(&scsi_driver_template, sizeof(*tgt));
if (shost == NULL) if (shost == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -1347,14 +1358,12 @@ static int ...@@ -1347,14 +1358,12 @@ static int
sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device, sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device,
struct sbp2_logical_unit *lu) struct sbp2_logical_unit *lu)
{ {
struct scatterlist *sg; struct scatterlist *sg = scsi_sglist(orb->cmd);
int sg_len, l, i, j, count; int i, n;
dma_addr_t sg_addr;
n = dma_map_sg(device->card->device, sg, scsi_sg_count(orb->cmd),
sg = scsi_sglist(orb->cmd); orb->cmd->sc_data_direction);
count = dma_map_sg(device->card->device, sg, scsi_sg_count(orb->cmd), if (n == 0)
orb->cmd->sc_data_direction);
if (count == 0)
goto fail; goto fail;
/* /*
...@@ -1364,7 +1373,7 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device, ...@@ -1364,7 +1373,7 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device,
* as the second generation iPod which doesn't support page * as the second generation iPod which doesn't support page
* tables. * tables.
*/ */
if (count == 1 && sg_dma_len(sg) < SBP2_MAX_SG_ELEMENT_LENGTH) { if (n == 1) {
orb->request.data_descriptor.high = orb->request.data_descriptor.high =
cpu_to_be32(lu->tgt->address_high); cpu_to_be32(lu->tgt->address_high);
orb->request.data_descriptor.low = orb->request.data_descriptor.low =
...@@ -1374,29 +1383,9 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device, ...@@ -1374,29 +1383,9 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device,
return 0; return 0;
} }
/* for_each_sg(sg, sg, n, i) {
* Convert the scatterlist to an sbp2 page table. If any orb->page_table[i].high = cpu_to_be32(sg_dma_len(sg) << 16);
* scatterlist entries are too big for sbp2, we split them as we orb->page_table[i].low = cpu_to_be32(sg_dma_address(sg));
* go. Even if we ask the block I/O layer to not give us sg
* elements larger than 65535 bytes, some IOMMUs may merge sg elements
* during DMA mapping, and Linux currently doesn't prevent this.
*/
for (i = 0, j = 0; i < count; i++, sg = sg_next(sg)) {
sg_len = sg_dma_len(sg);
sg_addr = sg_dma_address(sg);
while (sg_len) {
/* FIXME: This won't get us out of the pinch. */
if (unlikely(j >= ARRAY_SIZE(orb->page_table))) {
fw_error("page table overflow\n");
goto fail_page_table;
}
l = min(sg_len, SBP2_MAX_SG_ELEMENT_LENGTH);
orb->page_table[j].low = cpu_to_be32(sg_addr);
orb->page_table[j].high = cpu_to_be32(l << 16);
sg_addr += l;
sg_len -= l;
j++;
}
} }
orb->page_table_bus = orb->page_table_bus =
...@@ -1415,13 +1404,13 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device, ...@@ -1415,13 +1404,13 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device,
orb->request.data_descriptor.high = cpu_to_be32(lu->tgt->address_high); orb->request.data_descriptor.high = cpu_to_be32(lu->tgt->address_high);
orb->request.data_descriptor.low = cpu_to_be32(orb->page_table_bus); orb->request.data_descriptor.low = cpu_to_be32(orb->page_table_bus);
orb->request.misc |= cpu_to_be32(COMMAND_ORB_PAGE_TABLE_PRESENT | orb->request.misc |= cpu_to_be32(COMMAND_ORB_PAGE_TABLE_PRESENT |
COMMAND_ORB_DATA_SIZE(j)); COMMAND_ORB_DATA_SIZE(n));
return 0; return 0;
fail_page_table: fail_page_table:
dma_unmap_sg(device->card->device, sg, scsi_sg_count(orb->cmd), dma_unmap_sg(device->card->device, scsi_sglist(orb->cmd),
orb->cmd->sc_data_direction); scsi_sg_count(orb->cmd), orb->cmd->sc_data_direction);
fail: fail:
return -ENOMEM; return -ENOMEM;
} }
...@@ -1542,6 +1531,8 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev) ...@@ -1542,6 +1531,8 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
if (lu->tgt->workarounds & SBP2_WORKAROUND_128K_MAX_TRANS) if (lu->tgt->workarounds & SBP2_WORKAROUND_128K_MAX_TRANS)
blk_queue_max_sectors(sdev->request_queue, 128 * 1024 / 512); blk_queue_max_sectors(sdev->request_queue, 128 * 1024 / 512);
blk_queue_max_segment_size(sdev->request_queue, SBP2_MAX_SEG_SIZE);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment