patch-2.4.20 linux-2.4.20/drivers/scsi/scsi_merge.c
Next file: linux-2.4.20/drivers/scsi/scsi_scan.c
Previous file: linux-2.4.20/drivers/scsi/scsi_lib.c
Back to the patch index
Back to the overall index
- Lines: 523
- Date:
Thu Nov 28 15:53:14 2002
- Orig file:
linux-2.4.19/drivers/scsi/scsi_merge.c
- Orig date:
Mon Feb 25 11:38:04 2002
diff -urN linux-2.4.19/drivers/scsi/scsi_merge.c linux-2.4.20/drivers/scsi/scsi_merge.c
@@ -6,6 +6,7 @@
* Based upon conversations with large numbers
* of people at Linux Expo.
* Support for dynamic DMA mapping: Jakub Jelinek (jakub@redhat.com).
+ * Support for highmem I/O: Jens Axboe <axboe@suse.de>
*/
/*
@@ -48,7 +49,6 @@
#include <linux/delay.h>
#include <linux/smp_lock.h>
-
#define __KERNEL_SYSCALLS__
#include <linux/unistd.h>
@@ -64,6 +64,12 @@
#include <scsi/scsi_ioctl.h>
/*
+ * scsi_malloc() can only dish out items of PAGE_SIZE or less, so we cannot
+ * build a request that requires an sg table allocation of more than that.
+ */
+static const int scsi_max_sg = PAGE_SIZE / sizeof(struct scatterlist);
+
+/*
* This means that bounce buffers cannot be allocated in chunks > PAGE_SIZE.
* Ultimately we should get away from using a dedicated DMA bounce buffer
* pool, and we should instead try and use kmalloc() instead. If we can
@@ -95,7 +101,7 @@
printk("Segment 0x%p, blocks %d, addr 0x%lx\n",
bh,
bh->b_size >> 9,
- virt_to_phys(bh->b_data - 1));
+ bh_phys(bh) - 1);
}
panic("Ththththaats all folks. Too dangerous to continue.\n");
}
@@ -216,11 +222,10 @@
* DMA capable host, make sure that a segment doesn't span
* the DMA threshold boundary.
*/
- if (dma_host &&
- virt_to_phys(bhnext->b_data) - 1 == ISA_DMA_THRESHOLD) {
+ if (dma_host && bh_phys(bhnext) - 1 == ISA_DMA_THRESHOLD) {
ret++;
reqsize = bhnext->b_size;
- } else if (CONTIGUOUS_BUFFERS(bh, bhnext)) {
+ } else if (blk_seg_merge_ok(bh, bhnext)) {
/*
* This one is OK. Let it go.
*/
@@ -234,8 +239,7 @@
* kind of screwed and we need to start
* another segment.
*/
- if( dma_host
- && virt_to_phys(bh->b_data) - 1 >= ISA_DMA_THRESHOLD
+ if( dma_host && bh_phys(bh) - 1 >= ISA_DMA_THRESHOLD
&& reqsize + bhnext->b_size > PAGE_SIZE )
{
ret++;
@@ -297,7 +301,7 @@
}
#define MERGEABLE_BUFFERS(X,Y) \
-(((((long)(X)->b_data+(X)->b_size)|((long)(Y)->b_data)) & \
+(((((long)bh_phys((X))+(X)->b_size)|((long)bh_phys((Y)))) & \
(DMA_CHUNK_SIZE - 1)) == 0)
#ifdef DMA_CHUNK_SIZE
@@ -399,11 +403,11 @@
{
unsigned int count;
unsigned int segment_size = 0;
- Scsi_Device *SDpnt;
- struct Scsi_Host *SHpnt;
+ Scsi_Device *SDpnt = q->queuedata;
+ struct Scsi_Host *SHpnt = SDpnt->host;
- SDpnt = (Scsi_Device *) q->queuedata;
- SHpnt = SDpnt->host;
+ if (max_segments > scsi_max_sg)
+ max_segments = scsi_max_sg;
#ifdef DMA_CHUNK_SIZE
if (max_segments > 64)
@@ -413,6 +417,9 @@
if ((req->nr_sectors + (bh->b_size >> 9)) > SHpnt->max_sectors)
return 0;
+ if (!BH_PHYS_4G(req->bhtail, bh))
+ return 0;
+
if (use_clustering) {
/*
* See if we can do this without creating another
@@ -420,14 +427,11 @@
* DMA capable host, make sure that a segment doesn't span
* the DMA threshold boundary.
*/
- if (dma_host &&
- virt_to_phys(req->bhtail->b_data) - 1 == ISA_DMA_THRESHOLD) {
+ if (dma_host && bh_phys(req->bhtail) - 1 == ISA_DMA_THRESHOLD)
goto new_end_segment;
- }
- if (CONTIGUOUS_BUFFERS(req->bhtail, bh)) {
+ if (BH_CONTIG(req->bhtail, bh)) {
#ifdef DMA_SEGMENT_SIZE_LIMITED
- if( dma_host
- && virt_to_phys(bh->b_data) - 1 >= ISA_DMA_THRESHOLD ) {
+ if (dma_host && bh_phys(bh) - 1 >= ISA_DMA_THRESHOLD) {
segment_size = 0;
count = __count_segments(req, use_clustering, dma_host, &segment_size);
if( segment_size + bh->b_size > PAGE_SIZE ) {
@@ -458,11 +462,11 @@
{
unsigned int count;
unsigned int segment_size = 0;
- Scsi_Device *SDpnt;
- struct Scsi_Host *SHpnt;
+ Scsi_Device *SDpnt = q->queuedata;
+ struct Scsi_Host *SHpnt = SDpnt->host;
- SDpnt = (Scsi_Device *) q->queuedata;
- SHpnt = SDpnt->host;
+ if (max_segments > scsi_max_sg)
+ max_segments = scsi_max_sg;
#ifdef DMA_CHUNK_SIZE
if (max_segments > 64)
@@ -472,6 +476,9 @@
if ((req->nr_sectors + (bh->b_size >> 9)) > SHpnt->max_sectors)
return 0;
+ if (!BH_PHYS_4G(bh, req->bh))
+ return 0;
+
if (use_clustering) {
/*
* See if we can do this without creating another
@@ -479,14 +486,12 @@
* DMA capable host, make sure that a segment doesn't span
* the DMA threshold boundary.
*/
- if (dma_host &&
- virt_to_phys(bh->b_data) - 1 == ISA_DMA_THRESHOLD) {
+ if (dma_host && bh_phys(bh) - 1 == ISA_DMA_THRESHOLD) {
goto new_start_segment;
}
- if (CONTIGUOUS_BUFFERS(bh, req->bh)) {
+ if (BH_CONTIG(bh, req->bh)) {
#ifdef DMA_SEGMENT_SIZE_LIMITED
- if( dma_host
- && virt_to_phys(bh->b_data) - 1 >= ISA_DMA_THRESHOLD ) {
+ if (dma_host && bh_phys(bh) - 1 >= ISA_DMA_THRESHOLD) {
segment_size = bh->b_size;
count = __count_segments(req, use_clustering, dma_host, &segment_size);
if( count != req->nr_segments ) {
@@ -593,8 +598,8 @@
int use_clustering,
int dma_host)
{
- Scsi_Device *SDpnt;
- struct Scsi_Host *SHpnt;
+ Scsi_Device *SDpnt = q->queuedata;
+ struct Scsi_Host *SHpnt = SDpnt->host;
/*
* First check if the either of the requests are re-queued
@@ -603,8 +608,8 @@
if (req->special || next->special)
return 0;
- SDpnt = (Scsi_Device *) q->queuedata;
- SHpnt = SDpnt->host;
+ if (max_segments > scsi_max_sg)
+ max_segments = scsi_max_sg;
#ifdef DMA_CHUNK_SIZE
if (max_segments > 64)
@@ -634,6 +639,9 @@
if ((req->nr_sectors + next->nr_sectors) > SHpnt->max_sectors)
return 0;
+ if (!BH_PHYS_4G(req->bhtail, next->bh))
+ return 0;
+
/*
* The main question is whether the two segments at the boundaries
* would be considered one or two.
@@ -645,18 +653,15 @@
* DMA capable host, make sure that a segment doesn't span
* the DMA threshold boundary.
*/
- if (dma_host &&
- virt_to_phys(req->bhtail->b_data) - 1 == ISA_DMA_THRESHOLD) {
+ if (dma_host && bh_phys(req->bhtail) - 1 == ISA_DMA_THRESHOLD)
goto dont_combine;
- }
#ifdef DMA_SEGMENT_SIZE_LIMITED
/*
* We currently can only allocate scatter-gather bounce
* buffers in chunks of PAGE_SIZE or less.
*/
- if (dma_host
- && CONTIGUOUS_BUFFERS(req->bhtail, next->bh)
- && virt_to_phys(req->bhtail->b_data) - 1 >= ISA_DMA_THRESHOLD )
+ if (dma_host && BH_CONTIG(req->bhtail, next->bh)
+ && bh_phys(req->bhtail) - 1 >= ISA_DMA_THRESHOLD)
{
int segment_size = 0;
int count = 0;
@@ -668,7 +673,7 @@
}
}
#endif
- if (CONTIGUOUS_BUFFERS(req->bhtail, next->bh)) {
+ if (BH_CONTIG(req->bhtail, next->bh)) {
/*
* This one is OK. Let it go.
*/
@@ -796,37 +801,13 @@
char * buff;
int count;
int i;
- struct request * req;
+ struct request * req = &SCpnt->request;
int sectors;
struct scatterlist * sgpnt;
int this_count;
void ** bbpnt;
/*
- * FIXME(eric) - don't inline this - it doesn't depend on the
- * integer flags. Come to think of it, I don't think this is even
- * needed any more. Need to play with it and see if we hit the
- * panic. If not, then don't bother.
- */
- if (!SCpnt->request.bh) {
- /*
- * Case of page request (i.e. raw device), or unlinked buffer
- * Typically used for swapping, but this isn't how we do
- * swapping any more.
- */
- panic("I believe this is dead code. If we hit this, I was wrong");
-#if 0
- SCpnt->request_bufflen = SCpnt->request.nr_sectors << 9;
- SCpnt->request_buffer = SCpnt->request.buffer;
- SCpnt->use_sg = 0;
- /*
- * FIXME(eric) - need to handle DMA here.
- */
-#endif
- return 1;
- }
- req = &SCpnt->request;
- /*
* First we need to know how many scatter gather segments are needed.
*/
if (!sg_count_valid) {
@@ -841,21 +822,27 @@
* buffer.
*/
if (dma_host && scsi_dma_free_sectors <= 10) {
- this_count = SCpnt->request.current_nr_sectors;
+ this_count = req->current_nr_sectors;
goto single_segment;
}
/*
- * Don't bother with scatter-gather if there is only one segment.
- */
- if (count == 1) {
- this_count = SCpnt->request.nr_sectors;
+ * we really want to use sg even for a single segment request,
+ * however some people just cannot be bothered to write decent
+ * driver code so we can't risk to break somebody making the
+ * assumption that sg requests will always contain at least 2
+ * segments. if the driver is 32-bit dma safe, then use sg for
+ * 1 entry anyways. if not, don't rely on the driver handling this
+ * case.
+ */
+ if (count == 1 && !SCpnt->host->highmem_io) {
+ this_count = req->nr_sectors;
goto single_segment;
}
- SCpnt->use_sg = count;
- /*
- * Allocate the actual scatter-gather table itself.
+ /*
+ * for sane drivers, use sg even for 1 entry request
*/
+ SCpnt->use_sg = count;
SCpnt->sglist_len = (SCpnt->use_sg * sizeof(struct scatterlist));
/* If we could potentially require ISA bounce buffers, allocate
@@ -875,15 +862,25 @@
* Now fill the scatter-gather table.
*/
if (!sgpnt) {
+#if 0
/*
* If we cannot allocate the scatter-gather table, then
* simply write the first buffer all by itself.
*/
printk("Warning - running *really* short on DMA buffers\n");
- this_count = SCpnt->request.current_nr_sectors;
+ this_count = req->current_nr_sectors;
goto single_segment;
+#else
+ /*
+ * it's probably better to simply always back off a little,
+ * and let some memory be returned to dma pool instead of
+ * always falling back to (slow) single segments
+ */
+ return 0;
+#endif
}
- /*
+
+ /*
* Next, walk the list, and fill in the addresses and sizes of
* each segment.
*/
@@ -900,13 +897,11 @@
SCpnt->bounce_buffers = bbpnt;
- for (count = 0, bh = SCpnt->request.bh;
- bh; bh = bh->b_reqnext) {
+ for (count = 0, bh = req->bh; bh; bh = bh->b_reqnext) {
if (use_clustering && bhprev != NULL) {
- if (dma_host &&
- virt_to_phys(bhprev->b_data) - 1 == ISA_DMA_THRESHOLD) {
+ if (dma_host && bh_phys(bhprev) - 1 == ISA_DMA_THRESHOLD) {
/* Nothing - fall through */
- } else if (CONTIGUOUS_BUFFERS(bhprev, bh)) {
+ } else if (blk_seg_merge_ok(bhprev, bh)) {
/*
* This one is OK. Let it go. Note that we
* do not have the ability to allocate
@@ -915,7 +910,7 @@
*/
if( dma_host ) {
#ifdef DMA_SEGMENT_SIZE_LIMITED
- if( virt_to_phys(bh->b_data) - 1 < ISA_DMA_THRESHOLD
+ if (bh_phys(bh) - 1 < ISA_DMA_THRESHOLD
|| sgpnt[count - 1].length + bh->b_size <= PAGE_SIZE ) {
sgpnt[count - 1].length += bh->b_size;
bhprev = bh;
@@ -934,13 +929,25 @@
}
}
}
- count++;
- sgpnt[count - 1].address = bh->b_data;
- sgpnt[count - 1].page = NULL;
- sgpnt[count - 1].length += bh->b_size;
- if (!dma_host) {
- SCpnt->request_bufflen += bh->b_size;
+
+ if (SCpnt->host->highmem_io) {
+ sgpnt[count].page = bh->b_page;
+ sgpnt[count].offset = bh_offset(bh);
+ sgpnt[count].address = NULL;
+ } else {
+ if (PageHighMem(bh->b_page))
+ BUG();
+
+ sgpnt[count].page = NULL;
+ sgpnt[count].address = bh->b_data;
}
+
+ sgpnt[count].length = bh->b_size;
+
+ if (!dma_host)
+ SCpnt->request_bufflen += bh->b_size;
+
+ count++;
bhprev = bh;
}
@@ -963,6 +970,10 @@
for (i = 0; i < count; i++) {
sectors = (sgpnt[i].length >> 9);
SCpnt->request_bufflen += sgpnt[i].length;
+ /*
+ * only done for dma_host, in which case .page is not
+ * set since it's guarenteed to be a low memory page
+ */
if (virt_to_phys(sgpnt[i].address) + sgpnt[i].length - 1 >
ISA_DMA_THRESHOLD) {
if( scsi_dma_free_sectors - sectors <= 10 ) {
@@ -998,7 +1009,7 @@
}
break;
}
- if (SCpnt->request.cmd == WRITE) {
+ if (req->cmd == WRITE) {
memcpy(sgpnt[i].address, bbpnt[i],
sgpnt[i].length);
}
@@ -1043,8 +1054,7 @@
* single-block requests if we had hundreds of free sectors.
*/
if( scsi_dma_free_sectors > 30 ) {
- for (this_count = 0, bh = SCpnt->request.bh;
- bh; bh = bh->b_reqnext) {
+ for (this_count = 0, bh = req->bh; bh; bh = bh->b_reqnext) {
if( scsi_dma_free_sectors - this_count < 30
|| this_count == sectors )
{
@@ -1057,21 +1067,32 @@
/*
* Yow! Take the absolute minimum here.
*/
- this_count = SCpnt->request.current_nr_sectors;
+ this_count = req->current_nr_sectors;
}
/*
* Now drop through into the single-segment case.
*/
- single_segment:
+single_segment:
+ /*
+ * for highmem cases, we have to revert to bouncing for single
+ * segments. rather just give up now and let the device starvation
+ * path reinitiate this i/o later
+ */
+ if (SCpnt->host->highmem_io)
+ return 0;
+
/*
* Come here if for any reason we choose to do this as a single
* segment. Possibly the entire request, or possibly a small
* chunk of the entire request.
*/
- bh = SCpnt->request.bh;
- buff = SCpnt->request.buffer;
+ bh = req->bh;
+ buff = req->buffer = bh->b_data;
+
+ if (PageHighMem(bh->b_page))
+ BUG();
if (dma_host) {
/*
@@ -1079,21 +1100,21 @@
* back and allocate a really small one - enough to satisfy
* the first buffer.
*/
- if (virt_to_phys(SCpnt->request.bh->b_data)
- + (this_count << 9) - 1 > ISA_DMA_THRESHOLD) {
+ if (bh_phys(bh) + (this_count << 9) - 1 > ISA_DMA_THRESHOLD) {
buff = (char *) scsi_malloc(this_count << 9);
if (!buff) {
printk("Warning - running low on DMA memory\n");
- this_count = SCpnt->request.current_nr_sectors;
+ this_count = req->current_nr_sectors;
buff = (char *) scsi_malloc(this_count << 9);
if (!buff) {
dma_exhausted(SCpnt, 0);
}
}
- if (SCpnt->request.cmd == WRITE)
- memcpy(buff, (char *) SCpnt->request.buffer, this_count << 9);
+ if (req->cmd == WRITE)
+ memcpy(buff, (char *) req->buffer, this_count << 9);
}
}
+
SCpnt->request_bufflen = this_count << 9;
SCpnt->request_buffer = buff;
SCpnt->use_sg = 0;
@@ -1132,21 +1153,11 @@
*/
void initialize_merge_fn(Scsi_Device * SDpnt)
{
- request_queue_t *q;
- struct Scsi_Host *SHpnt;
- SHpnt = SDpnt->host;
-
- q = &SDpnt->request_queue;
+ struct Scsi_Host *SHpnt = SDpnt->host;
+ request_queue_t *q = &SDpnt->request_queue;
+ dma64_addr_t bounce_limit;
/*
- * If the host has already selected a merge manager, then don't
- * pick a new one.
- */
-#if 0
- if (q->back_merge_fn && q->front_merge_fn)
- return;
-#endif
- /*
* If this host has an unlimited tablesize, then don't bother with a
* merge manager. The whole point of the operation is to make sure
* that requests don't grow too large, and this host isn't picky.
@@ -1178,4 +1189,20 @@
q->merge_requests_fn = scsi_merge_requests_fn_dc;
SDpnt->scsi_init_io_fn = scsi_init_io_vdc;
}
+
+ /*
+ * now enable highmem I/O, if appropriate
+ */
+ bounce_limit = BLK_BOUNCE_HIGH;
+ if (SHpnt->highmem_io && (SDpnt->type == TYPE_DISK)) {
+ if (!PCI_DMA_BUS_IS_PHYS)
+ /* Platforms with virtual-DMA translation
+ * hardware have no practical limit.
+ */
+ bounce_limit = BLK_BOUNCE_ANY;
+ else
+ bounce_limit = SHpnt->pci_dev->dma_mask;
+ }
+
+ blk_queue_bounce_limit(q, bounce_limit);
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)