summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/sd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/sd.c')
-rw-r--r--drivers/scsi/sd.c218
1 files changed, 171 insertions, 47 deletions
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 253f8d2dc..334e6fb60 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1,19 +1,25 @@
/*
* sd.c Copyright (C) 1992 Drew Eckhardt
- * Copyright (C) 1993, 1994 Eric Youngdale
- * Linux scsi disk driver by
- * Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * Linux scsi disk driver
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
*
* <drew@colorado.edu>
*
* Modified by Eric Youngdale ericy@cais.com to
* add scatter-gather, multiple outstanding request, and other
* enhancements.
+ *
+ * Modified by Eric Youngdale eric@aib.com to support loadable
+ * low-level scsi drivers.
*/
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/mm.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <asm/system.h>
@@ -45,10 +51,12 @@ static const char RCSid[] = "$Header:";
SC->device->type != TYPE_MOD)
struct hd_struct * sd;
+int revalidate_scsidisk(int dev, int maxusage);
-Scsi_Disk * rscsi_disks;
+Scsi_Disk * rscsi_disks = NULL;
static int * sd_sizes;
static int * sd_blocksizes;
+static int * sd_hardsizes; /* Hardware sector size */
extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
@@ -61,13 +69,14 @@ static void requeue_sd_request (Scsi_Cmnd * SCpnt);
static void sd_init(void);
static void sd_finish(void);
-static void sd_attach(Scsi_Device *);
+static int sd_attach(Scsi_Device *);
static int sd_detect(Scsi_Device *);
+static void sd_detach(Scsi_Device *);
struct Scsi_Device_Template sd_template = {NULL, "disk", "sd", TYPE_DISK,
SCSI_DISK_MAJOR, 0, 0, 0, 1,
sd_detect, sd_init,
- sd_finish, sd_attach, NULL};
+ sd_finish, sd_attach, sd_detach};
static int sd_open(struct inode * inode, struct file * filp)
{
@@ -80,7 +89,8 @@ static int sd_open(struct inode * inode, struct file * filp)
/* Make sure that only one process can do a check_change_disk at one time.
This is also used to lock out further access when the partition table is being re-read. */
- while (rscsi_disks[target].device->busy);
+ while (rscsi_disks[target].device->busy)
+ barrier();
if(rscsi_disks[target].device->removable) {
check_disk_change(inode->i_rdev);
@@ -88,7 +98,16 @@ static int sd_open(struct inode * inode, struct file * filp)
if(!rscsi_disks[target].device->access_count)
sd_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
};
+ /*
+ * See if we are requesting a non-existent partition. Do this
+ * after checking for disk change.
+ */
+ if(sd_sizes[MINOR(inode->i_rdev)] == 0)
+ return -ENXIO;
+
rscsi_disks[target].device->access_count++;
+ if (rscsi_disks[target].device->host->hostt->usage_count)
+ (*rscsi_disks[target].device->host->hostt->usage_count)++;
return 0;
}
@@ -100,6 +119,8 @@ static void sd_release(struct inode * inode, struct file * file)
target = DEVICE_NR(MINOR(inode->i_rdev));
rscsi_disks[target].device->access_count--;
+ if (rscsi_disks[target].device->host->hostt->usage_count)
+ (*rscsi_disks[target].device->host->hostt->usage_count)--;
if(rscsi_disks[target].device->removable) {
if(!rscsi_disks[target].device->access_count)
@@ -146,7 +167,10 @@ static void sd_geninit (void)
for (i = 0; i < sd_template.dev_max; ++i)
if(rscsi_disks[i].device)
sd[i << 4].nr_sects = rscsi_disks[i].capacity;
+#if 0
+ /* No longer needed - we keep track of this as we attach/detach */
sd_gendisk.nr_real = sd_template.dev_max;
+#endif
}
/*
@@ -332,11 +356,14 @@ static void do_sd_request (void)
{
Scsi_Cmnd * SCpnt = NULL;
struct request * req = NULL;
+ unsigned long flags;
int flag = 0;
+
+ save_flags(flags);
while (1==1){
cli();
if (CURRENT != NULL && CURRENT->dev == -1) {
- sti();
+ restore_flags(flags);
return;
};
@@ -358,7 +385,13 @@ static void do_sd_request (void)
SCpnt = allocate_device(&CURRENT,
rscsi_disks[DEVICE_NR(MINOR(CURRENT->dev))].device, 0);
else SCpnt = NULL;
- sti();
+
+ /*
+ * The following restore_flags leads to latency problems. FIXME.
+ * Using a "sti()" gets rid of the latency problems but causes
+ * race conditions and crashes.
+ */
+ restore_flags(flags);
/* This is a performance enhancement. We dig down into the request list and
try and find a queueable request (i.e. device not busy, and host able to
@@ -385,7 +418,7 @@ static void do_sd_request (void)
else
req1->next = req->next;
};
- sti();
+ restore_flags(flags);
};
if (!SCpnt) return; /* Could not find anything to do */
@@ -493,9 +526,9 @@ repeat:
if (contiguous && SCpnt->request.bh &&
- ((int) SCpnt->request.bh->b_data) + (SCpnt->request.nr_sectors << 9) - 1 >
+ ((long) SCpnt->request.bh->b_data) + (SCpnt->request.nr_sectors << 9) - 1 >
ISA_DMA_THRESHOLD && SCpnt->host->unchecked_isa_dma) {
- if(((int) SCpnt->request.bh->b_data) > ISA_DMA_THRESHOLD)
+ if(((long) SCpnt->request.bh->b_data) > ISA_DMA_THRESHOLD)
bounce_buffer = (char *) scsi_malloc(bounce_size);
if(!bounce_buffer) contiguous = 0;
};
@@ -553,7 +586,7 @@ repeat:
if(!bhp || !CONTIGUOUS_BUFFERS(bhp,bh) ||
!CLUSTERABLE_DEVICE(SCpnt) ||
(SCpnt->host->unchecked_isa_dma &&
- ((unsigned int) bh->b_data-1) == ISA_DMA_THRESHOLD)) {
+ ((unsigned long) bh->b_data-1) == ISA_DMA_THRESHOLD)) {
if (count < SCpnt->host->sg_tablesize) count++;
else break;
};
@@ -592,7 +625,7 @@ repeat:
sgpnt[count].length += bh->b_size;
counted += bh->b_size >> 9;
- if (((int) sgpnt[count].address) + sgpnt[count].length - 1 >
+ if (((long) sgpnt[count].address) + sgpnt[count].length - 1 >
ISA_DMA_THRESHOLD && (SCpnt->host->unchecked_isa_dma) &&
!sgpnt[count].alt_address) {
sgpnt[count].alt_address = sgpnt[count].address;
@@ -635,7 +668,7 @@ repeat:
if(bhp && CONTIGUOUS_BUFFERS(bh,bhp) && CLUSTERABLE_DEVICE(SCpnt)) {
char * tmp;
- if (((int) sgpnt[count].address) + sgpnt[count].length +
+ if (((long) sgpnt[count].address) + sgpnt[count].length +
bhp->b_size - 1 > ISA_DMA_THRESHOLD &&
(SCpnt->host->unchecked_isa_dma) &&
!sgpnt[count].alt_address) continue;
@@ -691,7 +724,7 @@ repeat:
/* Now handle the possibility of DMA to addresses > 16Mb */
if(SCpnt->use_sg == 0){
- if (((int) buff) + (this_count << 9) - 1 > ISA_DMA_THRESHOLD &&
+ if (((long) buff) + (this_count << 9) - 1 > ISA_DMA_THRESHOLD &&
(SCpnt->host->unchecked_isa_dma)) {
if(bounce_buffer)
buff = bounce_buffer;
@@ -818,7 +851,7 @@ static int sd_init_onedisk(int i)
{
unsigned char cmd[10];
unsigned char *buffer;
- char spintime;
+ unsigned long spintime;
int the_result, retries;
Scsi_Cmnd * SCpnt;
@@ -847,7 +880,7 @@ static int sd_init_onedisk(int i)
512, sd_init_done, SD_TIMEOUT,
MAX_RETRIES);
- while(SCpnt->request.dev != 0xfffe);
+ while(SCpnt->request.dev != 0xfffe) barrier();
the_result = SCpnt->result;
@@ -873,7 +906,7 @@ static int sd_init_onedisk(int i)
512, sd_init_done, SD_TIMEOUT,
MAX_RETRIES);
- while(SCpnt->request.dev != 0xfffe);
+ while(SCpnt->request.dev != 0xfffe) barrier();
spintime = jiffies;
};
@@ -882,7 +915,7 @@ static int sd_init_onedisk(int i)
while(jiffies < time1 + HZ); /* Wait 1 second for next try */
printk( "." );
};
- } while(the_result && spintime && spintime+5000 > jiffies);
+ } while(the_result && spintime && spintime+50*HZ > jiffies);
if (spintime) {
if (the_result)
printk( "not responding...\n" );
@@ -909,7 +942,7 @@ static int sd_init_onedisk(int i)
MAX_RETRIES);
if (current == task[0])
- while(SCpnt->request.dev != 0xfffe);
+ while(SCpnt->request.dev != 0xfffe) barrier();
else
if (SCpnt->request.dev != 0xfffe){
struct semaphore sem = MUTEX_LOCKED;
@@ -994,6 +1027,21 @@ static int sd_init_onedisk(int i)
return i;
};
}
+ {
+ /*
+ The msdos fs need to know the hardware sector size
+ So I have created this table. See ll_rw_blk.c
+ Jacques Gelinas (Jacques@solucorp.qc.ca)
+ */
+ int m;
+ int hard_sector = rscsi_disks[i].sector_size;
+ /* There is 16 minor allocated for each devices */
+ for (m=i<<4; m<((i+1)<<4); m++){
+ sd_hardsizes[m] = hard_sector;
+ }
+ printk ("SCSI Hardware sector size is %d bytes on device sd%c\n"
+ ,hard_sector,i+'a');
+ }
if(rscsi_disks[i].sector_size == 1024)
rscsi_disks[i].capacity <<= 1; /* Change this into 512 byte sectors */
if(rscsi_disks[i].sector_size == 256)
@@ -1028,25 +1076,33 @@ static void sd_init()
}
/* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return;
+ if(rscsi_disks) return;
- sd_template.dev_max = sd_template.dev_noticed;
+ sd_template.dev_max = sd_template.dev_noticed + SD_EXTRA_DEVS;
rscsi_disks = (Scsi_Disk *)
- scsi_init_malloc(sd_template.dev_max * sizeof(Scsi_Disk));
+ scsi_init_malloc(sd_template.dev_max * sizeof(Scsi_Disk), GFP_ATOMIC);
memset(rscsi_disks, 0, sd_template.dev_max * sizeof(Scsi_Disk));
sd_sizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
- sizeof(int));
+ sizeof(int), GFP_ATOMIC);
memset(sd_sizes, 0, (sd_template.dev_max << 4) * sizeof(int));
sd_blocksizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
- sizeof(int));
- for(i=0;i<(sd_template.dev_max << 4);i++) sd_blocksizes[i] = 1024;
- blksize_size[MAJOR_NR] = sd_blocksizes;
+ sizeof(int), GFP_ATOMIC);
+
+ sd_hardsizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(struct hd_struct), GFP_ATOMIC);
+ for(i=0;i<(sd_template.dev_max << 4);i++){
+ sd_blocksizes[i] = 1024;
+ sd_hardsizes[i] = 512;
+ }
+ blksize_size[MAJOR_NR] = sd_blocksizes;
+ hardsect_size[MAJOR_NR] = sd_hardsizes;
sd = (struct hd_struct *) scsi_init_malloc((sd_template.dev_max << 4) *
- sizeof(struct hd_struct));
+ sizeof(struct hd_struct),
+ GFP_ATOMIC);
sd_gendisk.max_nr = sd_template.dev_max;
@@ -1060,28 +1116,37 @@ static void sd_finish()
{
int i;
- for (i = 0; i < sd_template.dev_max; ++i)
- if (rscsi_disks[i].device) i = sd_init_onedisk(i);
-
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ sd_gendisk.next = gendisk_head;
+ gendisk_head = &sd_gendisk;
+
+ for (i = 0; i < sd_template.dev_max; ++i)
+ if (!rscsi_disks[i].capacity &&
+ rscsi_disks[i].device)
+ {
+ i = sd_init_onedisk(i);
+ if (scsi_loadable_module_flag
+ && !rscsi_disks[i].has_part_table) {
+ sd_sizes[i << 4] = rscsi_disks[i].capacity;
+ revalidate_scsidisk(i << 4, 0);
+ }
+ rscsi_disks[i].has_part_table = 1;
+ }
+
/* If our host adapter is capable of scatter-gather, then we increase
the read-ahead to 16 blocks (32 sectors). If not, we use
a two block (4 sector) read ahead. */
- if(rscsi_disks[0].device->host->sg_tablesize)
+ if(rscsi_disks[0].device && rscsi_disks[0].device->host->sg_tablesize)
read_ahead[MAJOR_NR] = 120;
/* 64 sector read-ahead */
else
read_ahead[MAJOR_NR] = 4; /* 4 sector read-ahead */
- sd_gendisk.next = gendisk_head;
- gendisk_head = &sd_gendisk;
return;
}
static int sd_detect(Scsi_Device * SDp){
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return 0;
if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0;
printk("Detected scsi disk sd%c at scsi%d, id %d, lun %d\n",
@@ -1092,17 +1157,17 @@ static int sd_detect(Scsi_Device * SDp){
}
-static void sd_attach(Scsi_Device * SDp){
+static int sd_attach(Scsi_Device * SDp){
Scsi_Disk * dpnt;
int i;
- /* We do not support attaching loadable devices yet. */
- if(scsi_loadable_module_flag) return;
- if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return;
-
- if(sd_template.nr_dev >= sd_template.dev_max)
- panic ("scsi_devices corrupt (sd)");
+ if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0;
+ if(sd_template.nr_dev >= sd_template.dev_max) {
+ SDp->attached--;
+ return 1;
+ }
+
for(dpnt = rscsi_disks, i=0; i<sd_template.dev_max; i++, dpnt++)
if(!dpnt->device) break;
@@ -1110,8 +1175,11 @@ static void sd_attach(Scsi_Device * SDp){
SDp->scsi_request_fn = do_sd_request;
rscsi_disks[i].device = SDp;
+ rscsi_disks[i].has_part_table = 0;
sd_template.nr_dev++;
-};
+ sd_gendisk.nr_real++;
+ return 0;
+}
#define DEVICE_BUSY rscsi_disks[target].device->busy
#define USAGE rscsi_disks[target].device->access_count
@@ -1129,6 +1197,7 @@ static void sd_attach(Scsi_Device * SDp){
int revalidate_scsidisk(int dev, int maxusage){
int target, major;
struct gendisk * gdev;
+ unsigned long flags;
int max_p;
int start;
int i;
@@ -1136,14 +1205,15 @@ int revalidate_scsidisk(int dev, int maxusage){
target = DEVICE_NR(MINOR(dev));
gdev = &GENDISK_STRUCT;
+ save_flags(flags);
cli();
if (DEVICE_BUSY || USAGE > maxusage) {
- sti();
+ restore_flags(flags);
printk("Device busy for revalidation (usage=%d)\n", USAGE);
return -EBUSY;
};
DEVICE_BUSY = 1;
- sti();
+ restore_flags(flags);
max_p = gdev->max_p;
start = target << gdev->minor_shift;
@@ -1172,3 +1242,57 @@ static int fop_revalidate_scsidisk(dev_t dev){
return revalidate_scsidisk(dev, 0);
}
+
+static void sd_detach(Scsi_Device * SDp)
+{
+ Scsi_Disk * dpnt;
+ int i;
+ int max_p;
+ int major;
+ int start;
+
+ for(dpnt = rscsi_disks, i=0; i<sd_template.dev_max; i++, dpnt++)
+ if(dpnt->device == SDp) {
+
+ /* If we are disconnecting a disk driver, sync and invalidate everything */
+ max_p = sd_gendisk.max_p;
+ start = i << sd_gendisk.minor_shift;
+ major = MAJOR_NR << 8;
+
+ for (i=max_p - 1; i >=0 ; i--) {
+ sync_dev(major | start | i);
+ invalidate_inodes(major | start | i);
+ invalidate_buffers(major | start | i);
+ sd_gendisk.part[start+i].start_sect = 0;
+ sd_gendisk.part[start+i].nr_sects = 0;
+ sd_sizes[start+i] = 0;
+ };
+
+ dpnt->has_part_table = 0;
+ dpnt->device = NULL;
+ dpnt->capacity = 0;
+ SDp->attached--;
+ sd_template.dev_noticed--;
+ sd_template.nr_dev--;
+ sd_gendisk.nr_real--;
+ return;
+ }
+ return;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */