X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fscsi%2Faic7xxx%2Faic79xx_osm.c;h=66e4a47bb9ee3fe85d72f6c2e0679d759dbd1fd6;hb=9464c7cf61b9433057924c36e6e02f303a00e768;hp=07d51b870b3cfecf0477e92727dfb778e0c75989;hpb=41689045f6a3cbe0550e1d34e9cc20d2e8c432ba;p=linux-2.6.git diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c index 07d51b870..66e4a47bb 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm.c @@ -242,6 +242,25 @@ ahd_print_path(struct ahd_softc *ahd, struct scb *scb) */ static uint32_t aic79xx_no_reset; +/* + * Certain PCI motherboards will scan PCI devices from highest to lowest, + * others scan from lowest to highest, and they tend to do all kinds of + * strange things when they come into contact with PCI bridge chips. The + * net result of all this is that the PCI card that is actually used to boot + * the machine is very hard to detect. Most motherboards go from lowest + * PCI slot number to highest, and the first SCSI controller found is the + * one you boot from. The only exceptions to this are when a controller + * has its BIOS disabled. So, we by default sort all of our SCSI controllers + * from lowest PCI slot number to highest PCI slot number. We also force + * all controllers with their BIOS disabled to the end of the list. This + * works on *almost* all computers. Where it doesn't work, we have this + * option. Setting this option to non-0 will reverse the order of the sort + * to highest first, then lowest, but will still leave cards with their BIOS + * disabled at the very end. That should fix everyone up unless there are + * really strange cirumstances. + */ +static uint32_t aic79xx_reverse_scan; + /* * Should we force EXTENDED translation on a controller. * 0 == Use whatever is in the SEEPROM or default to off @@ -331,6 +350,7 @@ MODULE_PARM_DESC(aic79xx, " periodically to prevent tag starvation.\n" " This may be required by some older disk\n" " or drives/RAID arrays.\n" +" reverse_scan Sort PCI devices highest Bus/Slot to lowest\n" " tag_info: Set per-target tag depth\n" " global_tag_depth: Global tag depth for all targets on all buses\n" " slewrate:Set the signal slew rate (0-15).\n" @@ -464,6 +484,7 @@ ahd_linux_target_alloc(struct scsi_target *starget) struct seeprom_config *sc = ahd->seep_config; unsigned long flags; struct scsi_target **ahd_targp = ahd_linux_target_in_softc(starget); + struct ahd_linux_target *targ = scsi_transport_target_data(starget); struct ahd_devinfo devinfo; struct ahd_initiator_tinfo *tinfo; struct ahd_tmode_tstate *tstate; @@ -474,6 +495,7 @@ ahd_linux_target_alloc(struct scsi_target *starget) BUG_ON(*ahd_targp != NULL); *ahd_targp = starget; + memset(targ, 0, sizeof(*targ)); if (sc) { int flags = sc->device_flags[starget->id]; @@ -529,11 +551,15 @@ ahd_linux_slave_alloc(struct scsi_device *sdev) { struct ahd_softc *ahd = *((struct ahd_softc **)sdev->host->hostdata); + struct scsi_target *starget = sdev->sdev_target; + struct ahd_linux_target *targ = scsi_transport_target_data(starget); struct ahd_linux_device *dev; if (bootverbose) printf("%s: Slave Alloc %d\n", ahd_name(ahd), sdev->id); + BUG_ON(targ->sdev[sdev->lun] != NULL); + dev = scsi_transport_device_data(sdev); memset(dev, 0, sizeof(*dev)); @@ -550,6 +576,8 @@ ahd_linux_slave_alloc(struct scsi_device *sdev) */ dev->maxtags = 0; + targ->sdev[sdev->lun] = sdev; + return (0); } @@ -571,6 +599,23 @@ ahd_linux_slave_configure(struct scsi_device *sdev) return 0; } +static void +ahd_linux_slave_destroy(struct scsi_device *sdev) +{ + struct ahd_softc *ahd; + struct ahd_linux_device *dev = scsi_transport_device_data(sdev); + struct ahd_linux_target *targ = scsi_transport_target_data(sdev->sdev_target); + + ahd = *((struct ahd_softc **)sdev->host->hostdata); + if (bootverbose) + printf("%s: Slave Destroy %d\n", ahd_name(ahd), sdev->id); + + BUG_ON(dev->active); + + targ->sdev[sdev->lun] = NULL; + +} + #if defined(__i386__) /* * Return the disk geometry for the given SCSI device. @@ -646,7 +691,7 @@ ahd_linux_dev_reset(struct scsi_cmnd *cmd) struct ahd_initiator_tinfo *tinfo; struct ahd_tmode_tstate *tstate; unsigned long flags; - DECLARE_COMPLETION_ONSTACK(done); + DECLARE_COMPLETION(done); reset_scb = NULL; paused = FALSE; @@ -777,6 +822,7 @@ struct scsi_host_template aic79xx_driver_template = { .use_clustering = ENABLE_CLUSTERING, .slave_alloc = ahd_linux_slave_alloc, .slave_configure = ahd_linux_slave_configure, + .slave_destroy = ahd_linux_slave_destroy, .target_alloc = ahd_linux_target_alloc, .target_destroy = ahd_linux_target_destroy, }; @@ -870,7 +916,7 @@ ahd_linux_setup_iocell_info(u_long index, int instance, int targ, int32_t value) { if ((instance >= 0) - && (instance < ARRAY_SIZE(aic79xx_iocell_info))) { + && (instance < NUM_ELEMENTS(aic79xx_iocell_info))) { uint8_t *iocell_info; iocell_info = (uint8_t*)&aic79xx_iocell_info[instance]; @@ -888,7 +934,7 @@ ahd_linux_setup_tag_info_global(char *p) tags = simple_strtoul(p + 1, NULL, 0) & 0xff; printf("Setting Global Tags= %d\n", tags); - for (i = 0; i < ARRAY_SIZE(aic79xx_tag_info); i++) { + for (i = 0; i < NUM_ELEMENTS(aic79xx_tag_info); i++) { for (j = 0; j < AHD_NUM_TARGETS; j++) { aic79xx_tag_info[i].tag_commands[j] = tags; } @@ -900,7 +946,7 @@ ahd_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value) { if ((instance >= 0) && (targ >= 0) - && (instance < ARRAY_SIZE(aic79xx_tag_info)) + && (instance < NUM_ELEMENTS(aic79xx_tag_info)) && (targ < AHD_NUM_TARGETS)) { aic79xx_tag_info[instance].tag_commands[targ] = value & 0x1FF; if (bootverbose) @@ -1011,6 +1057,7 @@ aic79xx_setup(char *s) #ifdef AHD_DEBUG { "debug", &ahd_debug }, #endif + { "reverse_scan", &aic79xx_reverse_scan }, { "periodic_otag", &aic79xx_periodic_otag }, { "pci_parity", &aic79xx_pci_parity }, { "seltime", &aic79xx_seltime }, @@ -1025,21 +1072,21 @@ aic79xx_setup(char *s) end = strchr(s, '\0'); /* - * XXX ia64 gcc isn't smart enough to know that ARRAY_SIZE + * XXX ia64 gcc isn't smart enough to know that NUM_ELEMENTS * will never be 0 in this case. - */ - n = 0; + */ + n = 0; while ((p = strsep(&s, ",.")) != NULL) { if (*p == '\0') continue; - for (i = 0; i < ARRAY_SIZE(options); i++) { + for (i = 0; i < NUM_ELEMENTS(options); i++) { n = strlen(options[i].name); if (strncmp(options[i].name, p, n) == 0) break; } - if (i == ARRAY_SIZE(options)) + if (i == NUM_ELEMENTS(options)) continue; if (strncmp(p, "global_tag_depth", n) == 0) { @@ -1202,13 +1249,20 @@ void ahd_platform_free(struct ahd_softc *ahd) { struct scsi_target *starget; - int i; + int i, j; if (ahd->platform_data != NULL) { /* destroy all of the device and target objects */ for (i = 0; i < AHD_NUM_TARGETS; i++) { starget = ahd->platform_data->starget[i]; if (starget != NULL) { + for (j = 0; j < AHD_NUM_LUNS; j++) { + struct ahd_linux_target *targ = + scsi_transport_target_data(starget); + if (targ->sdev[j] == NULL) + continue; + targ->sdev[j] = NULL; + } ahd->platform_data->starget[i] = NULL; } } @@ -1240,7 +1294,7 @@ ahd_platform_init(struct ahd_softc *ahd) /* * Lookup and commit any modified IO Cell options. */ - if (ahd->unit < ARRAY_SIZE(aic79xx_iocell_info)) { + if (ahd->unit < NUM_ELEMENTS(aic79xx_iocell_info)) { struct ahd_linux_iocell_opts *iocell_opts; iocell_opts = &aic79xx_iocell_info[ahd->unit]; @@ -1264,13 +1318,20 @@ ahd_platform_freeze_devq(struct ahd_softc *ahd, struct scb *scb) } void -ahd_platform_set_tags(struct ahd_softc *ahd, struct scsi_device *sdev, - struct ahd_devinfo *devinfo, ahd_queue_alg alg) +ahd_platform_set_tags(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, + ahd_queue_alg alg) { + struct scsi_target *starget; + struct ahd_linux_target *targ; struct ahd_linux_device *dev; + struct scsi_device *sdev; int was_queuing; int now_queuing; + starget = ahd->platform_data->starget[devinfo->target]; + targ = scsi_transport_target_data(starget); + BUG_ON(targ == NULL); + sdev = targ->sdev[devinfo->lun]; if (sdev == NULL) return; @@ -1365,7 +1426,7 @@ ahd_linux_user_tagdepth(struct ahd_softc *ahd, struct ahd_devinfo *devinfo) tags = 0; if ((ahd->user_discenable & devinfo->target_mask) != 0) { - if (ahd->unit >= ARRAY_SIZE(aic79xx_tag_info)) { + if (ahd->unit >= NUM_ELEMENTS(aic79xx_tag_info)) { if (warned_user == 0) { printf(KERN_WARNING @@ -1406,15 +1467,11 @@ ahd_linux_device_queue_depth(struct scsi_device *sdev) tags = ahd_linux_user_tagdepth(ahd, &devinfo); if (tags != 0 && sdev->tagged_supported != 0) { - ahd_platform_set_tags(ahd, sdev, &devinfo, AHD_QUEUE_TAGGED); - ahd_send_async(ahd, devinfo.channel, devinfo.target, - devinfo.lun, AC_TRANSFER_NEG); + ahd_set_tags(ahd, &devinfo, AHD_QUEUE_TAGGED); ahd_print_devinfo(ahd, &devinfo); printf("Tagged Queuing enabled. Depth %d\n", tags); } else { - ahd_platform_set_tags(ahd, sdev, &devinfo, AHD_QUEUE_NONE); - ahd_send_async(ahd, devinfo.channel, devinfo.target, - devinfo.lun, AC_TRANSFER_NEG); + ahd_set_tags(ahd, &devinfo, AHD_QUEUE_NONE); } } @@ -1572,7 +1629,7 @@ ahd_linux_isr(int irq, void *dev_id, struct pt_regs * regs) void ahd_send_async(struct ahd_softc *ahd, char channel, - u_int target, u_int lun, ac_code code) + u_int target, u_int lun, ac_code code, void *arg) { switch (code) { case AC_TRANSFER_NEG: @@ -1899,7 +1956,7 @@ ahd_linux_handle_scsi_status(struct ahd_softc *ahd, } ahd_set_transaction_status(scb, CAM_REQUEUE_REQ); ahd_set_scsi_status(scb, SCSI_STATUS_OK); - ahd_platform_set_tags(ahd, sdev, &devinfo, + ahd_platform_set_tags(ahd, &devinfo, (dev->flags & AHD_DEV_Q_BASIC) ? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED); break; @@ -1909,7 +1966,7 @@ ahd_linux_handle_scsi_status(struct ahd_softc *ahd, * as if the target returned BUSY SCSI status. */ dev->openings = 1; - ahd_platform_set_tags(ahd, sdev, &devinfo, + ahd_platform_set_tags(ahd, &devinfo, (dev->flags & AHD_DEV_Q_BASIC) ? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED); ahd_set_scsi_status(scb, SCSI_STATUS_BUSY); @@ -2251,7 +2308,7 @@ done: if (paused) ahd_unpause(ahd); if (wait) { - DECLARE_COMPLETION_ONSTACK(done); + DECLARE_COMPLETION(done); ahd->platform_data->eh_done = &done; ahd_unlock(ahd, &flags); @@ -2721,6 +2778,8 @@ ahd_linux_init(void) if (!ahd_linux_transport_template) return -ENODEV; + scsi_transport_reserve_target(ahd_linux_transport_template, + sizeof(struct ahd_linux_target)); scsi_transport_reserve_device(ahd_linux_transport_template, sizeof(struct ahd_linux_device));