diff -u b/drivers/scsi/Kconfig b/drivers/scsi/Kconfig --- b/drivers/scsi/Kconfig 2004-10-06 22:21:10 -07:00 +++ b/drivers/scsi/Kconfig 2004-10-06 22:21:18 -07:00 @@ -1028,6 +1028,7 @@ config SCSI_ZALON tristate "Zalon SCSI support" depends on GSC && SCSI + select SCSI_SPI_ATTRS help The Zalon is a GSC/HSC bus interface chip that sits between the PA-RISC processor and the NCR 53c720 SCSI controller on C100, @@ -1038,6 +1039,7 @@ config SCSI_NCR_Q720 tristate "NCR Quad 720 MCA SCSI support" depends on MCA && SCSI + select SCSI_SPI_ATTRS help This is a driver for the MicroChannel Quad 720 card produced by NCR and commonly used in 345x/35xx/4100 class machines. It always diff -u b/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c --- b/drivers/scsi/ncr53c8xx.c 2004-10-06 22:21:10 -07:00 +++ b/drivers/scsi/ncr53c8xx.c 2004-10-06 22:21:18 -07:00 @@ -123,6 +123,8 @@ #include #include #include +#include +#include #include "ncr53c8xx.h" @@ -412,6 +414,8 @@ **========================================================== */ +static struct scsi_transport_template *ncr53c8xx_transport_template = NULL; + struct tcb; struct lcb; struct ccb; @@ -532,7 +536,7 @@ u_char usrwide; u_char usrtags; u_char usrflag; - struct scsi_device *sdev; + struct scsi_target *starget; }; /*======================================================================== @@ -1212,7 +1216,7 @@ static void ncr_init_ccb (struct ncb *np, struct ccb *cp); static void ncr_init_tcb (struct ncb *np, u_char tn); static struct lcb * ncr_alloc_lcb (struct ncb *np, u_char tn, u_char ln); -static struct lcb * ncr_setup_lcb (struct ncb *np, u_char tn, u_char ln); +static struct lcb * ncr_setup_lcb (struct ncb *np, struct scsi_device *sdev); static void ncr_getclock (struct ncb *np, int mult); static void ncr_selectclock (struct ncb *np, u_char scntl3); static struct ccb *ncr_get_ccb (struct ncb *np, u_char tn, u_char ln); @@ -1232,7 +1236,7 @@ static int ncr_scatter (struct ncb *np, struct ccb *cp, struct scsi_cmnd *cmd); static void ncr_getsync (struct ncb *np, u_char sfac, u_char *fakp, u_char *scntl3p); static void ncr_setsync (struct ncb *np, struct ccb *cp, u_char scntl3, u_char sxfer); -static void ncr_setup_tags (struct ncb *np, u_char tn, u_char ln); +static void ncr_setup_tags (struct ncb *np, struct scsi_device *sdev); static void ncr_setwide (struct ncb *np, struct ccb *cp, u_char wide, u_char ack); static int ncr_show_msg (u_char * msg); static void ncr_print_msg (struct ccb *cp, char *label, u_char *msg); @@ -3363,16 +3367,16 @@ struct tcb *tp = &np->target[cp->target]; int msglen = 0; int nego = 0; - struct scsi_device *sdev = tp->sdev; + struct scsi_target *starget = tp->starget; - if (likely(sdev)) { + if (likely(starget)) { /* ** negotiate wide transfers ? */ if (!tp->widedone) { - if (sdev->wdtr) { + if (spi_support_wide(starget)) { nego = NS_WIDE; } else tp->widedone=1; @@ -3384,7 +3388,7 @@ */ if (!nego && !tp->period) { - if (sdev->sdtr) { + if (spi_support_sync(starget)) { nego = NS_SYNC; } else { tp->period =0xffff; @@ -4273,7 +4277,7 @@ if (lp->num_good >= 1000) { lp->num_good = 0; ++lp->numtags; - ncr_setup_tags (np, cmd->device->id, cmd->device->lun); + ncr_setup_tags (np, cmd->device); } } } else if ((cp->host_status == HS_COMPLETE) @@ -4723,8 +4727,11 @@ if (minsync > np->maxsync) minsync = 255; + if (tp->maxoffs > np->maxoffs) + tp->maxoffs = np->maxoffs; + tp->minsync = minsync; - tp->maxoffs = (minsync<255 ? np->maxoffs : 0); + tp->maxoffs = (minsync<255 ? tp->maxoffs : 0); /* ** period=0: has to negotiate sync transfer @@ -4978,12 +4985,12 @@ **========================================================== */ -static void ncr_setup_tags (struct ncb *np, u_char tn, u_char ln) +static void ncr_setup_tags (struct ncb *np, struct scsi_device *sdev) { + unsigned char tn = sdev->id, ln = sdev->lun; struct tcb *tp = &np->target[tn]; struct lcb *lp = tp->lp[ln]; u_char reqtags, maxdepth; - struct scsi_device *sdev = tp->sdev; /* ** Just in case ... @@ -5939,7 +5946,7 @@ if (disc_cnt < lp->numtags) { lp->numtags = disc_cnt > 2 ? disc_cnt : 2; lp->num_good = 0; - ncr_setup_tags (np, cmd->device->id, cmd->device->lun); + ncr_setup_tags (np, cmd->device); } /* ** Requeue the command to the start queue. @@ -6078,6 +6085,7 @@ u_long dsa = INL (nc_dsa); u_char target = INB (nc_sdid) & 0x0f; struct tcb *tp = &np->target[target]; + struct scsi_target *starget = tp->starget; if (DEBUG_FLAGS & DEBUG_TINY) printk ("I#%d", num); @@ -6235,10 +6243,13 @@ case NS_SYNC: ncr_setsync (np, cp, 0, 0xe0); + spi_period(starget) = 0; + spi_offset(starget) = 0; break; case NS_WIDE: ncr_setwide (np, cp, 0, 0); + spi_width(starget) = 0; break; }; @@ -6273,8 +6284,8 @@ ** it CAN transfer synch. */ - if (ofs && tp->sdev) - tp->sdev->sdtr = 1; + if (ofs && tp->starget) + spi_support_sync(tp->starget) = 1; /* ** check values against driver limits. @@ -6325,18 +6336,23 @@ ** Answer wasn't acceptable. */ ncr_setsync (np, cp, 0, 0xe0); + spi_period(starget) = 0; + spi_offset(starget) = 0; OUTL_DSP (NCB_SCRIPT_PHYS (np, msg_bad)); } else { /* ** Answer is ok. */ ncr_setsync (np, cp, scntl3, (fak<<5)|ofs); + spi_period(starget) = per; + spi_offset(starget) = ofs; OUTL_DSP (NCB_SCRIPT_PHYS (np, clrack)); }; return; case NS_WIDE: ncr_setwide (np, cp, 0, 0); + spi_width(starget) = 0; break; }; }; @@ -6347,6 +6363,8 @@ */ ncr_setsync (np, cp, scntl3, (fak<<5)|ofs); + spi_period(starget) = per; + spi_offset(starget) = ofs; np->msgout[0] = M_EXTENDED; np->msgout[1] = 3; @@ -6394,8 +6412,8 @@ ** it CAN transfer wide. */ - if (wide && tp->sdev) - tp->sdev->wdtr = 1; + if (wide && tp->starget) + spi_support_wide(tp->starget) = 1; /* ** check values against driver limits. @@ -6422,18 +6440,22 @@ ** Answer wasn't acceptable. */ ncr_setwide (np, cp, 0, 1); + spi_width(starget) = 0; OUTL_DSP (NCB_SCRIPT_PHYS (np, msg_bad)); } else { /* ** Answer is ok. */ ncr_setwide (np, cp, wide, 1); + spi_width(starget) = wide; OUTL_DSP (NCB_SCRIPT_PHYS (np, clrack)); }; return; case NS_SYNC: ncr_setsync (np, cp, 0, 0xe0); + spi_period(starget) = 0; + spi_offset(starget) = 0; break; }; }; @@ -6444,6 +6466,7 @@ */ ncr_setwide (np, cp, wide, 1); + spi_width(starget) = wide; np->msgout[0] = M_EXTENDED; np->msgout[1] = 2; @@ -6573,13 +6596,6 @@ ncr_alloc_ccb(np, tn, ln); /* - ** Tune tag mode if asked by user. - */ - if (lp->queuedepth != lp->numtags) { - ncr_setup_tags(np, tn, ln); - } - - /* ** Look for free CCB */ qp = ncr_list_pop(&lp->free_ccbq); @@ -6796,7 +6812,6 @@ np->ccb->link_ccb = cp; list_add(&cp->link_ccbq, &lp->free_ccbq); - ncr_setup_tags (np, tn, ln); } /*========================================================== @@ -6994,11 +7009,12 @@ ** will play with CHANGE DEFINITION commands. :-) **------------------------------------------------------------------------ */ -static struct lcb *ncr_setup_lcb (struct ncb *np, u_char tn, u_char ln) +static struct lcb *ncr_setup_lcb (struct ncb *np, struct scsi_device *sdev) { + unsigned char tn = sdev->id, ln = sdev->lun; struct tcb *tp = &np->target[tn]; struct lcb *lp = tp->lp[ln]; - struct scsi_device *sdev = tp->sdev; + struct scsi_target *starget = tp->starget; /* ** If no lcb, try to allocate it. @@ -7009,7 +7025,7 @@ /* ** Prepare negotiation */ - if (sdev->wdtr || sdev->sdtr) + if (spi_support_wide(starget) || spi_support_sync(starget)) ncr_negotiate(np, tp); /* @@ -7031,7 +7047,7 @@ lp->cb_tags[i] = i; lp->maxnxs = MAX_TAGS; lp->tags_stime = ktime_get(3*HZ); - ncr_setup_tags (np, tn, ln); + ncr_setup_tags (np, sdev); } @@ -7416,9 +7432,9 @@ struct lcb *lp = tp->lp[device->lun]; int numtags, depth_to_use; - tp->sdev = device; + tp->starget = device->sdev_target; - ncr_setup_lcb(np, device->id, device->lun); + ncr_setup_lcb(np, device); /* ** Select queue depth from driver setup. @@ -7455,13 +7471,16 @@ lp->numtags = lp->maxtags = numtags; lp->scdev_depth = depth_to_use; } - ncr_setup_tags (np, device->id, device->lun); + ncr_setup_tags (np, device); #ifdef DEBUG_NCR53C8XX printk("ncr53c8xx_select_queue_depth: host=%d, id=%d, lun=%d, depth=%d\n", np->unit, device->id, device->lun, depth_to_use); #endif + if (spi_support_sync(device->sdev_target) && + !spi_initial_dv(device->sdev_target)) + spi_dv_device(device); return 0; } @@ -7856,6 +7875,10 @@ instance->dma_channel = 0; instance->cmd_per_lun = MAX_TAGS; instance->can_queue = (MAX_START-4); + /* This can happen if you forget to call ncr53c8xx_init from + * your module_init */ + BUG_ON(!ncr53c8xx_transport_template); + instance->transportt = ncr53c8xx_transport_template; scsi_set_device(instance, device->dev); /* Patch script to physical addresses */ @@ -7986,0 +8010,70 @@ + +static void ncr53c8xx_set_period(struct scsi_target *starget, int period) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ncb *np = ((struct host_data *)shost->hostdata)->ncb; + struct tcb *tp = &np->target[starget->id]; + + if (period > np->maxsync) + period = np->maxsync; + else if (period < np->minsync) + period = np->minsync; + + tp->usrsync = period; + + ncr_negotiate(np, tp); +} + +static void ncr53c8xx_set_offset(struct scsi_target *starget, int offset) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ncb *np = ((struct host_data *)shost->hostdata)->ncb; + struct tcb *tp = &np->target[starget->id]; + + if (offset > np->maxoffs) + offset = np->maxoffs; + else if (offset < 0) + offset = 0; + + tp->maxoffs = offset; + + ncr_negotiate(np, tp); +} + +static void ncr53c8xx_set_width(struct scsi_target *starget, int width) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ncb *np = ((struct host_data *)shost->hostdata)->ncb; + struct tcb *tp = &np->target[starget->id]; + + if (width > np->maxwide) + width = np->maxwide; + else if (width < 0) + width = 0; + + tp->usrwide = width; + + ncr_negotiate(np, tp); +} + +static struct spi_function_template ncr53c8xx_transport_functions = { + .set_period = ncr53c8xx_set_period, + .show_period = 1, + .set_offset = ncr53c8xx_set_offset, + .show_offset = 1, + .set_width = ncr53c8xx_set_width, + .show_width = 1, +}; + +int __init ncr53c8xx_init(void) +{ + ncr53c8xx_transport_template = spi_attach_transport(&ncr53c8xx_transport_functions); + if (!ncr53c8xx_transport_template) + return -ENODEV; + return 0; +} + +void ncr53c8xx_exit(void) +{ + spi_release_transport(ncr53c8xx_transport_template); +} diff -u b/drivers/scsi/ncr53c8xx.h b/drivers/scsi/ncr53c8xx.h --- b/drivers/scsi/ncr53c8xx.h 2004-10-06 22:21:10 -07:00 +++ b/drivers/scsi/ncr53c8xx.h 2004-10-06 22:21:18 -07:00 @@ -96,4 +96,6 @@ extern int ncr53c8xx_release(struct Scsi_Host *host); irqreturn_t ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs); +extern int ncr53c8xx_init(void); +extern void ncr53c8xx_exit(void); #endif /* NCR53C8XX_H */ diff -u b/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c --- b/drivers/scsi/qla2xxx/qla_os.c 2004-10-06 22:21:10 -07:00 +++ b/drivers/scsi/qla2xxx/qla_os.c 2004-10-06 22:21:19 -07:00 @@ -4451,61 +4451,64 @@ } static void -qla2xxx_get_port_id(struct scsi_device *sdev) +qla2xxx_get_port_id(struct scsi_target *starget) { - scsi_qla_host_t *ha = to_qla_host(sdev->host); + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + scsi_qla_host_t *ha = to_qla_host(shost); struct fc_port *fc; list_for_each_entry(fc, &ha->fcports, list) { - if (fc->os_target_id == sdev->id) { - fc_port_id(sdev) = fc->d_id.b.domain << 16 | + if (fc->os_target_id == starget->id) { + fc_starget_port_id(starget) = fc->d_id.b.domain << 16 | fc->d_id.b.area << 8 | fc->d_id.b.al_pa; return; } } - fc_port_id(sdev) = -1; + fc_starget_port_id(starget) = -1; } static void -qla2xxx_get_port_name(struct scsi_device *sdev) +qla2xxx_get_port_name(struct scsi_target *starget) { - scsi_qla_host_t *ha = to_qla_host(sdev->host); + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + scsi_qla_host_t *ha = to_qla_host(shost); struct fc_port *fc; list_for_each_entry(fc, &ha->fcports, list) { - if (fc->os_target_id == sdev->id) { - fc_port_name(sdev) = + if (fc->os_target_id == starget->id) { + fc_starget_port_name(starget) = __be64_to_cpu(*(uint64_t *)fc->port_name); return; } } - fc_port_name(sdev) = -1; + fc_starget_port_name(starget) = -1; } static void -qla2xxx_get_node_name(struct scsi_device *sdev) +qla2xxx_get_node_name(struct scsi_target *starget) { - scsi_qla_host_t *ha = to_qla_host(sdev->host); + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + scsi_qla_host_t *ha = to_qla_host(shost); struct fc_port *fc; list_for_each_entry(fc, &ha->fcports, list) { - if (fc->os_target_id == sdev->id) { - fc_node_name(sdev) = + if (fc->os_target_id == starget->id) { + fc_starget_node_name(starget) = __be64_to_cpu(*(uint64_t *)fc->node_name); return; } } - fc_node_name(sdev) = -1; + fc_starget_node_name(starget) = -1; } static struct fc_function_template qla2xxx_transport_functions = { - .get_port_id = qla2xxx_get_port_id, - .show_port_id = 1, - .get_port_name = qla2xxx_get_port_name, - .show_port_name = 1, - .get_node_name = qla2xxx_get_node_name, - .show_node_name = 1, + .get_starget_port_id = qla2xxx_get_port_id, + .show_starget_port_id = 1, + .get_starget_port_name = qla2xxx_get_port_name, + .show_starget_port_name = 1, + .get_starget_node_name = qla2xxx_get_node_name, + .show_starget_node_name = 1, }; /** diff -u b/drivers/scsi/scsi.c b/drivers/scsi/scsi.c --- b/drivers/scsi/scsi.c 2004-10-06 22:21:10 -07:00 +++ b/drivers/scsi/scsi.c 2004-10-06 22:21:18 -07:00 @@ -528,6 +528,26 @@ /* return 0 (because the command has been processed) */ goto out; } + + /* Check to see if the scsi lld put this device into state SDEV_BLOCK. */ + if (unlikely(cmd->device->sdev_state == SDEV_BLOCK)) { + /* + * in SDEV_BLOCK, the command is just put back on the device + * queue. The suspend state has already blocked the queue so + * future requests should not occur until the device + * transitions out of the suspend state. + */ + scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY); + + SCSI_LOG_MLQUEUE(3, printk("queuecommand : device blocked \n")); + + /* + * NOTE: rtn is still zero here because we don't need the + * queue to be plugged on return (it's already stopped) + */ + goto out; + } + /* Assign a unique nonzero serial_number. */ /* XXX(hch): this is racy */ if (++serial_number == 0) @@ -1113,8 +1133,8 @@ /** * scsi_device_cancel - cancel outstanding IO to this device - * @sdev: pointer to struct scsi_device - * @data: pointer to cancel value. + * @sdev: Pointer to struct scsi_device + * @recovery: Boolean instructing function to recover device or not. * **/ int scsi_device_cancel(struct scsi_device *sdev, int recovery) diff -u b/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c --- b/drivers/scsi/scsi_lib.c 2004-10-06 22:21:10 -07:00 +++ b/drivers/scsi/scsi_lib.c 2004-10-06 22:21:18 -07:00 @@ -365,7 +365,7 @@ unsigned long flags; spin_lock_irqsave(shost->host_lock, flags); - current_sdev->sdev_target->starget_sdev_user = NULL; + scsi_target(current_sdev)->starget_sdev_user = NULL; spin_unlock_irqrestore(shost->host_lock, flags); /* @@ -377,7 +377,7 @@ blk_run_queue(current_sdev->request_queue); spin_lock_irqsave(shost->host_lock, flags); - if (current_sdev->sdev_target->starget_sdev_user) + if (scsi_target(current_sdev)->starget_sdev_user) goto out; list_for_each_entry_safe(sdev, tmp, ¤t_sdev->same_target_siblings, same_target_siblings) { @@ -1253,10 +1253,10 @@ if (!scsi_host_queue_ready(q, shost, sdev)) goto not_ready; if (sdev->single_lun) { - if (sdev->sdev_target->starget_sdev_user && - sdev->sdev_target->starget_sdev_user != sdev) + if (scsi_target(sdev)->starget_sdev_user && + scsi_target(sdev)->starget_sdev_user != sdev) goto not_ready; - sdev->sdev_target->starget_sdev_user = sdev; + scsi_target(sdev)->starget_sdev_user = sdev; } shost->host_busy++; @@ -1642,6 +1642,7 @@ case SDEV_CREATED: case SDEV_OFFLINE: case SDEV_QUIESCE: + case SDEV_BLOCK: break; default: goto illegal; @@ -1669,11 +1670,22 @@ } break; + case SDEV_BLOCK: + switch (oldstate) { + case SDEV_CREATED: + case SDEV_RUNNING: + break; + default: + goto illegal; + } + break; + case SDEV_CANCEL: switch (oldstate) { case SDEV_CREATED: case SDEV_RUNNING: case SDEV_OFFLINE: + case SDEV_BLOCK: break; default: goto illegal; @@ -1753,0 +1766,109 @@ + +static int +device_quiesce_fn(struct device *dev, void *data) +{ + scsi_device_quiesce(to_scsi_device(dev)); + return 0; +} + +void +scsi_target_quiesce(struct scsi_target *starget) +{ + device_for_each_child(&starget->dev, NULL, device_quiesce_fn); +} +EXPORT_SYMBOL(scsi_target_quiesce); + +static int +device_resume_fn(struct device *dev, void *data) +{ + scsi_device_resume(to_scsi_device(dev)); + return 0; +} + +void +scsi_target_resume(struct scsi_target *starget) +{ + device_for_each_child(&starget->dev, NULL, device_resume_fn); +} +EXPORT_SYMBOL(scsi_target_resume); + +/** + * scsi_internal_device_block - internal function to put a device + * temporarily into the SDEV_BLOCK state + * @sdev: device to block + * + * Block request made by scsi lld's to temporarily stop all + * scsi commands on the specified device. Called from interrupt + * or normal process context. + * + * Returns zero if successful or error if not + * + * Notes: + * This routine transitions the device to the SDEV_BLOCK state + * (which must be a legal transition). When the device is in this + * state, all commands are deferred until the scsi lld reenables + * the device with scsi_device_unblock or device_block_tmo fires. + * This routine assumes the host_lock is held on entry. + **/ +int +scsi_internal_device_block(struct scsi_device *sdev) +{ + request_queue_t *q = sdev->request_queue; + unsigned long flags; + int err = 0; + + err = scsi_device_set_state(sdev, SDEV_BLOCK); + if (err) + return err; + + /* + * The device has transitioned to SDEV_BLOCK. Stop the + * block layer from calling the midlayer with this device's + * request queue. + */ + spin_lock_irqsave(q->queue_lock, flags); + blk_stop_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(scsi_internal_device_block); + +/** + * scsi_internal_device_unblock - resume a device after a block request + * @sdev: device to resume + * + * Called by scsi lld's or the midlayer to restart the device queue + * for the previously suspended scsi device. Called from interrupt or + * normal process context. + * + * Returns zero if successful or error if not. + * + * Notes: + * This routine transitions the device to the SDEV_RUNNING state + * (which must be a legal transition) allowing the midlayer to + * goose the queue for this device. This routine assumes the + * host_lock is held upon entry. + **/ +int +scsi_internal_device_unblock(struct scsi_device *sdev) +{ + request_queue_t *q = sdev->request_queue; + int err; + unsigned long flags; + + /* + * Try to transition the scsi device to SDEV_RUNNING + * and goose the device queue if successful. + */ + err = scsi_device_set_state(sdev, SDEV_RUNNING); + if (err) + return err; + + spin_lock_irqsave(q->queue_lock, flags); + blk_start_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(scsi_internal_device_unblock); diff -u b/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c --- b/drivers/scsi/scsi_scan.c 2004-10-06 22:21:10 -07:00 +++ b/drivers/scsi/scsi_scan.c 2004-10-06 22:21:18 -07:00 @@ -202,11 +202,12 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, uint channel, uint id, uint lun, void *hostdata) { - struct scsi_device *sdev, *device; + struct scsi_device *sdev; unsigned long flags; int display_failure_msg = 1, ret; - sdev = kmalloc(sizeof(*sdev) + shost->transportt->size, GFP_ATOMIC); + sdev = kmalloc(sizeof(*sdev) + shost->transportt->device_size, + GFP_ATOMIC); if (!sdev) goto out; @@ -265,67 +266,28 @@ } } - if (shost->transportt->setup) { - if (shost->transportt->setup(sdev)) + if (shost->transportt->device_setup) { + if (shost->transportt->device_setup(sdev)) goto out_cleanup_slave; } - if (get_device(&sdev->host->shost_gendev)) { + if (get_device(&sdev->host->shost_gendev) == NULL || + scsi_sysfs_device_initialize(sdev) != 0) + goto out_cleanup_slave; - device_initialize(&sdev->sdev_gendev); - sdev->sdev_gendev.parent = &sdev->host->shost_gendev; - sdev->sdev_gendev.bus = &scsi_bus_type; - sdev->sdev_gendev.release = scsi_device_dev_release; - sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d", - sdev->host->host_no, sdev->channel, sdev->id, - sdev->lun); - - class_device_initialize(&sdev->sdev_classdev); - sdev->sdev_classdev.dev = &sdev->sdev_gendev; - sdev->sdev_classdev.class = &sdev_class; - snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE, - "%d:%d:%d:%d", sdev->host->host_no, - sdev->channel, sdev->id, sdev->lun); - - class_device_initialize(&sdev->transport_classdev); - sdev->transport_classdev.dev = &sdev->sdev_gendev; - sdev->transport_classdev.class = sdev->host->transportt->class; - snprintf(sdev->transport_classdev.class_id, BUS_ID_SIZE, - "%d:%d:%d:%d", sdev->host->host_no, - sdev->channel, sdev->id, sdev->lun); - } else - goto out_cleanup_transport; - /* - * If there are any same target siblings, add this to the - * sibling list - */ - spin_lock_irqsave(shost->host_lock, flags); - list_for_each_entry(device, &shost->__devices, siblings) { - if (device->id == sdev->id && - device->channel == sdev->channel) { - list_add_tail(&sdev->same_target_siblings, - &device->same_target_siblings); - sdev->scsi_level = device->scsi_level; - break; - } - } + /* NOTE: this target initialisation code depends critically on + * lun scanning being sequential. */ + if (scsi_sysfs_target_initialize(sdev)) + goto out_remove_siblings; - /* - * If there wasn't another lun already configured at this - * target, then default this device to SCSI_2 until we - * know better - */ - if (!sdev->scsi_level) - sdev->scsi_level = SCSI_2; - - list_add_tail(&sdev->siblings, &shost->__devices); - spin_unlock_irqrestore(shost->host_lock, flags); return sdev; -out_cleanup_transport: - if (shost->transportt->cleanup) - shost->transportt->cleanup(sdev); +out_remove_siblings: + spin_lock_irqsave(shost->host_lock, flags); + list_del(&sdev->siblings); + list_del(&sdev->same_target_siblings); + spin_unlock_irqrestore(shost->host_lock, flags); out_cleanup_slave: if (shost->hostt->slave_destroy) shost->hostt->slave_destroy(sdev); @@ -510,10 +472,6 @@ **/ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) { - struct scsi_device *sdev_sibling; - struct scsi_target *starget; - unsigned long flags; - /* * XXX do not save the inquiry, since it can change underneath us, * save just vendor/model/rev. @@ -630,40 +588,9 @@ if (*bflags & BLIST_NOSTARTONADD) sdev->no_start_on_add = 1; - /* - * If we need to allow I/O to only one of the luns attached to - * this target id at a time set single_lun, and allocate or modify - * sdev_target. - */ - if (*bflags & BLIST_SINGLELUN) { + if (*bflags & BLIST_SINGLELUN) sdev->single_lun = 1; - spin_lock_irqsave(sdev->host->host_lock, flags); - starget = NULL; - /* - * Search for an existing target for this sdev. - */ - list_for_each_entry(sdev_sibling, &sdev->same_target_siblings, - same_target_siblings) { - if (sdev_sibling->sdev_target != NULL) { - starget = sdev_sibling->sdev_target; - break; - } - } - if (!starget) { - starget = kmalloc(sizeof(*starget), GFP_ATOMIC); - if (!starget) { - printk(ALLOC_FAILURE_MSG, __FUNCTION__); - spin_unlock_irqrestore(sdev->host->host_lock, - flags); - return SCSI_SCAN_NO_RESPONSE; - } - starget->starget_refcnt = 0; - starget->starget_sdev_user = NULL; - } - starget->starget_refcnt++; - sdev->sdev_target = starget; - spin_unlock_irqrestore(sdev->host->host_lock, flags); - } + sdev->use_10_for_rw = 1; @@ -686,7 +613,10 @@ if (*bflags & BLIST_NOT_LOCKABLE) sdev->lockable = 0; - if(sdev->host->hostt->slave_configure) + if (sdev->host->transportt->device_configure) + sdev->host->transportt->device_configure(sdev); + + if (sdev->host->hostt->slave_configure) sdev->host->hostt->slave_configure(sdev); /* @@ -803,8 +733,6 @@ } else { if (sdev->host->hostt->slave_destroy) sdev->host->hostt->slave_destroy(sdev); - if (sdev->host->transportt->cleanup) - sdev->host->transportt->cleanup(sdev); put_device(&sdev->sdev_gendev); } out: @@ -1364,6 +1292,4 @@ if (sdev->host->hostt->slave_destroy) sdev->host->hostt->slave_destroy(sdev); - if (sdev->host->transportt->cleanup) - sdev->host->transportt->cleanup(sdev); put_device(&sdev->sdev_gendev); } diff -u b/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c --- b/drivers/scsi/scsi_sysfs.c 2004-10-06 22:21:09 -07:00 +++ b/drivers/scsi/scsi_sysfs.c 2004-10-06 22:21:18 -07:00 @@ -30,6 +30,7 @@ { SDEV_DEL, "deleted" }, { SDEV_QUIESCE, "quiesce" }, { SDEV_OFFLINE, "offline" }, + { SDEV_BLOCK, "blocked" }, }; const char *scsi_device_state_name(enum scsi_device_state state) @@ -153,25 +154,36 @@ struct scsi_device *sdev; struct device *parent; unsigned long flags; + int delete; parent = dev->parent; sdev = to_scsi_device(dev); spin_lock_irqsave(sdev->host->host_lock, flags); + /* If we're the last LUN on the target, destroy the target */ + delete = list_empty(&sdev->same_target_siblings); list_del(&sdev->siblings); list_del(&sdev->same_target_siblings); list_del(&sdev->starved_entry); - if (sdev->single_lun && --sdev->sdev_target->starget_refcnt == 0) - kfree(sdev->sdev_target); spin_unlock_irqrestore(sdev->host->host_lock, flags); + if (delete) { + struct scsi_target *starget = to_scsi_target(parent); + if (!starget->create) { + device_del(parent); + if (starget->transport_classdev.class) + class_device_unregister(&starget->transport_classdev); + } + put_device(parent); + } if (sdev->request_queue) scsi_free_queue(sdev->request_queue); kfree(sdev->inquiry); kfree(sdev); - put_device(parent); + if (parent) + put_device(parent); } struct class sdev_class = { @@ -432,6 +444,14 @@ return device_create_file(dev, attr); } +static void scsi_target_dev_release(struct device *dev) +{ + struct scsi_target *starget = to_scsi_target(dev); + struct device *parent = dev->parent; + kfree(starget); + put_device(parent); +} + /** * scsi_sysfs_add_sdev - add scsi device to sysfs * @sdev: scsi_device to add @@ -442,13 +462,55 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) { struct class_device_attribute **attrs; - int error, i; + struct scsi_target *starget = sdev->sdev_target; + struct Scsi_Host *shost = sdev->host; + int error, i, create; + unsigned long flags; + + spin_lock_irqsave(shost->host_lock, flags); + create = starget->create; + starget->create = 0; + spin_unlock_irqrestore(shost->host_lock, flags); + + if (create) { + error = device_add(&starget->dev); + if (error) { + printk(KERN_ERR "Target device_add failed\n"); + return error; + } + if (starget->transport_classdev.class) { + int i; + struct class_device_attribute **attrs = + sdev->host->transportt->target_attrs; + + error = class_device_add(&starget->transport_classdev); + if (error) { + dev_printk(KERN_ERR, &starget->dev, + "Target transport add failed\n"); + return error; + } + + /* take a reference for the transport_classdev; this + * is released by the transport_class .release */ + get_device(&starget->dev); + for (i = 0; attrs[i]; i++) { + error = class_device_create_file(&starget->transport_classdev, + attrs[i]); + if (error) { + dev_printk(KERN_ERR, &starget->dev, + "Target transport attr add failed\n"); + return error; + } + } + } + } if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0) return error; error = device_add(&sdev->sdev_gendev); if (error) { + put_device(sdev->sdev_gendev.parent); printk(KERN_INFO "error 1\n"); return error; } @@ -461,7 +523,6 @@ /* take a reference for the sdev_classdev; this is * released by the sdev_class .release */ get_device(&sdev->sdev_gendev); - if (sdev->transport_classdev.class) { error = class_device_add(&sdev->transport_classdev); if (error) @@ -496,7 +557,7 @@ } if (sdev->transport_classdev.class) { - attrs = sdev->host->transportt->attrs; + attrs = sdev->host->transportt->device_attrs; for (i = 0; attrs[i]; i++) { error = class_device_create_file(&sdev->transport_classdev, attrs[i]); @@ -540,8 +601,6 @@ scsi_device_set_state(sdev, SDEV_DEL); if (sdev->host->hostt->slave_destroy) sdev->host->hostt->slave_destroy(sdev); - if (sdev->host->transportt->cleanup) - sdev->host->transportt->cleanup(sdev); put_device(&sdev->sdev_gendev); out: @@ -628,6 +687,121 @@ } } + class_device_initialize(&shost->transport_classdev); + shost->transport_classdev.class = shost->transportt->host_class; + shost->transport_classdev.dev = &shost->shost_gendev; + snprintf(shost->transport_classdev.class_id, BUS_ID_SIZE, + "host%d", shost->host_no); + + if (shost->transport_classdev.class) { + struct class_device_attribute **attrs = + shost->transportt->host_attrs; + error = class_device_add(&shost->transport_classdev); + if (error) + return error; + /* take a reference for the transport_classdev; this + * is released by the transport_class .release */ + get_device(&shost->shost_gendev); + for (i = 0; attrs[i]; i++) { + error = class_device_create_file(&shost->transport_classdev, + attrs[i]); + if (error) + return error; + } + } + + return 0; +} + +int scsi_sysfs_device_initialize(struct scsi_device *sdev) +{ + device_initialize(&sdev->sdev_gendev); + sdev->sdev_gendev.bus = &scsi_bus_type; + sdev->sdev_gendev.release = scsi_device_dev_release; + sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d", + sdev->host->host_no, sdev->channel, sdev->id, + sdev->lun); + + class_device_initialize(&sdev->sdev_classdev); + sdev->sdev_classdev.dev = &sdev->sdev_gendev; + sdev->sdev_classdev.class = &sdev_class; + snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE, + "%d:%d:%d:%d", sdev->host->host_no, + sdev->channel, sdev->id, sdev->lun); + + class_device_initialize(&sdev->transport_classdev); + sdev->transport_classdev.dev = &sdev->sdev_gendev; + sdev->transport_classdev.class = sdev->host->transportt->device_class; + snprintf(sdev->transport_classdev.class_id, BUS_ID_SIZE, + "%d:%d:%d:%d", sdev->host->host_no, + sdev->channel, sdev->id, sdev->lun); + return 0; +} + +int scsi_sysfs_target_initialize(struct scsi_device *sdev) +{ + struct scsi_target *starget = NULL; + struct Scsi_Host *shost = sdev->host; + struct scsi_device *device; + struct device *dev = NULL; + unsigned long flags; + int create = 0; + + spin_lock_irqsave(shost->host_lock, flags); + /* + * Search for an existing target for this sdev. + */ + list_for_each_entry(device, &shost->__devices, siblings) { + if (device->id == sdev->id && + device->channel == sdev->channel) { + list_add_tail(&sdev->same_target_siblings, + &device->same_target_siblings); + sdev->scsi_level = device->scsi_level; + starget = device->sdev_target; + break; + } + } + + if (!starget) { + const int size = sizeof(*starget) + + shost->transportt->target_size; + starget = kmalloc(size, GFP_ATOMIC); + if (!starget) { + printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__); + spin_unlock_irqrestore(shost->host_lock, + flags); + return -ENOMEM; + } + memset(starget, 0, size); + dev = &starget->dev; + device_initialize(dev); + dev->parent = &shost->shost_gendev; + dev->release = scsi_target_dev_release; + sprintf(dev->bus_id, "target%d:%d:%d", + shost->host_no, sdev->channel, sdev->id); + class_device_initialize(&starget->transport_classdev); + starget->transport_classdev.dev = &starget->dev; + starget->transport_classdev.class = shost->transportt->target_class; + snprintf(starget->transport_classdev.class_id, BUS_ID_SIZE, + "target%d:%d:%d", + shost->host_no, sdev->channel, sdev->id); + starget->id = sdev->id; + starget->channel = sdev->channel; + create = starget->create = 1; + /* + * If there wasn't another lun already configured at + * this target, then default this device to SCSI_2 + * until we know better + */ + sdev->scsi_level = SCSI_2; + } + get_device(&starget->dev); + sdev->sdev_gendev.parent = &starget->dev; + sdev->sdev_target = starget; + list_add_tail(&sdev->siblings, &shost->__devices); + spin_unlock_irqrestore(shost->host_lock, flags); + if (create && shost->transportt->target_setup) + shost->transportt->target_setup(starget); return 0; } diff -u b/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c --- b/drivers/scsi/sym53c8xx_2/sym_glue.c 2004-10-06 22:21:10 -07:00 +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c 2004-10-06 22:21:19 -07:00 @@ -1083,7 +1083,8 @@ lp->s.scdev_depth = depth_to_use; sym_tune_dev_queuing(np, device->id, device->lun, reqtags); - spi_dv_device(device); + if (!spi_initial_dv(device->sdev_target)) + spi_dv_device(device); return 0; } @@ -2302,35 +2303,61 @@ attach_count--; } -static void sym2_get_offset(struct scsi_device *sdev) +static void sym2_get_signalling(struct Scsi_Host *shost) { - struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; - struct sym_tcb *tp = &np->target[sdev->id]; + struct sym_hcb *np = ((struct host_data *)shost->hostdata)->ncb; + enum spi_signal_type type; - spi_offset(sdev) = tp->tinfo.curr.offset; + switch (np->scsi_mode) { + case SMODE_SE: + type = SPI_SIGNAL_SE; + break; + case SMODE_LVD: + type = SPI_SIGNAL_LVD; + break; + case SMODE_HVD: + type = SPI_SIGNAL_HVD; + break; + default: + type = SPI_SIGNAL_UNKNOWN; + break; + } + spi_signalling(shost) = type; +} + +static void sym2_get_offset(struct scsi_target *starget) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct sym_hcb *np = ((struct host_data *)shost->hostdata)->ncb; + struct sym_tcb *tp = &np->target[starget->id]; + + spi_offset(starget) = tp->tinfo.curr.offset; } -static void sym2_set_offset(struct scsi_device *sdev, int offset) +static void sym2_set_offset(struct scsi_target *starget, int offset) { - struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; - struct sym_tcb *tp = &np->target[sdev->id]; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct sym_hcb *np = ((struct host_data *)shost->hostdata)->ncb; + struct sym_tcb *tp = &np->target[starget->id]; tp->tinfo.goal.offset = offset; } -static void sym2_get_period(struct scsi_device *sdev) +static void sym2_get_period(struct scsi_target *starget) { - struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; - struct sym_tcb *tp = &np->target[sdev->id]; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct sym_hcb *np = ((struct host_data *)shost->hostdata)->ncb; + struct sym_tcb *tp = &np->target[starget->id]; - spi_period(sdev) = tp->tinfo.curr.period; + spi_period(starget) = tp->tinfo.curr.period; } -static void sym2_set_period(struct scsi_device *sdev, int period) +static void sym2_set_period(struct scsi_target *starget, int period) { - struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; - struct sym_tcb *tp = &np->target[sdev->id]; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct sym_hcb *np = ((struct host_data *)shost->hostdata)->ncb; + struct sym_tcb *tp = &np->target[starget->id]; /* have to have DT for these transfers */ if (period <= np->minsync) @@ -2339,18 +2366,20 @@ tp->tinfo.goal.period = period; } -static void sym2_get_width(struct scsi_device *sdev) +static void sym2_get_width(struct scsi_target *starget) { - struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; - struct sym_tcb *tp = &np->target[sdev->id]; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct sym_hcb *np = ((struct host_data *)shost->hostdata)->ncb; + struct sym_tcb *tp = &np->target[starget->id]; - spi_width(sdev) = tp->tinfo.curr.width ? 1 : 0; + spi_width(starget) = tp->tinfo.curr.width ? 1 : 0; } -static void sym2_set_width(struct scsi_device *sdev, int width) +static void sym2_set_width(struct scsi_target *starget, int width) { - struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; - struct sym_tcb *tp = &np->target[sdev->id]; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct sym_hcb *np = ((struct host_data *)shost->hostdata)->ncb; + struct sym_tcb *tp = &np->target[starget->id]; /* It is illegal to have DT set on narrow transfers */ if (width == 0) @@ -2359,18 +2388,20 @@ tp->tinfo.goal.width = width; } -static void sym2_get_dt(struct scsi_device *sdev) +static void sym2_get_dt(struct scsi_target *starget) { - struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; - struct sym_tcb *tp = &np->target[sdev->id]; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct sym_hcb *np = ((struct host_data *)shost->hostdata)->ncb; + struct sym_tcb *tp = &np->target[starget->id]; - spi_dt(sdev) = (tp->tinfo.curr.options & PPR_OPT_DT) ? 1 : 0; + spi_dt(starget) = (tp->tinfo.curr.options & PPR_OPT_DT) ? 1 : 0; } -static void sym2_set_dt(struct scsi_device *sdev, int dt) +static void sym2_set_dt(struct scsi_target *starget, int dt) { - struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; - struct sym_tcb *tp = &np->target[sdev->id]; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct sym_hcb *np = ((struct host_data *)shost->hostdata)->ncb; + struct sym_tcb *tp = &np->target[starget->id]; if (dt) tp->tinfo.goal.options |= PPR_OPT_DT; @@ -2392,6 +2423,7 @@ .get_dt = sym2_get_dt, .set_dt = sym2_set_dt, .show_dt = 1, + .get_signalling = sym2_get_signalling, }; static struct pci_device_id sym2_id_table[] __devinitdata = { diff -u b/include/scsi/scsi_device.h b/include/scsi/scsi_device.h --- b/include/scsi/scsi_device.h 2004-10-06 22:21:09 -07:00 +++ b/include/scsi/scsi_device.h 2004-10-06 22:21:18 -07:00 @@ -30,6 +30,9 @@ * originate in the mid-layer) */ SDEV_OFFLINE, /* Device offlined (by error handling or * user request */ + SDEV_BLOCK, /* Device blocked by scsi lld. No scsi + * commands from user or midlayer should be issued + * to the scsi lld. */ }; struct scsi_device { @@ -122,7 +125,7 @@ struct class_device transport_classdev; enum scsi_device_state sdev_state; - unsigned long transport_data[0]; + unsigned long sdev_data[0]; } __attribute__((aligned(sizeof(unsigned long)))); #define to_scsi_device(d) \ container_of(d, struct scsi_device, sdev_gendev) @@ -131,6 +134,30 @@ #define transport_class_to_sdev(class_dev) \ container_of(class_dev, struct scsi_device, transport_classdev) +/* + * scsi_target: representation of a scsi target, for now, this is only + * used for single_lun devices. If no one has active IO to the target, + * starget_sdev_user is NULL, else it points to the active sdev. + */ +struct scsi_target { + struct scsi_device *starget_sdev_user; + struct device dev; + unsigned int channel; + unsigned int id; /* target id ... replace + * scsi_device.id eventually */ + struct class_device transport_classdev; + unsigned long create:1; /* signal that it needs to be added */ + unsigned long starget_data[0]; +} __attribute__((aligned(sizeof(unsigned long)))); + +#define to_scsi_target(d) container_of(d, struct scsi_target, dev) +static inline struct scsi_target *scsi_target(struct scsi_device *sdev) +{ + return to_scsi_target(sdev->sdev_gendev.parent); +} +#define transport_class_to_starget(class_dev) \ + container_of(class_dev, struct scsi_target, transport_classdev) + extern struct scsi_device *__scsi_add_device(struct Scsi_Host *, uint, uint, uint, void *hostdata); #define scsi_add_device(host, channel, target, lun) \ @@ -193,6 +220,8 @@ enum scsi_device_state state); extern int scsi_device_quiesce(struct scsi_device *sdev); extern void scsi_device_resume(struct scsi_device *sdev); +extern void scsi_target_quiesce(struct scsi_target *); +extern void scsi_target_resume(struct scsi_target *); extern const char *scsi_device_state_name(enum scsi_device_state); static inline int scsi_device_online(struct scsi_device *sdev) { diff -u b/include/scsi/scsi_host.h b/include/scsi/scsi_host.h --- b/include/scsi/scsi_host.h 2004-10-06 22:21:09 -07:00 +++ b/include/scsi/scsi_host.h 2004-10-06 22:21:18 -07:00 @@ -502,6 +502,13 @@ struct list_head sht_legacy_list; /* + * Points to the transport data (if any) which is allocated + * separately + */ + void *shost_data; + struct class_device transport_classdev; + + /* * We should ensure that this is aligned, both for better performance * and also because some compilers (m68k) don't automatically force * alignment to a long boundary. @@ -513,6 +520,9 @@ container_of(d, struct Scsi_Host, shost_gendev) #define class_to_shost(d) \ container_of(d, struct Scsi_Host, shost_classdev) +#define transport_class_to_shost(class_dev) \ + container_of(class_dev, struct Scsi_Host, transport_classdev) + extern struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *, int); extern int __must_check scsi_add_host(struct Scsi_Host *, struct device *); only in patch2: unchanged: --- a/drivers/s390/scsi/zfcp_scsi.c 2004-10-06 22:21:19 -07:00 +++ b/drivers/s390/scsi/zfcp_scsi.c 2004-10-06 22:21:19 -07:00 @@ -48,6 +48,8 @@ static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int, scsi_id_t, scsi_lun_t); +static struct zfcp_port * zfcp_port_lookup(struct zfcp_adapter *, int, + scsi_id_t); static struct device_attribute *zfcp_sysfs_sdev_attrs[]; @@ -398,6 +400,26 @@ out: return retval; } +/* + * function: zfcp_unit_tgt_lookup + * + * purpose: + * + * returns: + * + * context: + */ +static struct zfcp_port * +zfcp_port_lookup(struct zfcp_adapter *adapter, int channel, scsi_id_t id) +{ + struct zfcp_port *port; + + list_for_each_entry(port, &adapter->port_list_head, list) { + if (id == port->scsi_id) + return port; + } + return (zfcp_port *)NULL; +} /* * function: zfcp_scsi_eh_abort_handler @@ -839,39 +861,63 @@ * Support functions for FC transport class */ static void -zfcp_get_port_id(struct scsi_device *sdev) +zfcp_get_port_id(struct scsi_target *starget) { - struct zfcp_unit *unit; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct zfcp_adapter *adapter = (struct zfcp_adapter *)shost->hostdata[0]; + struct zfcp_port *port; + unsigned long flags; - unit = (struct zfcp_unit *) sdev->hostdata; - fc_port_id(sdev) = unit->port->d_id; + read_lock_irqsave(&zfcp_data.config_lock, flags); + port = zfcp_port_lookup(adapter, starget->channel, starget->id); + if (port) + fc_starget_port_id(starget) = port->d_id; + else + fc_starget_port_id(starget) = -1; + read_unlock_irqrestore(&zfcp_data.config_lock, flags); } static void -zfcp_get_port_name(struct scsi_device *sdev) +zfcp_get_port_name(struct scsi_target *starget) { - struct zfcp_unit *unit; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct zfcp_adapter *adapter = (struct zfcp_adapter *)shost->hostdata[0]; + struct zfcp_port *port; + unsigned long flags; - unit = (struct zfcp_unit *) sdev->hostdata; - fc_port_name(sdev) = unit->port->wwpn; + read_lock_irqsave(&zfcp_data.config_lock, flags); + port = zfcp_port_lookup(adapter, starget->channel, starget->id); + if (port) + fc_starget_port_name(starget) = port->wwpn; + else + fc_starget_port_name(starget) = -1; + read_unlock_irqrestore(&zfcp_data.config_lock, flags); } static void -zfcp_get_node_name(struct scsi_device *sdev) +zfcp_get_node_name(struct scsi_target *starget) { - struct zfcp_unit *unit; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct zfcp_adapter *adapter = (struct zfcp_adapter *)shost->hostdata[0]; + struct zfcp_port *port; + unsigned long flags; - unit = (struct zfcp_unit *) sdev->hostdata; - fc_node_name(sdev) = unit->port->wwnn; + read_lock_irqsave(&zfcp_data.config_lock, flags); + port = zfcp_port_lookup(adapter, starget->channel, starget->id); + if (port) + fc_starget_node_name(starget) = port->wwnn; + else + fc_starget_node_name(starget) = -1; + read_unlock_irqrestore(&zfcp_data.config_lock, flags); } struct fc_function_template zfcp_transport_functions = { - .get_port_id = zfcp_get_port_id, - .get_port_name = zfcp_get_port_name, - .get_node_name = zfcp_get_node_name, - .show_port_id = 1, - .show_port_name = 1, - .show_node_name = 1, + .get_starget_port_id = zfcp_get_port_id, + .get_starget_port_name = zfcp_get_port_name, + .get_starget_node_name = zfcp_get_node_name, + .show_starget_port_id = 1, + .show_starget_port_name = 1, + .show_starget_node_name = 1, }; /** only in patch2: unchanged: --- a/drivers/scsi/53c700.c 2004-10-06 22:21:18 -07:00 +++ b/drivers/scsi/53c700.c 2004-10-06 22:21:18 -07:00 @@ -287,8 +287,9 @@ struct NCR_700_Host_Parameters *hostdata = (struct NCR_700_Host_Parameters *)SDp->host->hostdata[0]; - return NCR_700_offset_period_to_sxfer(hostdata, spi_offset(SDp), - spi_period(SDp)); + return NCR_700_offset_period_to_sxfer(hostdata, + spi_offset(SDp->sdev_target), + spi_period(SDp->sdev_target)); } struct Scsi_Host * @@ -403,6 +404,8 @@ (hostdata->fast ? "53c700-66" : "53c700"), hostdata->rev, hostdata->differential ? "(Differential)" : ""); + spi_signalling(host) = hostdata->differential ? SPI_SIGNAL_HVD : + SPI_SIGNAL_SE; /* reset the chip */ NCR_700_chip_reset(host); @@ -803,7 +806,7 @@ } if(NCR_700_is_flag_set(SCp->device, NCR_700_DEV_PRINT_SYNC_NEGOTIATION)) { - if(spi_offset(SCp->device) != 0) + if(spi_offset(SCp->device->sdev_target) != 0) printk(KERN_INFO "scsi%d: (%d:%d) Synchronous at offset %d, period %dns\n", host->host_no, pun, lun, offset, period*4); @@ -813,8 +816,8 @@ NCR_700_clear_flag(SCp->device, NCR_700_DEV_PRINT_SYNC_NEGOTIATION); } - spi_offset(SCp->device) = offset; - spi_period(SCp->device) = period; + spi_offset(SCp->device->sdev_target) = offset; + spi_period(SCp->device->sdev_target) = period; NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); @@ -894,7 +897,8 @@ case A_REJECT_MSG: if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION)) { /* Rejected our sync negotiation attempt */ - spi_period(SCp->device) = spi_offset(SCp->device) = 0; + spi_period(SCp->device->sdev_target) = + spi_offset(SCp->device->sdev_target) = 0; NCR_700_set_flag(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC); NCR_700_clear_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); } else if(SCp != NULL && NCR_700_is_flag_set(SCp->device, NCR_700_DEV_BEGIN_TAG_QUEUEING)) { @@ -1420,8 +1424,8 @@ NCR_700_is_flag_clear(SCp->device, NCR_700_DEV_NEGOTIATED_SYNC)) { memcpy(&hostdata->msgout[count], NCR_700_SDTR_msg, sizeof(NCR_700_SDTR_msg)); - hostdata->msgout[count+3] = spi_period(SCp->device); - hostdata->msgout[count+4] = spi_offset(SCp->device); + hostdata->msgout[count+3] = spi_period(SCp->device->sdev_target); + hostdata->msgout[count+4] = spi_offset(SCp->device->sdev_target); count += sizeof(NCR_700_SDTR_msg); NCR_700_set_flag(SCp->device, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); } @@ -1999,10 +2003,11 @@ } STATIC void -NCR_700_set_period(struct scsi_device *SDp, int period) +NCR_700_set_period(struct scsi_target *STp, int period) { + struct Scsi_Host *SHp = dev_to_shost(STp->dev.parent); struct NCR_700_Host_Parameters *hostdata = - (struct NCR_700_Host_Parameters *)SDp->host->hostdata[0]; + (struct NCR_700_Host_Parameters *)SHp->hostdata[0]; if(!hostdata->fast) return; @@ -2010,17 +2015,18 @@ if(period < hostdata->min_period) period = hostdata->min_period; - spi_period(SDp) = period; - NCR_700_clear_flag(SDp, NCR_700_DEV_NEGOTIATED_SYNC); - NCR_700_clear_flag(SDp, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); - NCR_700_set_flag(SDp, NCR_700_DEV_PRINT_SYNC_NEGOTIATION); + spi_period(STp) = period; + spi_flags(STp) &= ~(NCR_700_DEV_NEGOTIATED_SYNC | + NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + spi_flags(STp) |= NCR_700_DEV_PRINT_SYNC_NEGOTIATION; } STATIC void -NCR_700_set_offset(struct scsi_device *SDp, int offset) +NCR_700_set_offset(struct scsi_target *STp, int offset) { + struct Scsi_Host *SHp = dev_to_shost(STp->dev.parent); struct NCR_700_Host_Parameters *hostdata = - (struct NCR_700_Host_Parameters *)SDp->host->hostdata[0]; + (struct NCR_700_Host_Parameters *)SHp->hostdata[0]; int max_offset = hostdata->chip710 ? NCR_710_MAX_OFFSET : NCR_700_MAX_OFFSET; @@ -2031,14 +2037,14 @@ offset = max_offset; /* if we're currently async, make sure the period is reasonable */ - if(spi_offset(SDp) == 0 && (spi_period(SDp) < hostdata->min_period || - spi_period(SDp) > 0xff)) - spi_period(SDp) = hostdata->min_period; - - spi_offset(SDp) = offset; - NCR_700_clear_flag(SDp, NCR_700_DEV_NEGOTIATED_SYNC); - NCR_700_clear_flag(SDp, NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); - NCR_700_set_flag(SDp, NCR_700_DEV_PRINT_SYNC_NEGOTIATION); + if(spi_offset(STp) == 0 && (spi_period(STp) < hostdata->min_period || + spi_period(STp) > 0xff)) + spi_period(STp) = hostdata->min_period; + + spi_offset(STp) = offset; + spi_flags(STp) &= ~(NCR_700_DEV_NEGOTIATED_SYNC | + NCR_700_DEV_BEGIN_SYNC_NEGOTIATION); + spi_flags(STp) |= NCR_700_DEV_PRINT_SYNC_NEGOTIATION; } @@ -2058,10 +2064,11 @@ } if(hostdata->fast) { /* Find the correct offset and period via domain validation */ - spi_dv_device(SDp); + if (!spi_initial_dv(SDp->sdev_target)) + spi_dv_device(SDp); } else { - spi_offset(SDp) = 0; - spi_period(SDp) = 0; + spi_offset(SDp->sdev_target) = 0; + spi_period(SDp->sdev_target) = 0; } return 0; } only in patch2: unchanged: --- a/drivers/scsi/53c700.h 2004-10-06 22:21:18 -07:00 +++ b/drivers/scsi/53c700.h 2004-10-06 22:21:18 -07:00 @@ -121,22 +121,22 @@ static inline int NCR_700_is_flag_set(struct scsi_device *SDp, __u32 flag) { - return (((unsigned long)SDp->hostdata) & flag) == flag; + return (spi_flags(SDp->sdev_target) & flag) == flag; } static inline int NCR_700_is_flag_clear(struct scsi_device *SDp, __u32 flag) { - return (((unsigned long)SDp->hostdata) & flag) == 0; + return (spi_flags(SDp->sdev_target) & flag) == 0; } static inline void NCR_700_set_flag(struct scsi_device *SDp, __u32 flag) { - SDp->hostdata = (void *)((long)SDp->hostdata | (flag & 0xffff0000)); + spi_flags(SDp->sdev_target) |= flag; } static inline void NCR_700_clear_flag(struct scsi_device *SDp, __u32 flag) { - SDp->hostdata = (void *)((long)SDp->hostdata & ~(flag & 0xffff0000)); + spi_flags(SDp->sdev_target) &= ~flag; } struct NCR_700_command_slot { only in patch2: unchanged: --- a/drivers/scsi/NCR_D700.c 2004-10-06 22:21:18 -07:00 +++ b/drivers/scsi/NCR_D700.c 2004-10-06 22:21:18 -07:00 @@ -99,6 +99,9 @@ #include #include #include +#include +#include +#include #include "53c700.h" #include "NCR_D700.h" only in patch2: unchanged: --- a/drivers/scsi/NCR_Q720.c 2004-10-06 22:21:18 -07:00 +++ b/drivers/scsi/NCR_Q720.c 2004-10-06 22:21:18 -07:00 @@ -358,13 +358,19 @@ static int __init NCR_Q720_init(void) { - return mca_register_driver(&NCR_Q720_driver); + int ret = ncr53c8xx_init(); + if (!ret) + ret = mca_register_driver(&NCR_Q720_driver); + if (ret) + ncr53c8xx_exit(); + return ret; } static void __exit NCR_Q720_exit(void) { mca_unregister_driver(&NCR_Q720_driver); + ncr53c8xx_exit(); } module_init(NCR_Q720_init); only in patch2: unchanged: --- a/drivers/scsi/hosts.c 2004-10-06 22:21:18 -07:00 +++ b/drivers/scsi/hosts.c 2004-10-06 22:21:18 -07:00 @@ -82,6 +82,8 @@ set_bit(SHOST_DEL, &shost->shost_state); class_device_unregister(&shost->shost_classdev); + if (shost->transport_classdev.class) + class_device_unregister(&shost->transport_classdev); device_del(&shost->shost_gendev); } @@ -154,6 +156,7 @@ scsi_proc_hostdir_rm(shost->hostt); scsi_destroy_command_freelist(shost); + kfree(shost->shost_data); /* * Some drivers (eg aha1542) do scsi_register()/scsi_unregister() @@ -278,15 +281,26 @@ snprintf(shost->shost_classdev.class_id, BUS_ID_SIZE, "host%d", shost->host_no); + if (shost->transportt->host_size && + (shost->shost_data = kmalloc(shost->transportt->host_size, + GFP_KERNEL)) == NULL) + goto fail_destroy_freelist; + + if (shost->transportt->host_setup) + shost->transportt->host_setup(shost); + shost->eh_notify = &complete; rval = kernel_thread(scsi_error_handler, shost, 0); if (rval < 0) - goto fail_destroy_freelist; + goto fail_free_shost_data; wait_for_completion(&complete); shost->eh_notify = NULL; + scsi_proc_hostdir_add(shost->hostt); return shost; + fail_free_shost_data: + kfree(shost->shost_data); fail_destroy_freelist: scsi_destroy_command_freelist(shost); fail_kfree: only in patch2: unchanged: --- a/drivers/scsi/lasi700.c 2004-10-06 22:21:18 -07:00 +++ b/drivers/scsi/lasi700.c 2004-10-06 22:21:18 -07:00 @@ -50,6 +50,9 @@ #include #include +#include +#include +#include #include "lasi700.h" #include "53c700.h" only in patch2: unchanged: --- a/drivers/scsi/scsi_priv.h 2004-10-06 22:21:18 -07:00 +++ b/drivers/scsi/scsi_priv.h 2004-10-06 22:21:18 -07:00 @@ -58,16 +58,6 @@ */ #define SCAN_WILD_CARD ~0 -/* - * scsi_target: representation of a scsi target, for now, this is only - * used for single_lun devices. If no one has active IO to the target, - * starget_sdev_user is NULL, else it points to the active sdev. - */ -struct scsi_target { - struct scsi_device *starget_sdev_user; - unsigned int starget_refcnt; -}; - /* hosts.c */ extern int scsi_init_hosts(void); extern void scsi_exit_hosts(void); @@ -156,9 +146,20 @@ extern int scsi_sysfs_add_host(struct Scsi_Host *); extern int scsi_sysfs_register(void); extern void scsi_sysfs_unregister(void); +extern int scsi_sysfs_device_initialize(struct scsi_device *); +extern int scsi_sysfs_target_initialize(struct scsi_device *); extern struct scsi_transport_template blank_transport_template; extern struct class sdev_class; extern struct bus_type scsi_bus_type; + +/* + * internal scsi timeout functions: for use by mid-layer and transport + * classes. + */ + +#define SCSI_DEVICE_BLOCK_MAX_TIMEOUT (HZ*60) +extern int scsi_internal_device_block(struct scsi_device *sdev); +extern int scsi_internal_device_unblock(struct scsi_device *sdev); #endif /* _SCSI_PRIV_H */ only in patch2: unchanged: --- a/drivers/scsi/scsi_transport_fc.c 2004-10-06 22:21:18 -07:00 +++ b/drivers/scsi/scsi_transport_fc.c 2004-10-06 22:21:18 -07:00 @@ -23,23 +23,31 @@ #include #include #include +#include "scsi_priv.h" #define FC_PRINTK(x, l, f, a...) printk(l "scsi(%d:%d:%d:%d): " f, (x)->host->host_no, (x)->channel, (x)->id, (x)->lun , ##a) static void transport_class_release(struct class_device *class_dev); +static void host_class_release(struct class_device *class_dev); -#define FC_NUM_ATTRS 3 /* increase this if you add attributes */ -#define FC_OTHER_ATTRS 0 /* increase this if you add "always on" - * attributes */ +#define FC_STARGET_NUM_ATTRS 4 /* increase this if you add attributes */ +#define FC_STARGET_OTHER_ATTRS 0 /* increase this if you add "always on" + * attributes */ +#define FC_HOST_NUM_ATTRS 1 struct fc_internal { struct scsi_transport_template t; struct fc_function_template *f; /* The actual attributes */ - struct class_device_attribute private_attrs[FC_NUM_ATTRS]; + struct class_device_attribute private_starget_attrs[ + FC_STARGET_NUM_ATTRS]; /* The array of null terminated pointers to attributes * needed by scsi_sysfs.c */ - struct class_device_attribute *attrs[FC_NUM_ATTRS + FC_OTHER_ATTRS + 1]; + struct class_device_attribute *starget_attrs[ + FC_STARGET_NUM_ATTRS + FC_STARGET_OTHER_ATTRS + 1]; + + struct class_device_attribute private_host_attrs[FC_HOST_NUM_ATTRS]; + struct class_device_attribute *host_attrs[FC_HOST_NUM_ATTRS + 1]; }; #define to_fc_internal(tmpl) container_of(tmpl, struct fc_internal, t) @@ -49,101 +57,211 @@ .release = transport_class_release, }; +struct class fc_host_class = { + .name = "fc_host", + .release = host_class_release, +}; + static __init int fc_transport_init(void) { + int error = class_register(&fc_host_class); + if (error) + return error; return class_register(&fc_transport_class); } static void __exit fc_transport_exit(void) { class_unregister(&fc_transport_class); + class_unregister(&fc_host_class); } -static int fc_setup_transport_attrs(struct scsi_device *sdev) +static int fc_setup_starget_transport_attrs(struct scsi_target *starget) { - /* I'm not sure what values are invalid. We should pick some invalid - * values for the defaults */ - fc_node_name(sdev) = -1; - fc_port_name(sdev) = -1; - fc_port_id(sdev) = -1; + /* + * Set default values easily detected by the midlayer as + * failure cases. The scsi lldd is responsible for initializing + * all transport attributes to valid values per target. + */ + fc_starget_node_name(starget) = -1; + fc_starget_port_name(starget) = -1; + fc_starget_port_id(starget) = -1; + fc_starget_dev_loss_tmo(starget) = -1; + init_timer(&fc_starget_dev_loss_timer(starget)); + return 0; +} +static int fc_setup_host_transport_attrs(struct Scsi_Host *shost) +{ + /* + * Set default values easily detected by the midlayer as + * failure cases. The scsi lldd is responsible for initializing + * all transport attributes to valid values per host. + */ + fc_host_link_down_tmo(shost) = -1; + init_timer(&fc_host_link_down_timer(shost)); return 0; } static void transport_class_release(struct class_device *class_dev) { - struct scsi_device *sdev = transport_class_to_sdev(class_dev); - put_device(&sdev->sdev_gendev); + struct scsi_target *starget = transport_class_to_starget(class_dev); + put_device(&starget->dev); } -#define fc_transport_show_function(field, format_string, cast) \ - \ +static void host_class_release(struct class_device *class_dev) +{ + struct Scsi_Host *shost = transport_class_to_shost(class_dev); + put_device(&shost->shost_gendev); +} + + +/* + * Remote Port Attribute Management + */ + +#define fc_starget_show_function(field, format_string, cast) \ static ssize_t \ -show_fc_transport_##field (struct class_device *cdev, char *buf) \ +show_fc_starget_##field (struct class_device *cdev, char *buf) \ { \ - struct scsi_device *sdev = transport_class_to_sdev(cdev); \ - struct fc_transport_attrs *tp; \ - struct fc_internal *i = to_fc_internal(sdev->host->transportt); \ - tp = (struct fc_transport_attrs *)&sdev->transport_data; \ - if (i->f->get_##field) \ - i->f->get_##field(sdev); \ + struct scsi_target *starget = transport_class_to_starget(cdev); \ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ + struct fc_starget_attrs *tp; \ + struct fc_internal *i = to_fc_internal(shost->transportt); \ + tp = (struct fc_starget_attrs *)&starget->starget_data; \ + if (i->f->get_starget_##field) \ + i->f->get_starget_##field(starget); \ return snprintf(buf, 20, format_string, cast tp->field); \ } -#define fc_transport_store_function(field, format_string) \ +#define fc_starget_store_function(field, format_string) \ static ssize_t \ -store_fc_transport_##field(struct class_device *cdev, const char *buf, \ +store_fc_starget_##field(struct class_device *cdev, const char *buf, \ size_t count) \ { \ int val; \ - struct scsi_device *sdev = transport_class_to_sdev(cdev); \ - struct fc_internal *i = to_fc_internal(sdev->host->transportt); \ + struct scsi_target *starget = transport_class_to_starget(cdev); \ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ + struct fc_internal *i = to_fc_internal(shost->transportt); \ \ val = simple_strtoul(buf, NULL, 0); \ - i->f->set_##field(sdev, val); \ + i->f->set_starget_##field(starget, val); \ return count; \ } -#define fc_transport_rd_attr(field, format_string) \ - fc_transport_show_function(field, format_string, ) \ +#define fc_starget_rd_attr(field, format_string) \ + fc_starget_show_function(field, format_string, ) \ +static CLASS_DEVICE_ATTR(field, S_IRUGO, \ + show_fc_starget_##field, NULL) + +#define fc_starget_rd_attr_cast(field, format_string, cast) \ + fc_starget_show_function(field, format_string, (cast)) \ static CLASS_DEVICE_ATTR(field, S_IRUGO, \ - show_fc_transport_##field, NULL) + show_fc_starget_##field, NULL) -#define fc_transport_rd_attr_cast(field, format_string, cast) \ - fc_transport_show_function(field, format_string, (cast)) \ -static CLASS_DEVICE_ATTR( field, S_IRUGO, \ - show_fc_transport_##field, NULL) - -#define fc_transport_rw_attr(field, format_string) \ - fc_transport_show_function(field, format_string, ) \ - fc_transport_store_function(field, format_string) \ +#define fc_starget_rw_attr(field, format_string) \ + fc_starget_show_function(field, format_string, ) \ + fc_starget_store_function(field, format_string) \ static CLASS_DEVICE_ATTR(field, S_IRUGO | S_IWUSR, \ - show_fc_transport_##field, \ - store_fc_transport_##field) + show_fc_starget_##field, \ + store_fc_starget_##field) -/* the FiberChannel Tranport Attributes: */ -fc_transport_rd_attr_cast(node_name, "0x%llx\n", unsigned long long); -fc_transport_rd_attr_cast(port_name, "0x%llx\n", unsigned long long); -fc_transport_rd_attr(port_id, "0x%06x\n"); - -#define SETUP_ATTRIBUTE_RD(field) \ - i->private_attrs[count] = class_device_attr_##field; \ - i->private_attrs[count].attr.mode = S_IRUGO; \ - i->private_attrs[count].store = NULL; \ - i->attrs[count] = &i->private_attrs[count]; \ - if (i->f->show_##field) \ +#define SETUP_STARGET_ATTRIBUTE_RD(field) \ + i->private_starget_attrs[count] = class_device_attr_##field; \ + i->private_starget_attrs[count].attr.mode = S_IRUGO; \ + i->private_starget_attrs[count].store = NULL; \ + i->starget_attrs[count] = &i->private_starget_attrs[count]; \ + if (i->f->show_starget_##field) \ count++ -#define SETUP_ATTRIBUTE_RW(field) \ - i->private_attrs[count] = class_device_attr_##field; \ - if (!i->f->set_##field) { \ - i->private_attrs[count].attr.mode = S_IRUGO; \ - i->private_attrs[count].store = NULL; \ - } \ - i->attrs[count] = &i->private_attrs[count]; \ - if (i->f->show_##field) \ +#define SETUP_STARGET_ATTRIBUTE_RW(field) \ + i->private_starget_attrs[count] = class_device_attr_##field; \ + if (!i->f->set_starget_##field) { \ + i->private_starget_attrs[count].attr.mode = S_IRUGO; \ + i->private_starget_attrs[count].store = NULL; \ + } \ + i->starget_attrs[count] = &i->private_starget_attrs[count]; \ + if (i->f->show_starget_##field) \ count++ +/* The FC Tranport Remote Port (Target) Attributes: */ +fc_starget_rd_attr_cast(node_name, "0x%llx\n", unsigned long long); +fc_starget_rd_attr_cast(port_name, "0x%llx\n", unsigned long long); +fc_starget_rd_attr(port_id, "0x%06x\n"); +fc_starget_rw_attr(dev_loss_tmo, "%d\n"); + + +/* + * Host Attribute Management + */ + +#define fc_host_show_function(field, format_string, cast) \ +static ssize_t \ +show_fc_host_##field (struct class_device *cdev, char *buf) \ +{ \ + struct Scsi_Host *shost = transport_class_to_shost(cdev); \ + struct fc_host_attrs *tp; \ + struct fc_internal *i = to_fc_internal(shost->transportt); \ + tp = (struct fc_host_attrs *)&shost->shost_data; \ + if (i->f->get_host_##field) \ + i->f->get_host_##field(shost); \ + return snprintf(buf, 20, format_string, cast tp->field); \ +} + +#define fc_host_store_function(field, format_string) \ +static ssize_t \ +store_fc_host_##field(struct class_device *cdev, const char *buf, \ + size_t count) \ +{ \ + int val; \ + struct Scsi_Host *shost = transport_class_to_shost(cdev); \ + struct fc_internal *i = to_fc_internal(shost->transportt); \ + \ + val = simple_strtoul(buf, NULL, 0); \ + i->f->set_host_##field(shost, val); \ + return count; \ +} + +#define fc_host_rd_attr(field, format_string) \ + fc_host_show_function(field, format_string, ) \ +static CLASS_DEVICE_ATTR(host_##field, S_IRUGO, \ + show_fc_host_##field, NULL) + +#define fc_host_rd_attr_cast(field, format_string, cast) \ + fc_host_show_function(field, format_string, (cast)) \ +static CLASS_DEVICE_ATTR(host_##field, S_IRUGO, \ + show_fc_host_##field, NULL) + +#define fc_host_rw_attr(field, format_string) \ + fc_host_show_function(field, format_string, ) \ + fc_host_store_function(field, format_string) \ +static CLASS_DEVICE_ATTR(host_##field, S_IRUGO | S_IWUSR, \ + show_fc_host_##field, \ + store_fc_host_##field) + +#define SETUP_HOST_ATTRIBUTE_RD(field) \ + i->private_host_attrs[count] = class_device_attr_host_##field; \ + i->private_host_attrs[count].attr.mode = S_IRUGO; \ + i->private_host_attrs[count].store = NULL; \ + i->host_attrs[count] = &i->private_host_attrs[count]; \ + if (i->f->show_host_##field) \ + count++ + +#define SETUP_HOST_ATTRIBUTE_RW(field) \ + i->private_host_attrs[count] = class_device_attr_host_##field; \ + if (!i->f->set_host_##field) { \ + i->private_host_attrs[count].attr.mode = S_IRUGO; \ + i->private_host_attrs[count].store = NULL; \ + } \ + i->host_attrs[count] = &i->private_host_attrs[count]; \ + if (i->f->show_host_##field) \ + count++ + +/* The FC Tranport Host Attributes: */ +fc_host_rw_attr(link_down_tmo, "%d\n"); + + + struct scsi_transport_template * fc_attach_transport(struct fc_function_template *ft) { @@ -156,21 +274,43 @@ memset(i, 0, sizeof(struct fc_internal)); - i->t.attrs = &i->attrs[0]; - i->t.class = &fc_transport_class; - i->t.setup = &fc_setup_transport_attrs; - i->t.size = sizeof(struct fc_transport_attrs); + i->t.target_attrs = &i->starget_attrs[0]; + i->t.target_class = &fc_transport_class; + i->t.target_setup = &fc_setup_starget_transport_attrs; + i->t.target_size = sizeof(struct fc_starget_attrs); + + i->t.host_attrs = &i->host_attrs[0]; + i->t.host_class = &fc_host_class; + i->t.host_setup = &fc_setup_host_transport_attrs; + i->t.host_size = sizeof(struct fc_host_attrs); i->f = ft; - SETUP_ATTRIBUTE_RD(port_id); - SETUP_ATTRIBUTE_RD(port_name); - SETUP_ATTRIBUTE_RD(node_name); + + /* + * setup remote port (target) attributes + */ + SETUP_STARGET_ATTRIBUTE_RD(port_id); + SETUP_STARGET_ATTRIBUTE_RD(port_name); + SETUP_STARGET_ATTRIBUTE_RD(node_name); + SETUP_STARGET_ATTRIBUTE_RW(dev_loss_tmo); - BUG_ON(count > FC_NUM_ATTRS); + BUG_ON(count > FC_STARGET_NUM_ATTRS); /* Setup the always-on attributes here */ - i->attrs[count] = NULL; + i->starget_attrs[count] = NULL; + + + /* setup host attributes */ + count=0; + SETUP_HOST_ATTRIBUTE_RW(link_down_tmo); + + BUG_ON(count > FC_HOST_NUM_ATTRS); + + /* Setup the always-on attributes here */ + + i->host_attrs[count] = NULL; + return &i->t; } @@ -184,6 +324,204 @@ } EXPORT_SYMBOL(fc_release_transport); + + +/** + * fc_device_block - called by target functions to block a scsi device + * @dev: scsi device + * @data: unused + **/ +static int fc_device_block(struct device *dev, void *data) +{ + scsi_internal_device_block(to_scsi_device(dev)); + return 0; +} + +/** + * fc_device_unblock - called by target functions to unblock a scsi device + * @dev: scsi device + * @data: unused + **/ +static int fc_device_unblock(struct device *dev, void *data) +{ + scsi_internal_device_unblock(to_scsi_device(dev)); + return 0; +} + +/** + * fc_timeout_blocked_tgt - Timeout handler for blocked scsi targets + * that fail to recover in the alloted time. + * @data: scsi target that failed to reappear in the alloted time. + **/ +static void fc_timeout_blocked_tgt(unsigned long data) +{ + struct scsi_target *starget = (struct scsi_target *)data; + + dev_printk(KERN_ERR, &starget->dev, + "blocked target time out: target resuming\n"); + + /* + * set the device going again ... if the scsi lld didn't + * unblock this device, then IO errors will probably + * result if the host still isn't ready. + */ + device_for_each_child(&starget->dev, NULL, fc_device_unblock); +} + +/** + * fc_target_block - block a target by temporarily putting all its scsi devices + * into the SDEV_BLOCK state. + * @starget: scsi target managed by this fc scsi lldd. + * + * scsi lldd's with a FC transport call this routine to temporarily stop all + * scsi commands to all devices managed by this scsi target. Called + * from interrupt or normal process context. + * + * Returns zero if successful or error if not + * + * Notes: + * The timeout and timer types are extracted from the fc transport + * attributes from the caller's target pointer. This routine assumes no + * locks are held on entry. + **/ +int +fc_target_block(struct scsi_target *starget) +{ + int timeout = fc_starget_dev_loss_tmo(starget); + struct timer_list *timer = &fc_starget_dev_loss_timer(starget); + + if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT) + return -EINVAL; + + device_for_each_child(&starget->dev, NULL, fc_device_block); + + /* The scsi lld blocks this target for the timeout period only. */ + timer->data = (unsigned long)starget; + timer->expires = jiffies + timeout * HZ; + timer->function = fc_timeout_blocked_tgt; + add_timer(timer); + + return 0; +} +EXPORT_SYMBOL(fc_target_block); + +/** + * fc_target_unblock - unblock a target following a fc_target_block request. + * @starget: scsi target managed by this fc scsi lldd. + * + * scsi lld's with a FC transport call this routine to restart IO to all + * devices associated with the caller's scsi target following a fc_target_block + * request. Called from interrupt or normal process context. + * + * Notes: + * This routine assumes no locks are held on entry. + **/ +void +fc_target_unblock(struct scsi_target *starget) +{ + /* + * Stop the target timer first. Take no action on the del_timer + * failure as the state machine state change will validate the + * transaction. + */ + del_timer_sync(&fc_starget_dev_loss_timer(starget)); + + device_for_each_child(&starget->dev, NULL, fc_device_unblock); +} +EXPORT_SYMBOL(fc_target_unblock); + +/** + * fc_timeout_blocked_host - Timeout handler for blocked scsi hosts + * that fail to recover in the alloted time. + * @data: scsi host that failed to recover its devices in the alloted + * time. + **/ +static void fc_timeout_blocked_host(unsigned long data) +{ + struct Scsi_Host *shost = (struct Scsi_Host *)data; + struct scsi_device *sdev; + + dev_printk(KERN_ERR, &shost->shost_gendev, + "blocked host time out: host resuming\n"); + + shost_for_each_device(sdev, shost) { + /* + * set the device going again ... if the scsi lld didn't + * unblock this device, then IO errors will probably + * result if the host still isn't ready. + */ + scsi_internal_device_unblock(sdev); + } +} + +/** + * fc_host_block - block all scsi devices managed by the calling host temporarily + * by putting each device in the SDEV_BLOCK state. + * @shost: scsi host pointer that contains all scsi device siblings. + * + * scsi lld's with a FC transport call this routine to temporarily stop all + * scsi commands to all devices managed by this host. Called + * from interrupt or normal process context. + * + * Returns zero if successful or error if not + * + * Notes: + * The timeout and timer types are extracted from the fc transport + * attributes from the caller's host pointer. This routine assumes no + * locks are held on entry. + **/ +int +fc_host_block(struct Scsi_Host *shost) +{ + struct scsi_device *sdev; + int timeout = fc_host_link_down_tmo(shost); + struct timer_list *timer = &fc_host_link_down_timer(shost); + + if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT) + return -EINVAL; + + shost_for_each_device(sdev, shost) { + scsi_internal_device_block(sdev); + } + + /* The scsi lld blocks this host for the timeout period only. */ + timer->data = (unsigned long)shost; + timer->expires = jiffies + timeout * HZ; + timer->function = fc_timeout_blocked_host; + add_timer(timer); + + return 0; +} +EXPORT_SYMBOL(fc_host_block); + +/** + * fc_host_unblock - unblock all devices managed by this host following a + * fc_host_block request. + * @shost: scsi host containing all scsi device siblings to unblock. + * + * scsi lld's with a FC transport call this routine to restart IO to all scsi + * devices managed by the specified scsi host following an fc_host_block + * request. Called from interrupt or normal process context. + * + * Notes: + * This routine assumes no locks are held on entry. + **/ +void +fc_host_unblock(struct Scsi_Host *shost) +{ + struct scsi_device *sdev; + + /* + * Stop the host timer first. Take no action on the del_timer + * failure as the state machine state change will validate the + * transaction. + */ + del_timer_sync(&fc_host_link_down_timer(shost)); + shost_for_each_device(sdev, shost) { + scsi_internal_device_unblock(sdev); + } +} +EXPORT_SYMBOL(fc_host_unblock); MODULE_AUTHOR("Martin Hicks"); MODULE_DESCRIPTION("FC Transport Attributes"); only in patch2: unchanged: --- a/drivers/scsi/scsi_transport_spi.c 2004-10-06 22:21:18 -07:00 +++ b/drivers/scsi/scsi_transport_spi.c 2004-10-06 22:21:18 -07:00 @@ -27,25 +27,28 @@ #include #include #include +#include "scsi_priv.h" #include #include #include #include #include -#define SPI_PRINTK(x, l, f, a...) printk(l "scsi(%d:%d:%d:%d): " f, (x)->host->host_no, (x)->channel, (x)->id, (x)->lun , ##a) +#define SPI_PRINTK(x, l, f, a...) dev_printk(l, &(x)->dev, f , ##a) static void transport_class_release(struct class_device *class_dev); +static void host_class_release(struct class_device *class_dev); #define SPI_NUM_ATTRS 10 /* increase this if you add attributes */ #define SPI_OTHER_ATTRS 1 /* Increase this if you add "always * on" attributes */ +#define SPI_HOST_ATTRS 1 #define SPI_MAX_ECHO_BUFFER_SIZE 4096 /* Private data accessors (keep these out of the header file) */ -#define spi_dv_pending(x) (((struct spi_transport_attrs *)&(x)->transport_data)->dv_pending) -#define spi_dv_sem(x) (((struct spi_transport_attrs *)&(x)->transport_data)->dv_sem) +#define spi_dv_pending(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_pending) +#define spi_dv_sem(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_sem) struct spi_internal { struct scsi_transport_template t; @@ -55,6 +58,8 @@ /* The array of null terminated pointers to attributes * needed by scsi_sysfs.c */ struct class_device_attribute *attrs[SPI_NUM_ATTRS + SPI_OTHER_ATTRS + 1]; + struct class_device_attribute private_host_attrs[SPI_HOST_ATTRS]; + struct class_device_attribute *host_attrs[SPI_HOST_ATTRS + 1]; }; #define to_spi_internal(tmpl) container_of(tmpl, struct spi_internal, t) @@ -80,43 +85,117 @@ * by 4 */ #define SPI_STATIC_PPR 0x0c +static struct { + enum spi_signal_type value; + char *name; +} signal_types[] = { + { SPI_SIGNAL_UNKNOWN, "unknown" }, + { SPI_SIGNAL_SE, "SE" }, + { SPI_SIGNAL_LVD, "LVD" }, + { SPI_SIGNAL_HVD, "HVD" }, +}; + +static inline const char *spi_signal_to_string(enum spi_signal_type type) +{ + int i; + + for (i = 0; i < sizeof(signal_types)/sizeof(signal_types[0]); i++) { + if (type == signal_types[i].value) + return signal_types[i].name; + } + return NULL; +} +static inline enum spi_signal_type spi_signal_to_value(const char *name) +{ + int i, len; + + for (i = 0; i < sizeof(signal_types)/sizeof(signal_types[0]); i++) { + len = strlen(signal_types[i].name); + if (strncmp(name, signal_types[i].name, len) == 0 && + (name[len] == '\n' || name[len] == '\0')) + return signal_types[i].value; + } + return SPI_SIGNAL_UNKNOWN; +} + + struct class spi_transport_class = { .name = "spi_transport", .release = transport_class_release, }; +struct class spi_host_class = { + .name = "spi_host", + .release = host_class_release, +}; + static __init int spi_transport_init(void) { + int error = class_register(&spi_host_class); + if (error) + return error; return class_register(&spi_transport_class); } static void __exit spi_transport_exit(void) { class_unregister(&spi_transport_class); + class_unregister(&spi_host_class); +} + +static int spi_setup_host_attrs(struct Scsi_Host *shost) +{ + spi_signalling(shost) = SPI_SIGNAL_UNKNOWN; + + return 0; } -static int spi_setup_transport_attrs(struct scsi_device *sdev) +static int spi_configure_device(struct scsi_device *sdev) { - spi_period(sdev) = -1; /* illegal value */ - spi_offset(sdev) = 0; /* async */ - spi_width(sdev) = 0; /* narrow */ - spi_iu(sdev) = 0; /* no IU */ - spi_dt(sdev) = 0; /* ST */ - spi_qas(sdev) = 0; - spi_wr_flow(sdev) = 0; - spi_rd_strm(sdev) = 0; - spi_rti(sdev) = 0; - spi_pcomp_en(sdev) = 0; - spi_dv_pending(sdev) = 0; - init_MUTEX(&spi_dv_sem(sdev)); + struct scsi_target *starget = sdev->sdev_target; + + /* Populate the target capability fields with the values + * gleaned from the device inquiry */ + + spi_support_sync(starget) = scsi_device_sync(sdev); + spi_support_wide(starget) = scsi_device_wide(sdev); + spi_support_dt(starget) = scsi_device_dt(sdev); + spi_support_dt_only(starget) = scsi_device_dt_only(sdev); + spi_support_ius(starget) = scsi_device_ius(sdev); + spi_support_qas(starget) = scsi_device_qas(sdev); + + return 0; +} + +static int spi_setup_transport_attrs(struct scsi_target *starget) +{ + spi_period(starget) = -1; /* illegal value */ + spi_offset(starget) = 0; /* async */ + spi_width(starget) = 0; /* narrow */ + spi_iu(starget) = 0; /* no IU */ + spi_dt(starget) = 0; /* ST */ + spi_qas(starget) = 0; + spi_wr_flow(starget) = 0; + spi_rd_strm(starget) = 0; + spi_rti(starget) = 0; + spi_pcomp_en(starget) = 0; + spi_dv_pending(starget) = 0; + spi_initial_dv(starget) = 0; + init_MUTEX(&spi_dv_sem(starget)); return 0; } static void transport_class_release(struct class_device *class_dev) { - struct scsi_device *sdev = transport_class_to_sdev(class_dev); - put_device(&sdev->sdev_gendev); + struct scsi_target *starget = transport_class_to_starget(class_dev); + put_device(&starget->dev); +} + +static void host_class_release(struct class_device *class_dev) +{ + struct Scsi_Host *shost = transport_class_to_shost(class_dev); + put_device(&shost->shost_gendev); } #define spi_transport_show_function(field, format_string) \ @@ -124,12 +203,13 @@ static ssize_t \ show_spi_transport_##field(struct class_device *cdev, char *buf) \ { \ - struct scsi_device *sdev = transport_class_to_sdev(cdev); \ + struct scsi_target *starget = transport_class_to_starget(cdev); \ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ struct spi_transport_attrs *tp; \ - struct spi_internal *i = to_spi_internal(sdev->host->transportt); \ - tp = (struct spi_transport_attrs *)&sdev->transport_data; \ + struct spi_internal *i = to_spi_internal(shost->transportt); \ + tp = (struct spi_transport_attrs *)&starget->starget_data; \ if (i->f->get_##field) \ - i->f->get_##field(sdev); \ + i->f->get_##field(starget); \ return snprintf(buf, 20, format_string, tp->field); \ } @@ -139,11 +219,12 @@ size_t count) \ { \ int val; \ - struct scsi_device *sdev = transport_class_to_sdev(cdev); \ - struct spi_internal *i = to_spi_internal(sdev->host->transportt); \ + struct scsi_target *starget = transport_class_to_starget(cdev); \ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ + struct spi_internal *i = to_spi_internal(shost->transportt); \ \ val = simple_strtoul(buf, NULL, 0); \ - i->f->set_##field(sdev, val); \ + i->f->set_##field(starget, val); \ return count; \ } @@ -168,8 +249,13 @@ static ssize_t store_spi_revalidate(struct class_device *cdev, const char *buf, size_t count) { - struct scsi_device *sdev = transport_class_to_sdev(cdev); + struct scsi_target *starget = transport_class_to_starget(cdev); + /* FIXME: we're relying on an awful lot of device internals + * here. We really need a function to get the first available + * child */ + struct device *dev = container_of(starget->dev.children.next, struct device, node); + struct scsi_device *sdev = to_scsi_device(dev); spi_dv_device(sdev); return count; } @@ -180,15 +266,16 @@ static ssize_t show_spi_transport_period(struct class_device *cdev, char *buf) { - struct scsi_device *sdev = transport_class_to_sdev(cdev); + struct scsi_target *starget = transport_class_to_starget(cdev); + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); struct spi_transport_attrs *tp; const char *str; - struct spi_internal *i = to_spi_internal(sdev->host->transportt); + struct spi_internal *i = to_spi_internal(shost->transportt); - tp = (struct spi_transport_attrs *)&sdev->transport_data; + tp = (struct spi_transport_attrs *)&starget->starget_data; if (i->f->get_period) - i->f->get_period(sdev); + i->f->get_period(starget); switch(tp->period) { @@ -212,8 +299,9 @@ store_spi_transport_period(struct class_device *cdev, const char *buf, size_t count) { - struct scsi_device *sdev = transport_class_to_sdev(cdev); - struct spi_internal *i = to_spi_internal(sdev->host->transportt); + struct scsi_target *starget = transport_class_to_starget(cdev); + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct spi_internal *i = to_spi_internal(shost->transportt); int j, period = -1; for (j = 0; j < SPI_STATIC_PPR; j++) { @@ -246,7 +334,7 @@ if (period > 0xff) period = 0xff; - i->f->set_period(sdev, period); + i->f->set_period(starget, period); return count; } @@ -255,9 +343,36 @@ show_spi_transport_period, store_spi_transport_period); +static ssize_t show_spi_host_signalling(struct class_device *cdev, char *buf) +{ + struct Scsi_Host *shost = transport_class_to_shost(cdev); + struct spi_internal *i = to_spi_internal(shost->transportt); + + if (i->f->get_signalling) + i->f->get_signalling(shost); + + return sprintf(buf, "%s\n", spi_signal_to_string(spi_signalling(shost))); +} +static ssize_t store_spi_host_signalling(struct class_device *cdev, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = transport_class_to_shost(cdev); + struct spi_internal *i = to_spi_internal(shost->transportt); + enum spi_signal_type type = spi_signal_to_value(buf); + + if (type != SPI_SIGNAL_UNKNOWN) + return count; + + i->f->set_signalling(shost, type); + return count; +} +static CLASS_DEVICE_ATTR(signalling, S_IRUGO | S_IWUSR, + show_spi_host_signalling, + store_spi_host_signalling); + #define DV_SET(x, y) \ if(i->f->set_##x) \ - i->f->set_##x(sdev, y) + i->f->set_##x(sdev->sdev_target, y) #define DV_LOOPS 3 #define DV_TIMEOUT (10*HZ) @@ -325,7 +440,7 @@ DV_TIMEOUT, DV_RETRIES); if(sreq->sr_result || !scsi_device_online(sdev)) { scsi_device_set_state(sdev, SDEV_QUIESCE); - SPI_PRINTK(sdev, KERN_ERR, "Write Buffer failure %x\n", sreq->sr_result); + SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Write Buffer failure %x\n", sreq->sr_result); return 0; } @@ -401,8 +516,8 @@ /* OK, retrain, fallback */ if (i->f->get_period) - i->f->get_period(sdev); - newperiod = spi_period(sdev); + i->f->get_period(sdev->sdev_target); + newperiod = spi_period(sdev->sdev_target); period = newperiod > period ? newperiod : period; if (period < 0x0d) period++; @@ -411,11 +526,11 @@ if (unlikely(period > 0xff || period == prevperiod)) { /* Total failure; set to async and return */ - SPI_PRINTK(sdev, KERN_ERR, "Domain Validation Failure, dropping back to Asynchronous\n"); + SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Domain Validation Failure, dropping back to Asynchronous\n"); DV_SET(offset, 0); return 0; } - SPI_PRINTK(sdev, KERN_ERR, "Domain Validation detected failure, dropping back\n"); + SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Domain Validation detected failure, dropping back\n"); DV_SET(period, period); prevperiod = period; } @@ -486,20 +601,20 @@ DV_SET(width, 0); if (!spi_dv_device_compare_inquiry(sreq, buffer, buffer, DV_LOOPS)) { - SPI_PRINTK(sdev, KERN_ERR, "Domain Validation Initial Inquiry Failed\n"); + SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Domain Validation Initial Inquiry Failed\n"); /* FIXME: should probably offline the device here? */ return; } /* test width */ if (i->f->set_width && sdev->wdtr) { - i->f->set_width(sdev, 1); + i->f->set_width(sdev->sdev_target, 1); if (!spi_dv_device_compare_inquiry(sreq, buffer, buffer + len, DV_LOOPS)) { - SPI_PRINTK(sdev, KERN_ERR, "Wide Transfers Fail\n"); - i->f->set_width(sdev, 0); + SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Wide Transfers Fail\n"); + i->f->set_width(sdev->sdev_target, 0); } } @@ -521,11 +636,11 @@ * test, now try an echo buffer test (if the device allows it) */ if ((len = spi_dv_device_get_echo_buffer(sreq, buffer)) == 0) { - SPI_PRINTK(sdev, KERN_INFO, "Domain Validation skipping write tests\n"); + SPI_PRINTK(sdev->sdev_target, KERN_INFO, "Domain Validation skipping write tests\n"); return; } if (len > SPI_MAX_ECHO_BUFFER_SIZE) { - SPI_PRINTK(sdev, KERN_WARNING, "Echo buffer size %d is too big, trimming to %d\n", len, SPI_MAX_ECHO_BUFFER_SIZE); + SPI_PRINTK(sdev->sdev_target, KERN_WARNING, "Echo buffer size %d is too big, trimming to %d\n", len, SPI_MAX_ECHO_BUFFER_SIZE); len = SPI_MAX_ECHO_BUFFER_SIZE; } @@ -547,6 +662,7 @@ spi_dv_device(struct scsi_device *sdev) { struct scsi_request *sreq = scsi_allocate_request(sdev, GFP_KERNEL); + struct scsi_target *starget = sdev->sdev_target; u8 *buffer; const int len = SPI_MAX_ECHO_BUFFER_SIZE*2; @@ -563,22 +679,28 @@ memset(buffer, 0, len); + /* We need to verify that the actual device will quiesce; the + * later target quiesce is just a nice to have */ if (unlikely(scsi_device_quiesce(sdev))) goto out_free; - spi_dv_pending(sdev) = 1; - down(&spi_dv_sem(sdev)); + scsi_target_quiesce(starget); - SPI_PRINTK(sdev, KERN_INFO, "Beginning Domain Validation\n"); + spi_dv_pending(starget) = 1; + down(&spi_dv_sem(starget)); + + SPI_PRINTK(starget, KERN_INFO, "Beginning Domain Validation\n"); spi_dv_device_internal(sreq, buffer); - SPI_PRINTK(sdev, KERN_INFO, "Ending Domain Validation\n"); + SPI_PRINTK(starget, KERN_INFO, "Ending Domain Validation\n"); + + up(&spi_dv_sem(starget)); + spi_dv_pending(starget) = 0; - up(&spi_dv_sem(sdev)); - spi_dv_pending(sdev) = 0; + scsi_target_resume(starget); - scsi_device_resume(sdev); + spi_initial_dv(starget) = 1; out_free: kfree(buffer); @@ -602,7 +724,7 @@ kfree(wqw); spi_dv_device(sdev); - spi_dv_pending(sdev) = 0; + spi_dv_pending(sdev->sdev_target) = 0; scsi_device_put(sdev); } @@ -625,15 +747,15 @@ if (unlikely(!wqw)) return; - if (unlikely(spi_dv_pending(sdev))) { + if (unlikely(spi_dv_pending(sdev->sdev_target))) { kfree(wqw); return; } /* Set pending early (dv_device doesn't check it, only sets it) */ - spi_dv_pending(sdev) = 1; + spi_dv_pending(sdev->sdev_target) = 1; if (unlikely(scsi_device_get(sdev))) { kfree(wqw); - spi_dv_pending(sdev) = 0; + spi_dv_pending(sdev->sdev_target) = 0; return; } @@ -654,6 +776,15 @@ if (i->f->show_##field) \ count++ +#define SETUP_HOST_ATTRIBUTE(field) \ + i->private_host_attrs[count] = class_device_attr_##field; \ + if (!i->f->set_##field) { \ + i->private_host_attrs[count].attr.mode = S_IRUGO; \ + i->private_host_attrs[count].store = NULL; \ + } \ + i->host_attrs[count] = &i->private_host_attrs[count]; \ + count++ + struct scsi_transport_template * spi_attach_transport(struct spi_function_template *ft) { @@ -666,10 +797,15 @@ memset(i, 0, sizeof(struct spi_internal)); - i->t.attrs = &i->attrs[0]; - i->t.class = &spi_transport_class; - i->t.setup = &spi_setup_transport_attrs; - i->t.size = sizeof(struct spi_transport_attrs); + i->t.target_attrs = &i->attrs[0]; + i->t.target_class = &spi_transport_class; + i->t.target_setup = &spi_setup_transport_attrs; + i->t.device_configure = &spi_configure_device; + i->t.target_size = sizeof(struct spi_transport_attrs); + i->t.host_attrs = &i->host_attrs[0]; + i->t.host_class = &spi_host_class; + i->t.host_setup = &spi_setup_host_attrs; + i->t.host_size = sizeof(struct spi_host_attrs); i->f = ft; SETUP_ATTRIBUTE(period); @@ -690,6 +826,13 @@ i->attrs[count++] = &class_device_attr_revalidate; i->attrs[count] = NULL; + + count = 0; + SETUP_HOST_ATTRIBUTE(signalling); + + BUG_ON(count > SPI_HOST_ATTRS); + + i->host_attrs[count] = NULL; return &i->t; } only in patch2: unchanged: --- a/drivers/scsi/sim710.c 2004-10-06 22:21:18 -07:00 +++ b/drivers/scsi/sim710.c 2004-10-06 22:21:18 -07:00 @@ -36,6 +36,9 @@ #include #include #include +#include +#include +#include #include "53c700.h" only in patch2: unchanged: --- a/drivers/scsi/zalon.c 2004-10-06 22:21:18 -07:00 +++ b/drivers/scsi/zalon.c 2004-10-06 22:21:18 -07:00 @@ -190,7 +190,12 @@ static int __init zalon7xx_init(void) { - return register_parisc_driver(&zalon_driver); + int ret = ncr53c8xx_init(); + if (!ret) + ret = register_parisc_driver(&zalon_driver); + if (ret) + ncr53c8xx_exit(); + return ret; } static void __exit zalon7xx_exit(void) only in patch2: unchanged: --- a/include/scsi/scsi_transport.h 2004-10-06 22:21:18 -07:00 +++ b/include/scsi/scsi_transport.h 2004-10-06 22:21:18 -07:00 @@ -24,18 +24,28 @@ /* The NULL terminated list of transport attributes * that should be exported. */ - struct class_device_attribute **attrs; + struct class_device_attribute **device_attrs; + struct class_device_attribute **target_attrs; + struct class_device_attribute **host_attrs; + /* The transport class that the device is in */ - struct class *class; + struct class *device_class; + struct class *target_class; + struct class *host_class; + + /* Constructor functions */ + int (*device_setup)(struct scsi_device *); + int (*device_configure)(struct scsi_device *); + int (*target_setup)(struct scsi_target *); + int (*host_setup)(struct Scsi_Host *); - /* Constructor/Destructor functions */ - int (* setup)(struct scsi_device *); - void (* cleanup)(struct scsi_device *); /* The size of the specific transport attribute structure (a * space of this size will be left at the end of the - * scsi_device structure */ - int size; + * scsi_* structure */ + int device_size; + int target_size; + int host_size; }; #endif /* SCSI_TRANSPORT_H */ only in patch2: unchanged: --- a/include/scsi/scsi_transport_fc.h 2004-10-06 22:21:18 -07:00 +++ b/include/scsi/scsi_transport_fc.h 2004-10-06 22:21:18 -07:00 @@ -24,33 +24,68 @@ struct scsi_transport_template; -struct fc_transport_attrs { +struct fc_starget_attrs { /* aka fc_target_attrs */ int port_id; uint64_t node_name; uint64_t port_name; + uint32_t dev_loss_tmo; /* Remote Port loss timeout in seconds. */ + struct timer_list dev_loss_timer; }; -/* accessor functions */ -#define fc_port_id(x) (((struct fc_transport_attrs *)&(x)->transport_data)->port_id) -#define fc_node_name(x) (((struct fc_transport_attrs *)&(x)->transport_data)->node_name) -#define fc_port_name(x) (((struct fc_transport_attrs *)&(x)->transport_data)->port_name) +#define fc_starget_port_id(x) \ + (((struct fc_starget_attrs *)&(x)->starget_data)->port_id) +#define fc_starget_node_name(x) \ + (((struct fc_starget_attrs *)&(x)->starget_data)->node_name) +#define fc_starget_port_name(x) \ + (((struct fc_starget_attrs *)&(x)->starget_data)->port_name) +#define fc_starget_dev_loss_tmo(x) \ + (((struct fc_starget_attrs *)&(x)->starget_data)->dev_loss_tmo) +#define fc_starget_dev_loss_timer(x) \ + (((struct fc_starget_attrs *)&(x)->starget_data)->dev_loss_timer) + +struct fc_host_attrs { + uint32_t link_down_tmo; /* Link Down timeout in seconds. */ + struct timer_list link_down_timer; +}; + +#define fc_host_link_down_tmo(x) \ + (((struct fc_host_attrs *)&(x)->shost_data)->link_down_tmo) +#define fc_host_link_down_timer(x) \ + (((struct fc_host_attrs *)&(x)->shost_data)->link_down_timer) + /* The functions by which the transport class and the driver communicate */ struct fc_function_template { - void (*get_port_id)(struct scsi_device *); - void (*get_node_name)(struct scsi_device *); - void (*get_port_name)(struct scsi_device *); - /* The driver sets these to tell the transport class it + void (*get_starget_port_id)(struct scsi_target *); + void (*get_starget_node_name)(struct scsi_target *); + void (*get_starget_port_name)(struct scsi_target *); + void (*get_starget_dev_loss_tmo)(struct scsi_target *); + void (*set_starget_dev_loss_tmo)(struct scsi_target *, uint32_t); + + void (*get_host_link_down_tmo)(struct Scsi_Host *); + void (*set_host_link_down_tmo)(struct Scsi_Host *, uint32_t); + + /* + * The driver sets these to tell the transport class it * wants the attributes displayed in sysfs. If the show_ flag * is not set, the attribute will be private to the transport - * class */ - unsigned long show_port_id:1; - unsigned long show_node_name:1; - unsigned long show_port_name:1; + * class + */ + unsigned long show_starget_port_id:1; + unsigned long show_starget_node_name:1; + unsigned long show_starget_port_name:1; + unsigned long show_starget_dev_loss_tmo:1; + + unsigned long show_host_link_down_tmo:1; + /* Private Attributes */ }; struct scsi_transport_template *fc_attach_transport(struct fc_function_template *); void fc_release_transport(struct scsi_transport_template *); +int fc_target_block(struct scsi_target *starget); +void fc_target_unblock(struct scsi_target *starget); +int fc_host_block(struct Scsi_Host *shost); +void fc_host_unblock(struct Scsi_Host *shost); #endif /* SCSI_TRANSPORT_FC_H */ only in patch2: unchanged: --- a/include/scsi/scsi_transport_spi.h 2004-10-06 22:21:18 -07:00 +++ b/include/scsi/scsi_transport_spi.h 2004-10-06 22:21:18 -07:00 @@ -35,45 +35,80 @@ unsigned int rd_strm:1; /* Read streaming enabled */ unsigned int rti:1; /* Retain Training Information */ unsigned int pcomp_en:1;/* Precompensation enabled */ + unsigned int initial_dv:1; /* DV done to this target yet */ + unsigned long flags; /* flags field for drivers to use */ + /* Device Properties fields */ + unsigned int support_sync:1; /* synchronous support */ + unsigned int support_wide:1; /* wide support */ + unsigned int support_dt:1; /* allows DT phases */ + unsigned int support_dt_only; /* disallows ST phases */ + unsigned int support_ius; /* support Information Units */ + unsigned int support_qas; /* supports quick arbitration and selection */ /* Private Fields */ unsigned int dv_pending:1; /* Internal flag */ struct semaphore dv_sem; /* semaphore to serialise dv */ }; +enum spi_signal_type { + SPI_SIGNAL_UNKNOWN = 1, + SPI_SIGNAL_SE, + SPI_SIGNAL_LVD, + SPI_SIGNAL_HVD, +}; + +struct spi_host_attrs { + enum spi_signal_type signalling; +}; + /* accessor functions */ -#define spi_period(x) (((struct spi_transport_attrs *)&(x)->transport_data)->period) -#define spi_offset(x) (((struct spi_transport_attrs *)&(x)->transport_data)->offset) -#define spi_width(x) (((struct spi_transport_attrs *)&(x)->transport_data)->width) -#define spi_iu(x) (((struct spi_transport_attrs *)&(x)->transport_data)->iu) -#define spi_dt(x) (((struct spi_transport_attrs *)&(x)->transport_data)->dt) -#define spi_qas(x) (((struct spi_transport_attrs *)&(x)->transport_data)->qas) -#define spi_wr_flow(x) (((struct spi_transport_attrs *)&(x)->transport_data)->wr_flow) -#define spi_rd_strm(x) (((struct spi_transport_attrs *)&(x)->transport_data)->rd_strm) -#define spi_rti(x) (((struct spi_transport_attrs *)&(x)->transport_data)->rti) -#define spi_pcomp_en(x) (((struct spi_transport_attrs *)&(x)->transport_data)->pcomp_en) +#define spi_period(x) (((struct spi_transport_attrs *)&(x)->starget_data)->period) +#define spi_offset(x) (((struct spi_transport_attrs *)&(x)->starget_data)->offset) +#define spi_width(x) (((struct spi_transport_attrs *)&(x)->starget_data)->width) +#define spi_iu(x) (((struct spi_transport_attrs *)&(x)->starget_data)->iu) +#define spi_dt(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dt) +#define spi_qas(x) (((struct spi_transport_attrs *)&(x)->starget_data)->qas) +#define spi_wr_flow(x) (((struct spi_transport_attrs *)&(x)->starget_data)->wr_flow) +#define spi_rd_strm(x) (((struct spi_transport_attrs *)&(x)->starget_data)->rd_strm) +#define spi_rti(x) (((struct spi_transport_attrs *)&(x)->starget_data)->rti) +#define spi_pcomp_en(x) (((struct spi_transport_attrs *)&(x)->starget_data)->pcomp_en) +#define spi_initial_dv(x) (((struct spi_transport_attrs *)&(x)->starget_data)->initial_dv) + +#define spi_support_sync(x) (((struct spi_transport_attrs *)&(x)->starget_data)->support_sync) +#define spi_support_wide(x) (((struct spi_transport_attrs *)&(x)->starget_data)->support_wide) +#define spi_support_dt(x) (((struct spi_transport_attrs *)&(x)->starget_data)->support_dt) +#define spi_support_dt_only(x) (((struct spi_transport_attrs *)&(x)->starget_data)->support_dt_only) +#define spi_support_ius(x) (((struct spi_transport_attrs *)&(x)->starget_data)->support_ius) +#define spi_support_qas(x) (((struct spi_transport_attrs *)&(x)->starget_data)->support_qas) + +#define spi_flags(x) (((struct spi_transport_attrs *)&(x)->starget_data)->flags) +#define spi_signalling(h) (((struct spi_host_attrs *)&(h)->shost_data)->signalling) + + /* The functions by which the transport class and the driver communicate */ struct spi_function_template { - void (*get_period)(struct scsi_device *); - void (*set_period)(struct scsi_device *, int); - void (*get_offset)(struct scsi_device *); - void (*set_offset)(struct scsi_device *, int); - void (*get_width)(struct scsi_device *); - void (*set_width)(struct scsi_device *, int); - void (*get_iu)(struct scsi_device *); - void (*set_iu)(struct scsi_device *, int); - void (*get_dt)(struct scsi_device *); - void (*set_dt)(struct scsi_device *, int); - void (*get_qas)(struct scsi_device *); - void (*set_qas)(struct scsi_device *, int); - void (*get_wr_flow)(struct scsi_device *); - void (*set_wr_flow)(struct scsi_device *, int); - void (*get_rd_strm)(struct scsi_device *); - void (*set_rd_strm)(struct scsi_device *, int); - void (*get_rti)(struct scsi_device *); - void (*set_rti)(struct scsi_device *, int); - void (*get_pcomp_en)(struct scsi_device *); - void (*set_pcomp_en)(struct scsi_device *, int); + void (*get_period)(struct scsi_target *); + void (*set_period)(struct scsi_target *, int); + void (*get_offset)(struct scsi_target *); + void (*set_offset)(struct scsi_target *, int); + void (*get_width)(struct scsi_target *); + void (*set_width)(struct scsi_target *, int); + void (*get_iu)(struct scsi_target *); + void (*set_iu)(struct scsi_target *, int); + void (*get_dt)(struct scsi_target *); + void (*set_dt)(struct scsi_target *, int); + void (*get_qas)(struct scsi_target *); + void (*set_qas)(struct scsi_target *, int); + void (*get_wr_flow)(struct scsi_target *); + void (*set_wr_flow)(struct scsi_target *, int); + void (*get_rd_strm)(struct scsi_target *); + void (*set_rd_strm)(struct scsi_target *, int); + void (*get_rti)(struct scsi_target *); + void (*set_rti)(struct scsi_target *, int); + void (*get_pcomp_en)(struct scsi_target *); + void (*set_pcomp_en)(struct scsi_target *, int); + void (*get_signalling)(struct Scsi_Host *); + void (*set_signalling)(struct Scsi_Host *, enum spi_signal_type); /* The driver sets these to tell the transport class it * wants the attributes displayed in sysfs. If the show_ flag * is not set, the attribute will be private to the transport