Commit c7572b86 authored by James Bottomley's avatar James Bottomley Committed by James Bottomley

Add SCSI transport attributes

From: 	Martin Hicks <mort@wildopensource.com>

Transport attributes are classes which can be
attached to by a scsi driver to export (and
later control) transport based properties.
parent b362c0a1
......@@ -196,6 +196,25 @@ config SCSI_LOGGING
there should be no noticeable performance impact as long as you have
logging turned off.
menu "SCSI Transport Attributes"
depends on SCSI
config SCSI_SPI_ATTRS
tristate "Parallel SCSI (SPI) Transport Attributes"
depends on SCSI
help
If you wish to export transport-specific information about
each attached SCSI device to sysfs, say Y. Otherwise, say N.
config SCSI_FC_ATTRS
tristate "FiberChannel Transport Attributes"
depends on SCSI
help
If you wish to export transport-specific information about
each attached FiberChannel device to sysfs, say Y.
Otherwise, say N.
endmenu
menu "SCSI low-level drivers"
depends on SCSI!=n
......
......@@ -22,6 +22,14 @@ subdir-$(CONFIG_PCMCIA) += pcmcia
obj-$(CONFIG_SCSI) += scsi_mod.o
# --- NOTE ORDERING HERE ---
# For kernel non-modular link, transport attributes need to
# be initialised before drivers
# --------------------------
obj-$(CONFIG_SCSI_SPI_ATTRS) += scsi_transport_spi.o
obj-$(CONFIG_SCSI_FC_ATTRS) += scsi_transport_fc.o
obj-$(CONFIG_SCSI_AMIGA7XX) += amiga7xx.o 53c7xx.o
obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o
obj-$(CONFIG_A2091_SCSI) += a2091.o wd33c93.o
......
......@@ -32,6 +32,7 @@
#include <linux/unistd.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
#include "scsi.h"
#include "scsi_priv.h"
......@@ -222,6 +223,11 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
shost->max_id = 8;
shost->max_lun = 8;
/* Give each shost a default transportt if the driver
* doesn't yet support Transport Attributes */
if (!shost->transportt)
shost->transportt = &blank_transport_template;
/*
* All drivers right now should be able to handle 12 byte
* commands. Every so often there are requests for 16 byte
......
......@@ -156,6 +156,7 @@ extern int scsi_sysfs_add_sdev(struct scsi_device *);
extern int scsi_sysfs_add_host(struct Scsi_Host *);
extern int scsi_sysfs_register(void);
extern void scsi_sysfs_unregister(void);
extern struct scsi_transport_template blank_transport_template;
extern struct class sdev_class;
extern struct bus_type scsi_bus_type;
......
......@@ -35,6 +35,7 @@
#include <scsi/scsi_driver.h>
#include <scsi/scsi_devinfo.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
#include "scsi.h"
#include "scsi_priv.h"
......@@ -192,7 +193,7 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
struct scsi_device *sdev, *device;
unsigned long flags;
sdev = kmalloc(sizeof(*sdev), GFP_ATOMIC);
sdev = kmalloc(sizeof(*sdev) + shost->transportt->size, GFP_ATOMIC);
if (!sdev)
goto out;
......@@ -237,6 +238,11 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
goto out_free_queue;
}
if (shost->transportt->setup) {
if (shost->transportt->setup(sdev))
goto out_cleanup_slave;
}
if (get_device(&sdev->host->shost_gendev)) {
device_initialize(&sdev->sdev_gendev);
......@@ -253,8 +259,15 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
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_slave;
goto out_cleanup_transport;
/*
* If there are any same target siblings, add this to the
......@@ -283,6 +296,9 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
spin_unlock_irqrestore(shost->host_lock, flags);
return sdev;
out_cleanup_transport:
if (shost->transportt->cleanup)
shost->transportt->cleanup(sdev);
out_cleanup_slave:
if (shost->hostt->slave_destroy)
shost->hostt->slave_destroy(sdev);
......@@ -744,6 +760,8 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
} 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:
......@@ -1300,5 +1318,7 @@ void scsi_free_host_dev(struct scsi_device *sdev)
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);
}
......@@ -13,6 +13,7 @@
#include <linux/device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
#include "scsi.h"
#include "scsi_priv.h"
......@@ -349,6 +350,7 @@ static int attr_add(struct device *dev, struct device_attribute *attr)
**/
int scsi_sysfs_add_sdev(struct scsi_device *sdev)
{
struct class_device_attribute **attrs;
int error = -EINVAL, i;
if (sdev->sdev_state != SDEV_CREATED)
......@@ -368,6 +370,12 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
goto clean_device;
}
if (sdev->transport_classdev.class) {
error = class_device_add(&sdev->transport_classdev);
if (error)
goto clean_device2;
}
get_device(&sdev->sdev_gendev);
if (sdev->host->hostt->sdev_attrs) {
......@@ -393,10 +401,24 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
}
}
if (sdev->transport_classdev.class) {
attrs = sdev->host->transportt->attrs;
for (i = 0; attrs[i]; i++) {
error = class_device_create_file(&sdev->transport_classdev,
attrs[i]);
if (error) {
scsi_remove_device(sdev);
goto out;
}
}
}
out:
return error;
clean_device:
clean_device2:
class_device_del(&sdev->sdev_classdev);
clean_device:
sdev->sdev_state = SDEV_CANCEL;
device_del(&sdev->sdev_gendev);
......@@ -414,9 +436,12 @@ void scsi_remove_device(struct scsi_device *sdev)
if (sdev->sdev_state == SDEV_RUNNING || sdev->sdev_state == SDEV_CANCEL) {
sdev->sdev_state = SDEV_DEL;
class_device_unregister(&sdev->sdev_classdev);
class_device_unregister(&sdev->transport_classdev);
device_del(&sdev->sdev_gendev);
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);
}
}
......@@ -503,3 +528,7 @@ int scsi_sysfs_add_host(struct Scsi_Host *shost)
return 0;
}
/* A blank transport template that is used in drivers that don't
* yet implement Transport Attributes */
struct scsi_transport_template blank_transport_template = { 0, };
/*
* FiberChannel transport specific attributes exported to sysfs.
*
* Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_fc.h>
static void transport_class_release(struct class_device *class_dev);
struct class fc_transport_class = {
.name = "fc_transport",
.release = transport_class_release,
};
static __init int fc_transport_init(void)
{
return class_register(&fc_transport_class);
}
static void __exit fc_transport_exit(void)
{
class_unregister(&fc_transport_class);
}
static int fc_setup_transport_attrs(struct scsi_device *sdev)
{
/* FIXME: Callback into the driver */
fc_node_name(sdev) = -1;
fc_port_name(sdev) = -1;
fc_port_id(sdev) = -1;
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);
}
#define fc_transport_show_function(field, format_string, cast) \
static ssize_t \
show_fc_transport_##field (struct class_device *cdev, char *buf) \
{ \
struct scsi_device *sdev = transport_class_to_sdev(cdev); \
struct fc_transport_attrs *tp; \
tp = (struct fc_transport_attrs *)&sdev->transport_data; \
return snprintf(buf, 20, format_string, cast tp->field); \
}
#define fc_transport_rd_attr(field, format_string) \
fc_transport_show_function(field, format_string, ) \
static CLASS_DEVICE_ATTR( field, S_IRUGO, show_fc_transport_##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)
/* 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");
struct class_device_attribute *fc_transport_attrs[] = {
&class_device_attr_node_name,
&class_device_attr_port_name,
&class_device_attr_port_id,
NULL
};
struct scsi_transport_template fc_transport_template = {
.attrs = fc_transport_attrs,
.class = &fc_transport_class,
.setup = &fc_setup_transport_attrs,
.cleanup = NULL,
.size = sizeof(struct fc_transport_attrs) - sizeof(unsigned long),
};
EXPORT_SYMBOL(fc_transport_template);
MODULE_AUTHOR("Martin Hicks");
MODULE_DESCRIPTION("FC Transport Attributes");
MODULE_LICENSE("GPL");
module_init(fc_transport_init);
module_exit(fc_transport_exit);
/*
* Parallel SCSI (SPI) transport specific attributes exported to sysfs.
*
* Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_spi.h>
static void transport_class_release(struct class_device *class_dev);
struct class spi_transport_class = {
.name = "spi_transport",
.release = transport_class_release,
};
static __init int spi_transport_init(void)
{
return class_register(&spi_transport_class);
}
static void __exit spi_transport_exit(void)
{
class_unregister(&spi_transport_class);
}
static int spi_setup_transport_attrs(struct scsi_device *sdev)
{
/* FIXME: should callback into the driver to get these values */
spi_period(sdev) = -1;
spi_offset(sdev) = -1;
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);
}
#define spi_transport_show_function(field, format_string) \
static ssize_t \
show_spi_transport_##field (struct class_device *cdev, char *buf) \
{ \
struct scsi_device *sdev = transport_class_to_sdev(cdev); \
struct spi_transport_attrs *tp; \
tp = (struct spi_transport_attrs *)&sdev->transport_data; \
return snprintf(buf, 20, format_string, tp->field); \
}
#define spi_transport_rd_attr(field, format_string) \
spi_transport_show_function(field, format_string) \
static CLASS_DEVICE_ATTR( field, S_IRUGO, show_spi_transport_##field, NULL)
/* The Parallel SCSI Tranport Attributes: */
spi_transport_rd_attr(period, "%d\n");
spi_transport_rd_attr(offset, "%d\n");
struct class_device_attribute *spi_transport_attrs[] = {
&class_device_attr_period,
&class_device_attr_offset,
NULL
};
struct scsi_transport_template spi_transport_template = {
.attrs = spi_transport_attrs,
.class = &spi_transport_class,
.setup = &spi_setup_transport_attrs,
.cleanup = NULL,
.size = sizeof(struct spi_transport_attrs) - sizeof(unsigned long),
};
EXPORT_SYMBOL(spi_transport_template);
MODULE_AUTHOR("Martin Hicks");
MODULE_DESCRIPTION("SPI Transport Attributes");
MODULE_LICENSE("GPL");
module_init(spi_transport_init);
module_exit(spi_transport_exit);
......@@ -104,12 +104,17 @@ struct scsi_device {
struct device sdev_gendev;
struct class_device sdev_classdev;
struct class_device transport_classdev;
enum scsi_device_state sdev_state;
};
unsigned long transport_data[0];
} __attribute__((aligned(sizeof(unsigned long))));
#define to_scsi_device(d) \
container_of(d, struct scsi_device, sdev_gendev)
#define class_to_sdev(d) \
container_of(d, struct scsi_device, sdev_classdev)
#define transport_class_to_sdev(class_dev) \
container_of(class_dev, struct scsi_device, transport_classdev)
extern struct scsi_device *scsi_add_device(struct Scsi_Host *,
uint, uint, uint);
......
......@@ -11,6 +11,7 @@ struct scsi_cmnd;
struct scsi_device;
struct Scsi_Host;
struct scsi_host_cmd_pool;
struct scsi_transport_template;
/*
......@@ -395,6 +396,7 @@ struct Scsi_Host {
unsigned int eh_kill:1; /* set when killing the eh thread */
wait_queue_head_t host_wait;
struct scsi_host_template *hostt;
struct scsi_transport_template *transportt;
volatile unsigned short host_busy; /* commands actually active on low-level */
volatile unsigned short host_failed; /* commands that failed. */
......
/*
* Transport specific attributes.
*
* Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef SCSI_TRANSPORT_H
#define SCSI_TRANSPORT_H
struct scsi_transport_template {
/* The NULL terminated list of transport attributes
* that should be exported.
*/
struct class_device_attribute **attrs;
/* The transport class that the device is in */
struct class *class;
/* 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;
};
#endif /* SCSI_TRANSPORT_H */
/*
* FiberChannel transport specific attributes exported to sysfs.
*
* Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef SCSI_TRANSPORT_FC_H
#define SCSI_TRANSPORT_FC_H
struct scsi_transport_template;
struct fc_transport_attrs {
int port_id;
uint64_t node_name;
uint64_t port_name;
};
/* 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)
extern struct scsi_transport_template fc_transport_template;
#endif /* SCSI_TRANSPORT_FC_H */
/*
* Parallel SCSI (SPI) transport specific attributes exported to sysfs.
*
* Copyright (c) 2003 Silicon Graphics, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef SCSI_TRANSPORT_SPI_H
#define SCSI_TRANSPORT_SPI_H
#include <linux/config.h>
struct scsi_transport_template;
struct spi_transport_attrs {
int period;
int offset;
};
/* 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)
extern struct scsi_transport_template spi_transport_template;
#endif /* SCSI_TRANSPORT_SPI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment