| /* |
| * scsi_merge.c Copyright (C) 1999 Eric Youngdale |
| * |
| * SCSI queueing library. |
| * Initial versions: Eric Youngdale (eric@andante.org). |
| * 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> |
| */ |
| |
| /* |
| * This file contains queue management functions that are used by SCSI. |
| * We need to ensure that commands do not grow so large that they cannot |
| * be handled all at once by a host adapter. |
| */ |
| |
| #include <linux/config.h> |
| #include <linux/module.h> |
| |
| #include <linux/sched.h> |
| #include <linux/timer.h> |
| #include <linux/string.h> |
| #include <linux/slab.h> |
| #include <linux/ioport.h> |
| #include <linux/kernel.h> |
| #include <linux/stat.h> |
| #include <linux/blk.h> |
| #include <linux/interrupt.h> |
| #include <linux/delay.h> |
| #include <linux/smp_lock.h> |
| |
| |
| #define __KERNEL_SYSCALLS__ |
| |
| #include <linux/unistd.h> |
| |
| #include <asm/system.h> |
| #include <asm/irq.h> |
| #include <asm/dma.h> |
| #include <asm/io.h> |
| |
| #include "scsi.h" |
| #include "hosts.h" |
| #include "constants.h" |
| #include <scsi/scsi_ioctl.h> |
| |
| /* |
| * Function: scsi_init_io() |
| * |
| * Purpose: SCSI I/O initialize function. |
| * |
| * Arguments: SCpnt - Command descriptor we wish to initialize |
| * |
| * Returns: 1 on success. |
| * |
| * Lock status: |
| */ |
| int scsi_init_io(Scsi_Cmnd *SCpnt) |
| { |
| struct request *req = &SCpnt->request; |
| struct scatterlist *sgpnt; |
| int count, gfp_mask; |
| |
| /* |
| * First we need to know how many scatter gather segments are needed. |
| */ |
| count = req->nr_phys_segments; |
| |
| /* |
| * we used to not use scatter-gather for single segment request, |
| * but now we do (it makes highmem I/O easier to support without |
| * kmapping pages) |
| */ |
| SCpnt->use_sg = count; |
| |
| gfp_mask = GFP_NOIO; |
| if (in_interrupt()) |
| gfp_mask &= ~__GFP_WAIT; |
| |
| /* |
| * if sg table allocation fails, requeue request later. |
| */ |
| sgpnt = scsi_alloc_sgtable(SCpnt, gfp_mask); |
| if (!sgpnt) |
| return 0; |
| |
| SCpnt->request_buffer = (char *) sgpnt; |
| SCpnt->request_bufflen = req->nr_sectors << 9; |
| req->buffer = NULL; |
| |
| /* |
| * Next, walk the list, and fill in the addresses and sizes of |
| * each segment. |
| */ |
| count = blk_rq_map_sg(req->q, req, SCpnt->request_buffer); |
| |
| /* |
| * mapped well, send it off |
| */ |
| if (count <= SCpnt->use_sg) { |
| SCpnt->use_sg = count; |
| return 1; |
| } |
| |
| printk("Incorrect number of segments after building list\n"); |
| printk("counted %d, received %d\n", count, SCpnt->use_sg); |
| printk("req nr_sec %lu, cur_nr_sec %u\n", req->nr_sectors, req->current_nr_sectors); |
| |
| /* |
| * kill it. there should be no leftover blocks in this request |
| */ |
| SCpnt = scsi_end_request(SCpnt, 0, req->nr_sectors); |
| BUG_ON(SCpnt); |
| return 0; |
| } |
| |
| /* |
| * Function: scsi_initialize_merge_fn() |
| * |
| * Purpose: Initialize merge function for a host |
| * |
| * Arguments: SHpnt - Host descriptor. |
| * |
| * Returns: Nothing. |
| * |
| * Lock status: |
| * |
| * Notes: |
| */ |
| void scsi_initialize_merge_fn(Scsi_Device * SDpnt) |
| { |
| struct Scsi_Host *SHpnt = SDpnt->host; |
| request_queue_t *q = &SDpnt->request_queue; |
| u64 bounce_limit; |
| |
| /* |
| * The generic merging functions work just fine for us. |
| * 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 if (SHpnt->pci_dev) |
| bounce_limit = SHpnt->pci_dev->dma_mask; |
| } else if (SHpnt->unchecked_isa_dma) |
| bounce_limit = BLK_BOUNCE_ISA; |
| |
| blk_queue_bounce_limit(q, bounce_limit); |
| } |