diff options
Diffstat (limited to 'drivers/scsi')
32 files changed, 1690 insertions, 426 deletions
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 12c208fb18c5..b3ae796eb624 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1,5 +1,11 @@ menu "SCSI device support" +config RAID_ATTRS + tristate "RAID Transport Class" + default n + ---help--- + Provides RAID + config SCSI tristate "SCSI device support" ---help--- diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 3746fb9fa2f5..85f9e6bb34b9 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -22,6 +22,8 @@ subdir-$(CONFIG_PCMCIA) += pcmcia obj-$(CONFIG_SCSI) += scsi_mod.o +obj-$(CONFIG_RAID_ATTRS) += raid_class.o + # --- NOTE ORDERING HERE --- # For kernel non-modular link, transport attributes need to # be initialised before drivers diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index 83bfab73ff65..a8e3dfcd0dc7 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -972,7 +972,7 @@ static int aac_read(struct scsi_cmnd * scsicmd, int cid) fibsize = sizeof(struct aac_read64) + ((le32_to_cpu(readcmd->sg.count) - 1) * sizeof (struct sgentry64)); - BUG_ON (fibsize > (sizeof(struct hw_fib) - + BUG_ON (fibsize > (dev->max_fib_size - sizeof(struct aac_fibhdr))); /* * Now send the Fib to the adapter diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c index 3feb739cd554..6b6d4e287793 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm.c @@ -48,12 +48,6 @@ static struct scsi_transport_template *ahd_linux_transport_template = NULL; -/* - * Include aiclib.c as part of our - * "module dependencies are hard" work around. - */ -#include "aiclib.c" - #include <linux/init.h> /* __setup */ #include <linux/mm.h> /* For fetching system memory size */ #include <linux/blkdev.h> /* For block_size() */ @@ -372,8 +366,6 @@ static int ahd_linux_run_command(struct ahd_softc*, struct ahd_linux_device *, struct scsi_cmnd *); static void ahd_linux_setup_tag_info_global(char *p); -static aic_option_callback_t ahd_linux_setup_tag_info; -static aic_option_callback_t ahd_linux_setup_iocell_info; static int aic79xx_setup(char *c); static int ahd_linux_unit; @@ -907,6 +899,86 @@ ahd_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value) } } +static char * +ahd_parse_brace_option(char *opt_name, char *opt_arg, char *end, int depth, + void (*callback)(u_long, int, int, int32_t), + u_long callback_arg) +{ + char *tok_end; + char *tok_end2; + int i; + int instance; + int targ; + int done; + char tok_list[] = {'.', ',', '{', '}', '\0'}; + + /* All options use a ':' name/arg separator */ + if (*opt_arg != ':') + return (opt_arg); + opt_arg++; + instance = -1; + targ = -1; + done = FALSE; + /* + * Restore separator that may be in + * the middle of our option argument. + */ + tok_end = strchr(opt_arg, '\0'); + if (tok_end < end) + *tok_end = ','; + while (!done) { + switch (*opt_arg) { + case '{': + if (instance == -1) { + instance = 0; + } else { + if (depth > 1) { + if (targ == -1) + targ = 0; + } else { + printf("Malformed Option %s\n", + opt_name); + done = TRUE; + } + } + opt_arg++; + break; + case '}': + if (targ != -1) + targ = -1; + else if (instance != -1) + instance = -1; + opt_arg++; + break; + case ',': + case '.': + if (instance == -1) + done = TRUE; + else if (targ >= 0) + targ++; + else if (instance >= 0) + instance++; + opt_arg++; + break; + case '\0': + done = TRUE; + break; + default: + tok_end = end; + for (i = 0; tok_list[i]; i++) { + tok_end2 = strchr(opt_arg, tok_list[i]); + if ((tok_end2) && (tok_end2 < tok_end)) + tok_end = tok_end2; + } + callback(callback_arg, instance, targ, + simple_strtol(opt_arg, NULL, 0)); + opt_arg = tok_end; + break; + } + } + return (opt_arg); +} + /* * Handle Linux boot parameters. This routine allows for assigning a value * to a parameter with a ':' between the parameter and the value. @@ -964,18 +1036,18 @@ aic79xx_setup(char *s) if (strncmp(p, "global_tag_depth", n) == 0) { ahd_linux_setup_tag_info_global(p + n); } else if (strncmp(p, "tag_info", n) == 0) { - s = aic_parse_brace_option("tag_info", p + n, end, + s = ahd_parse_brace_option("tag_info", p + n, end, 2, ahd_linux_setup_tag_info, 0); } else if (strncmp(p, "slewrate", n) == 0) { - s = aic_parse_brace_option("slewrate", + s = ahd_parse_brace_option("slewrate", p + n, end, 1, ahd_linux_setup_iocell_info, AIC79XX_SLEWRATE_INDEX); } else if (strncmp(p, "precomp", n) == 0) { - s = aic_parse_brace_option("precomp", + s = ahd_parse_brace_option("precomp", p + n, end, 1, ahd_linux_setup_iocell_info, AIC79XX_PRECOMP_INDEX); } else if (strncmp(p, "amplitude", n) == 0) { - s = aic_parse_brace_option("amplitude", + s = ahd_parse_brace_option("amplitude", p + n, end, 1, ahd_linux_setup_iocell_info, AIC79XX_AMPLITUDE_INDEX); } else if (p[n] == ':') { diff --git a/drivers/scsi/aic7xxx/aic79xx_proc.c b/drivers/scsi/aic7xxx/aic79xx_proc.c index 32be1f55998c..39a27840fce6 100644 --- a/drivers/scsi/aic7xxx/aic79xx_proc.c +++ b/drivers/scsi/aic7xxx/aic79xx_proc.c @@ -53,6 +53,49 @@ static void ahd_dump_device_state(struct info_str *info, static int ahd_proc_write_seeprom(struct ahd_softc *ahd, char *buffer, int length); +/* + * Table of syncrates that don't follow the "divisible by 4" + * rule. This table will be expanded in future SCSI specs. + */ +static struct { + u_int period_factor; + u_int period; /* in 100ths of ns */ +} scsi_syncrates[] = { + { 0x08, 625 }, /* FAST-160 */ + { 0x09, 1250 }, /* FAST-80 */ + { 0x0a, 2500 }, /* FAST-40 40MHz */ + { 0x0b, 3030 }, /* FAST-40 33MHz */ + { 0x0c, 5000 } /* FAST-20 */ +}; + +/* + * Return the frequency in kHz corresponding to the given + * sync period factor. + */ +static u_int +ahd_calc_syncsrate(u_int period_factor) +{ + int i; + int num_syncrates; + + num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]); + /* See if the period is in the "exception" table */ + for (i = 0; i < num_syncrates; i++) { + + if (period_factor == scsi_syncrates[i].period_factor) { + /* Period in kHz */ + return (100000000 / scsi_syncrates[i].period); + } + } + + /* + * Wasn't in the table, so use the standard + * 4 times conversion. + */ + return (10000000 / (period_factor * 4 * 10)); +} + + static void copy_mem_info(struct info_str *info, char *data, int len) { @@ -109,7 +152,7 @@ ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo) speed = 3300; freq = 0; if (tinfo->offset != 0) { - freq = aic_calc_syncsrate(tinfo->period); + freq = ahd_calc_syncsrate(tinfo->period); speed = freq; } speed *= (0x01 << tinfo->width); diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index 54173887e160..c932b3b94490 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -125,12 +125,6 @@ static struct scsi_transport_template *ahc_linux_transport_template = NULL; -/* - * Include aiclib.c as part of our - * "module dependencies are hard" work around. - */ -#include "aiclib.c" - #include <linux/init.h> /* __setup */ #include <linux/mm.h> /* For fetching system memory size */ #include <linux/blkdev.h> /* For block_size() */ @@ -391,7 +385,6 @@ static int ahc_linux_run_command(struct ahc_softc*, struct ahc_linux_device *, struct scsi_cmnd *); static void ahc_linux_setup_tag_info_global(char *p); -static aic_option_callback_t ahc_linux_setup_tag_info; static int aic7xxx_setup(char *s); static int ahc_linux_unit; @@ -920,6 +913,86 @@ ahc_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value) } } +static char * +ahc_parse_brace_option(char *opt_name, char *opt_arg, char *end, int depth, + void (*callback)(u_long, int, int, int32_t), + u_long callback_arg) +{ + char *tok_end; + char *tok_end2; + int i; + int instance; + int targ; + int done; + char tok_list[] = {'.', ',', '{', '}', '\0'}; + + /* All options use a ':' name/arg separator */ + if (*opt_arg != ':') + return (opt_arg); + opt_arg++; + instance = -1; + targ = -1; + done = FALSE; + /* + * Restore separator that may be in + * the middle of our option argument. + */ + tok_end = strchr(opt_arg, '\0'); + if (tok_end < end) + *tok_end = ','; + while (!done) { + switch (*opt_arg) { + case '{': + if (instance == -1) { + instance = 0; + } else { + if (depth > 1) { + if (targ == -1) + targ = 0; + } else { + printf("Malformed Option %s\n", + opt_name); + done = TRUE; + } + } + opt_arg++; + break; + case '}': + if (targ != -1) + targ = -1; + else if (instance != -1) + instance = -1; + opt_arg++; + break; + case ',': + case '.': + if (instance == -1) + done = TRUE; + else if (targ >= 0) + targ++; + else if (instance >= 0) + instance++; + opt_arg++; + break; + case '\0': + done = TRUE; + break; + default: + tok_end = end; + for (i = 0; tok_list[i]; i++) { + tok_end2 = strchr(opt_arg, tok_list[i]); + if ((tok_end2) && (tok_end2 < tok_end)) + tok_end = tok_end2; + } + callback(callback_arg, instance, targ, + simple_strtol(opt_arg, NULL, 0)); + opt_arg = tok_end; + break; + } + } + return (opt_arg); +} + /* * Handle Linux boot parameters. This routine allows for assigning a value * to a parameter with a ':' between the parameter and the value. @@ -974,7 +1047,7 @@ aic7xxx_setup(char *s) if (strncmp(p, "global_tag_depth", n) == 0) { ahc_linux_setup_tag_info_global(p + n); } else if (strncmp(p, "tag_info", n) == 0) { - s = aic_parse_brace_option("tag_info", p + n, end, + s = ahc_parse_brace_option("tag_info", p + n, end, 2, ahc_linux_setup_tag_info, 0); } else if (p[n] == ':') { *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0); diff --git a/drivers/scsi/aic7xxx/aic7xxx_proc.c b/drivers/scsi/aic7xxx/aic7xxx_proc.c index 3802c91f0b07..04a3506cf340 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_proc.c +++ b/drivers/scsi/aic7xxx/aic7xxx_proc.c @@ -54,6 +54,49 @@ static void ahc_dump_device_state(struct info_str *info, static int ahc_proc_write_seeprom(struct ahc_softc *ahc, char *buffer, int length); +/* + * Table of syncrates that don't follow the "divisible by 4" + * rule. This table will be expanded in future SCSI specs. + */ +static struct { + u_int period_factor; + u_int period; /* in 100ths of ns */ +} scsi_syncrates[] = { + { 0x08, 625 }, /* FAST-160 */ + { 0x09, 1250 }, /* FAST-80 */ + { 0x0a, 2500 }, /* FAST-40 40MHz */ + { 0x0b, 3030 }, /* FAST-40 33MHz */ + { 0x0c, 5000 } /* FAST-20 */ +}; + +/* + * Return the frequency in kHz corresponding to the given + * sync period factor. + */ +static u_int +ahc_calc_syncsrate(u_int period_factor) +{ + int i; + int num_syncrates; + + num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]); + /* See if the period is in the "exception" table */ + for (i = 0; i < num_syncrates; i++) { + + if (period_factor == scsi_syncrates[i].period_factor) { + /* Period in kHz */ + return (100000000 / scsi_syncrates[i].period); + } + } + + /* + * Wasn't in the table, so use the standard + * 4 times conversion. + */ + return (10000000 / (period_factor * 4 * 10)); +} + + static void copy_mem_info(struct info_str *info, char *data, int len) { @@ -106,7 +149,7 @@ ahc_format_transinfo(struct info_str *info, struct ahc_transinfo *tinfo) speed = 3300; freq = 0; if (tinfo->offset != 0) { - freq = aic_calc_syncsrate(tinfo->period); + freq = ahc_calc_syncsrate(tinfo->period); speed = freq; } speed *= (0x01 << tinfo->width); diff --git a/drivers/scsi/aic7xxx/aiclib.c b/drivers/scsi/aic7xxx/aiclib.c index 4d44a9211185..828ae3d9a510 100644 --- a/drivers/scsi/aic7xxx/aiclib.c +++ b/drivers/scsi/aic7xxx/aiclib.c @@ -32,124 +32,3 @@ #include "aiclib.h" - -/* - * Table of syncrates that don't follow the "divisible by 4" - * rule. This table will be expanded in future SCSI specs. - */ -static struct { - u_int period_factor; - u_int period; /* in 100ths of ns */ -} scsi_syncrates[] = { - { 0x08, 625 }, /* FAST-160 */ - { 0x09, 1250 }, /* FAST-80 */ - { 0x0a, 2500 }, /* FAST-40 40MHz */ - { 0x0b, 3030 }, /* FAST-40 33MHz */ - { 0x0c, 5000 } /* FAST-20 */ -}; - -/* - * Return the frequency in kHz corresponding to the given - * sync period factor. - */ -u_int -aic_calc_syncsrate(u_int period_factor) -{ - int i; - int num_syncrates; - - num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]); - /* See if the period is in the "exception" table */ - for (i = 0; i < num_syncrates; i++) { - - if (period_factor == scsi_syncrates[i].period_factor) { - /* Period in kHz */ - return (100000000 / scsi_syncrates[i].period); - } - } - - /* - * Wasn't in the table, so use the standard - * 4 times conversion. - */ - return (10000000 / (period_factor * 4 * 10)); -} - -char * -aic_parse_brace_option(char *opt_name, char *opt_arg, char *end, int depth, - aic_option_callback_t *callback, u_long callback_arg) -{ - char *tok_end; - char *tok_end2; - int i; - int instance; - int targ; - int done; - char tok_list[] = {'.', ',', '{', '}', '\0'}; - - /* All options use a ':' name/arg separator */ - if (*opt_arg != ':') - return (opt_arg); - opt_arg++; - instance = -1; - targ = -1; - done = FALSE; - /* - * Restore separator that may be in - * the middle of our option argument. - */ - tok_end = strchr(opt_arg, '\0'); - if (tok_end < end) - *tok_end = ','; - while (!done) { - switch (*opt_arg) { - case '{': - if (instance == -1) { - instance = 0; - } else { - if (depth > 1) { - if (targ == -1) - targ = 0; - } else { - printf("Malformed Option %s\n", - opt_name); - done = TRUE; - } - } - opt_arg++; - break; - case '}': - if (targ != -1) - targ = -1; - else if (instance != -1) - instance = -1; - opt_arg++; - break; - case ',': - case '.': - if (instance == -1) - done = TRUE; - else if (targ >= 0) - targ++; - else if (instance >= 0) - instance++; - opt_arg++; - break; - case '\0': - done = TRUE; - break; - default: - tok_end = end; - for (i = 0; tok_list[i]; i++) { - tok_end2 = strchr(opt_arg, tok_list[i]); - if ((tok_end2) && (tok_end2 < tok_end)) - tok_end = tok_end2; - } - callback(callback_arg, instance, targ, - simple_strtol(opt_arg, NULL, 0)); - opt_arg = tok_end; - break; - } - } - return (opt_arg); -} diff --git a/drivers/scsi/aic7xxx/aiclib.h b/drivers/scsi/aic7xxx/aiclib.h index e7d94cbaf2a8..3bfbf0fe1ec2 100644 --- a/drivers/scsi/aic7xxx/aiclib.h +++ b/drivers/scsi/aic7xxx/aiclib.h @@ -141,28 +141,6 @@ aic_sector_div(sector_t capacity, int heads, int sectors) return (int)capacity; } -/**************************** Module Library Hack *****************************/ -/* - * What we'd like to do is have a single "scsi library" module that both the - * aic7xxx and aic79xx drivers could load and depend on. A cursory examination - * of implementing module dependencies in Linux (handling the install and - * initrd cases) does not look promissing. For now, we just duplicate this - * code in both drivers using a simple symbol renaming scheme that hides this - * hack from the drivers. - */ -#define AIC_LIB_ENTRY_CONCAT(x, prefix) prefix ## x -#define AIC_LIB_ENTRY_EXPAND(x, prefix) AIC_LIB_ENTRY_CONCAT(x, prefix) -#define AIC_LIB_ENTRY(x) AIC_LIB_ENTRY_EXPAND(x, AIC_LIB_PREFIX) - -#define aic_calc_syncsrate AIC_LIB_ENTRY(_calc_syncrate) - -u_int aic_calc_syncsrate(u_int /*period_factor*/); - -typedef void aic_option_callback_t(u_long, int, int, int32_t); -char * aic_parse_brace_option(char *opt_name, char *opt_arg, - char *end, int depth, - aic_option_callback_t *, u_long); - static __inline uint32_t scsi_4btoul(uint8_t *bytes) { diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 8640ad1c17e2..85503fad789a 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/blkdev.h> #include <linux/kernel.h> +#include <linux/kthread.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/init.h> @@ -225,15 +226,8 @@ static void scsi_host_dev_release(struct device *dev) struct Scsi_Host *shost = dev_to_shost(dev); struct device *parent = dev->parent; - if (shost->ehandler) { - DECLARE_COMPLETION(sem); - shost->eh_notify = &sem; - shost->eh_kill = 1; - up(shost->eh_wait); - wait_for_completion(&sem); - shost->eh_notify = NULL; - } - + if (shost->ehandler) + kthread_stop(shost->ehandler); if (shost->work_q) destroy_workqueue(shost->work_q); @@ -263,7 +257,6 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) { struct Scsi_Host *shost; int gfp_mask = GFP_KERNEL, rval; - DECLARE_COMPLETION(complete); if (sht->unchecked_isa_dma && privsize) gfp_mask |= __GFP_DMA; @@ -369,12 +362,12 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) snprintf(shost->shost_classdev.class_id, BUS_ID_SIZE, "host%d", shost->host_no); - shost->eh_notify = &complete; - rval = kernel_thread(scsi_error_handler, shost, 0); - if (rval < 0) + shost->ehandler = kthread_run(scsi_error_handler, shost, + "scsi_eh_%d", shost->host_no); + if (IS_ERR(shost->ehandler)) { + rval = PTR_ERR(shost->ehandler); goto fail_destroy_freelist; - wait_for_completion(&complete); - shost->eh_notify = NULL; + } scsi_proc_hostdir_add(shost->hostt); return shost; diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index e3e6752c4104..1b911dadf64b 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -87,7 +87,7 @@ static int max_channel = 3; static int init_timeout = 5; static int max_requests = 50; -#define IBMVSCSI_VERSION "1.5.6" +#define IBMVSCSI_VERSION "1.5.7" MODULE_DESCRIPTION("IBM Virtual SCSI"); MODULE_AUTHOR("Dave Boutcher"); @@ -145,6 +145,8 @@ static int initialize_event_pool(struct event_pool *pool, sizeof(*evt->xfer_iu) * i; evt->xfer_iu = pool->iu_storage + i; evt->hostdata = hostdata; + evt->ext_list = NULL; + evt->ext_list_token = 0; } return 0; @@ -161,9 +163,16 @@ static void release_event_pool(struct event_pool *pool, struct ibmvscsi_host_data *hostdata) { int i, in_use = 0; - for (i = 0; i < pool->size; ++i) + for (i = 0; i < pool->size; ++i) { if (atomic_read(&pool->events[i].free) != 1) ++in_use; + if (pool->events[i].ext_list) { + dma_free_coherent(hostdata->dev, + SG_ALL * sizeof(struct memory_descriptor), + pool->events[i].ext_list, + pool->events[i].ext_list_token); + } + } if (in_use) printk(KERN_WARNING "ibmvscsi: releasing event pool with %d " @@ -286,24 +295,41 @@ static void set_srp_direction(struct scsi_cmnd *cmd, } else { if (cmd->sc_data_direction == DMA_TO_DEVICE) { srp_cmd->data_out_format = SRP_INDIRECT_BUFFER; - srp_cmd->data_out_count = numbuf; + srp_cmd->data_out_count = + numbuf < MAX_INDIRECT_BUFS ? + numbuf: MAX_INDIRECT_BUFS; } else { srp_cmd->data_in_format = SRP_INDIRECT_BUFFER; - srp_cmd->data_in_count = numbuf; + srp_cmd->data_in_count = + numbuf < MAX_INDIRECT_BUFS ? + numbuf: MAX_INDIRECT_BUFS; } } } +static void unmap_sg_list(int num_entries, + struct device *dev, + struct memory_descriptor *md) +{ + int i; + + for (i = 0; i < num_entries; ++i) { + dma_unmap_single(dev, + md[i].virtual_address, + md[i].length, DMA_BIDIRECTIONAL); + } +} + /** * unmap_cmd_data: - Unmap data pointed in srp_cmd based on the format * @cmd: srp_cmd whose additional_data member will be unmapped * @dev: device for which the memory is mapped * */ -static void unmap_cmd_data(struct srp_cmd *cmd, struct device *dev) +static void unmap_cmd_data(struct srp_cmd *cmd, + struct srp_event_struct *evt_struct, + struct device *dev) { - int i; - if ((cmd->data_out_format == SRP_NO_BUFFER) && (cmd->data_in_format == SRP_NO_BUFFER)) return; @@ -318,15 +344,34 @@ static void unmap_cmd_data(struct srp_cmd *cmd, struct device *dev) (struct indirect_descriptor *)cmd->additional_data; int num_mapped = indirect->head.length / sizeof(indirect->list[0]); - for (i = 0; i < num_mapped; ++i) { - struct memory_descriptor *data = &indirect->list[i]; - dma_unmap_single(dev, - data->virtual_address, - data->length, DMA_BIDIRECTIONAL); + + if (num_mapped <= MAX_INDIRECT_BUFS) { + unmap_sg_list(num_mapped, dev, &indirect->list[0]); + return; } + + unmap_sg_list(num_mapped, dev, evt_struct->ext_list); } } +static int map_sg_list(int num_entries, + struct scatterlist *sg, + struct memory_descriptor *md) +{ + int i; + u64 total_length = 0; + + for (i = 0; i < num_entries; ++i) { + struct memory_descriptor *descr = md + i; + struct scatterlist *sg_entry = &sg[i]; + descr->virtual_address = sg_dma_address(sg_entry); + descr->length = sg_dma_len(sg_entry); + descr->memory_handle = 0; + total_length += sg_dma_len(sg_entry); + } + return total_length; +} + /** * map_sg_data: - Maps dma for a scatterlist and initializes decriptor fields * @cmd: Scsi_Cmnd with the scatterlist @@ -337,10 +382,11 @@ static void unmap_cmd_data(struct srp_cmd *cmd, struct device *dev) * Returns 1 on success. */ static int map_sg_data(struct scsi_cmnd *cmd, + struct srp_event_struct *evt_struct, struct srp_cmd *srp_cmd, struct device *dev) { - int i, sg_mapped; + int sg_mapped; u64 total_length = 0; struct scatterlist *sg = cmd->request_buffer; struct memory_descriptor *data = @@ -363,27 +409,46 @@ static int map_sg_data(struct scsi_cmnd *cmd, return 1; } - if (sg_mapped > MAX_INDIRECT_BUFS) { + if (sg_mapped > SG_ALL) { printk(KERN_ERR "ibmvscsi: More than %d mapped sg entries, got %d\n", - MAX_INDIRECT_BUFS, sg_mapped); + SG_ALL, sg_mapped); return 0; } indirect->head.virtual_address = 0; indirect->head.length = sg_mapped * sizeof(indirect->list[0]); indirect->head.memory_handle = 0; - for (i = 0; i < sg_mapped; ++i) { - struct memory_descriptor *descr = &indirect->list[i]; - struct scatterlist *sg_entry = &sg[i]; - descr->virtual_address = sg_dma_address(sg_entry); - descr->length = sg_dma_len(sg_entry); - descr->memory_handle = 0; - total_length += sg_dma_len(sg_entry); + + if (sg_mapped <= MAX_INDIRECT_BUFS) { + total_length = map_sg_list(sg_mapped, sg, &indirect->list[0]); + indirect->total_length = total_length; + return 1; } - indirect->total_length = total_length; - return 1; + /* get indirect table */ + if (!evt_struct->ext_list) { + evt_struct->ext_list =(struct memory_descriptor*) + dma_alloc_coherent(dev, + SG_ALL * sizeof(struct memory_descriptor), + &evt_struct->ext_list_token, 0); + if (!evt_struct->ext_list) { + printk(KERN_ERR + "ibmvscsi: Can't allocate memory for indirect table\n"); + return 0; + + } + } + + total_length = map_sg_list(sg_mapped, sg, evt_struct->ext_list); + + indirect->total_length = total_length; + indirect->head.virtual_address = evt_struct->ext_list_token; + indirect->head.length = sg_mapped * sizeof(indirect->list[0]); + memcpy(indirect->list, evt_struct->ext_list, + MAX_INDIRECT_BUFS * sizeof(struct memory_descriptor)); + + return 1; } /** @@ -428,6 +493,7 @@ static int map_single_data(struct scsi_cmnd *cmd, * Returns 1 on success. */ static int map_data_for_srp_cmd(struct scsi_cmnd *cmd, + struct srp_event_struct *evt_struct, struct srp_cmd *srp_cmd, struct device *dev) { switch (cmd->sc_data_direction) { @@ -450,7 +516,7 @@ static int map_data_for_srp_cmd(struct scsi_cmnd *cmd, if (!cmd->request_buffer) return 1; if (cmd->use_sg) - return map_sg_data(cmd, srp_cmd, dev); + return map_sg_data(cmd, evt_struct, srp_cmd, dev); return map_single_data(cmd, srp_cmd, dev); } @@ -486,6 +552,7 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, printk(KERN_WARNING "ibmvscsi: Warning, request_limit exceeded\n"); unmap_cmd_data(&evt_struct->iu.srp.cmd, + evt_struct, hostdata->dev); free_event_struct(&hostdata->pool, evt_struct); return SCSI_MLQUEUE_HOST_BUSY; @@ -513,7 +580,7 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, return 0; send_error: - unmap_cmd_data(&evt_struct->iu.srp.cmd, hostdata->dev); + unmap_cmd_data(&evt_struct->iu.srp.cmd, evt_struct, hostdata->dev); if ((cmnd = evt_struct->cmnd) != NULL) { cmnd->result = DID_ERROR << 16; @@ -551,6 +618,7 @@ static void handle_cmd_rsp(struct srp_event_struct *evt_struct) rsp->sense_and_response_data, rsp->sense_data_list_length); unmap_cmd_data(&evt_struct->iu.srp.cmd, + evt_struct, evt_struct->hostdata->dev); if (rsp->doover) @@ -583,6 +651,7 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd, { struct srp_cmd *srp_cmd; struct srp_event_struct *evt_struct; + struct indirect_descriptor *indirect; struct ibmvscsi_host_data *hostdata = (struct ibmvscsi_host_data *)&cmnd->device->host->hostdata; u16 lun = lun_from_dev(cmnd->device); @@ -591,14 +660 |