*/
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/sched.h> /* workqueue stuff, HZ */
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
{ FC_PORTSPEED_2GBIT, "2 Gbit" },
{ FC_PORTSPEED_4GBIT, "4 Gbit" },
{ FC_PORTSPEED_10GBIT, "10 Gbit" },
+ { FC_PORTSPEED_8GBIT, "8 Gbit" },
+ { FC_PORTSPEED_16GBIT, "16 Gbit" },
{ FC_PORTSPEED_NOT_NEGOTIATED, "Not Negotiated" },
};
fc_bitfield_name_search(port_speed, fc_port_speed_names)
#define FC_MGMTSRVR_PORTID 0x00000a
-static void fc_timeout_deleted_rport(void *data);
-static void fc_scsi_scan_rport(void *data);
+static void fc_timeout_deleted_rport(struct work_struct *work);
+static void fc_timeout_fail_rport_io(struct work_struct *work);
+static void fc_scsi_scan_rport(struct work_struct *work);
/*
* Attribute counts pre object type...
* Increase these values if you add attributes
*/
#define FC_STARGET_NUM_ATTRS 3
-#define FC_RPORT_NUM_ATTRS 9
+#define FC_RPORT_NUM_ATTRS 10
#define FC_HOST_NUM_ATTRS 17
struct fc_internal {
event->event_code = event_code;
event->event_data = event_data;
- err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS);
+ err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
+ GFP_KERNEL);
if (err && (err != -ESRCH)) /* filter no recipient errors */
/* nlmsg_multicast already kfree_skb'd */
goto send_fail;
event->event_code = FCH_EVT_VENDOR_UNIQUE;
memcpy(&event->event_data, data_buf, data_len);
- err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS);
+ err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
+ GFP_KERNEL);
if (err && (err != -ESRCH)) /* filter no recipient errors */
/* nlmsg_multicast already kfree_skb'd */
goto send_vendor_fail;
struct fc_rport *rport = transport_class_to_rport(cdev); \
struct Scsi_Host *shost = rport_to_shost(rport); \
struct fc_internal *i = to_fc_internal(shost->transportt); \
+ char *cp; \
if ((rport->port_state == FC_PORTSTATE_BLOCKED) || \
(rport->port_state == FC_PORTSTATE_DELETED) || \
(rport->port_state == FC_PORTSTATE_NOTPRESENT)) \
return -EBUSY; \
- val = simple_strtoul(buf, NULL, 0); \
+ val = simple_strtoul(buf, &cp, 0); \
+ if (*cp && (*cp != '\n')) \
+ return -EINVAL; \
i->f->set_rport_##field(rport, val); \
return count; \
}
if (i->f->show_rport_##field) \
count++
+#define SETUP_PRIVATE_RPORT_ATTRIBUTE_RW(field) \
+{ \
+ i->private_rport_attrs[count] = class_device_attr_rport_##field; \
+ i->rport_attrs[count] = &i->private_rport_attrs[count]; \
+ count++; \
+}
+
/* The FC Transport Remote Port Attributes: */
struct fc_rport *rport = transport_class_to_rport(cdev);
struct Scsi_Host *shost = rport_to_shost(rport);
struct fc_internal *i = to_fc_internal(shost->transportt);
+ char *cp;
if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||
(rport->port_state == FC_PORTSTATE_DELETED) ||
(rport->port_state == FC_PORTSTATE_NOTPRESENT))
return -EBUSY;
- val = simple_strtoul(buf, NULL, 0);
- if ((val < 0) || (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT))
+ val = simple_strtoul(buf, &cp, 0);
+ if ((*cp && (*cp != '\n')) ||
+ (val < 0) || (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT))
return -EINVAL;
i->f->set_rport_dev_loss_tmo(rport, val);
return count;
fc_private_rport_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN);
fc_private_rport_rd_attr(scsi_target_id, "%d\n", 20);
+/*
+ * fast_io_fail_tmo attribute
+ */
+static ssize_t
+show_fc_rport_fast_io_fail_tmo (struct class_device *cdev, char *buf)
+{
+ struct fc_rport *rport = transport_class_to_rport(cdev);
+
+ if (rport->fast_io_fail_tmo == -1)
+ return snprintf(buf, 5, "off\n");
+ return snprintf(buf, 20, "%d\n", rport->fast_io_fail_tmo);
+}
+
+static ssize_t
+store_fc_rport_fast_io_fail_tmo(struct class_device *cdev, const char *buf,
+ size_t count)
+{
+ int val;
+ char *cp;
+ struct fc_rport *rport = transport_class_to_rport(cdev);
+
+ if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||
+ (rport->port_state == FC_PORTSTATE_DELETED) ||
+ (rport->port_state == FC_PORTSTATE_NOTPRESENT))
+ return -EBUSY;
+ if (strncmp(buf, "off", 3) == 0)
+ rport->fast_io_fail_tmo = -1;
+ else {
+ val = simple_strtoul(buf, &cp, 0);
+ if ((*cp && (*cp != '\n')) ||
+ (val < 0) || (val >= rport->dev_loss_tmo))
+ return -EINVAL;
+ rport->fast_io_fail_tmo = val;
+ }
+ return count;
+}
+static FC_CLASS_DEVICE_ATTR(rport, fast_io_fail_tmo, S_IRUGO | S_IWUSR,
+ show_fc_rport_fast_io_fail_tmo, store_fc_rport_fast_io_fail_tmo);
/*
/*
* Note: in the target show function we recognize when the remote
- * port is in the heirarchy and do not allow the driver to get
+ * port is in the hierarchy and do not allow the driver to get
* involved in sysfs functions. The driver only gets involved if
* it's the "old" style that doesn't use rports.
*/
int val; \
struct Scsi_Host *shost = transport_class_to_shost(cdev); \
struct fc_internal *i = to_fc_internal(shost->transportt); \
+ char *cp; \
\
- val = simple_strtoul(buf, NULL, 0); \
+ val = simple_strtoul(buf, &cp, 0); \
+ if (*cp && (*cp != '\n')) \
+ return -EINVAL; \
i->f->set_host_##field(shost, val); \
return count; \
}
SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(roles);
SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(port_state);
SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(scsi_target_id);
+ if (ft->terminate_rport_io)
+ SETUP_PRIVATE_RPORT_ATTRIBUTE_RW(fast_io_fail_tmo);
BUG_ON(count > FC_RPORT_NUM_ATTRS);
* @delay: jiffies to delay the work queuing
*
* Return value:
- * 0 on success / != 0 for error
+ * 1 on success / 0 already queued / < 0 for error
**/
static int
-fc_queue_devloss_work(struct Scsi_Host *shost, struct work_struct *work,
+fc_queue_devloss_work(struct Scsi_Host *shost, struct delayed_work *work,
unsigned long delay)
{
if (unlikely(!fc_host_devloss_work_q(shost))) {
* fc_starget_delete - called to delete the scsi decendents of an rport
* (target and all sdevs)
*
- * @data: remote port to be operated on.
+ * @work: remote port to be operated on.
**/
static void
-fc_starget_delete(void *data)
+fc_starget_delete(struct work_struct *work)
{
- struct fc_rport *rport = (struct fc_rport *)data;
+ struct fc_rport *rport =
+ container_of(work, struct fc_rport, stgt_delete_work);
struct Scsi_Host *shost = rport_to_shost(rport);
unsigned long flags;
+ struct fc_internal *i = to_fc_internal(shost->transportt);
+
+ /*
+ * Involve the LLDD if possible. All io on the rport is to
+ * be terminated, either as part of the dev_loss_tmo callback
+ * processing, or via the terminate_rport_io function.
+ */
+ if (i->f->dev_loss_tmo_callbk)
+ i->f->dev_loss_tmo_callbk(rport);
+ else if (i->f->terminate_rport_io)
+ i->f->terminate_rport_io(rport);
spin_lock_irqsave(shost->host_lock, flags);
if (rport->flags & FC_RPORT_DEVLOSS_PENDING) {
spin_unlock_irqrestore(shost->host_lock, flags);
+ if (!cancel_delayed_work(&rport->fail_io_work))
+ fc_flush_devloss(shost);
if (!cancel_delayed_work(&rport->dev_loss_work))
fc_flush_devloss(shost);
spin_lock_irqsave(shost->host_lock, flags);
/**
* fc_rport_final_delete - finish rport termination and delete it.
*
- * @data: remote port to be deleted.
+ * @work: remote port to be deleted.
**/
static void
-fc_rport_final_delete(void *data)
+fc_rport_final_delete(struct work_struct *work)
{
- struct fc_rport *rport = (struct fc_rport *)data;
+ struct fc_rport *rport =
+ container_of(work, struct fc_rport, rport_delete_work);
struct device *dev = &rport->dev;
struct Scsi_Host *shost = rport_to_shost(rport);
-
- /* Delete SCSI target and sdevs */
- if (rport->scsi_target_id != -1)
- fc_starget_delete(data);
+ struct fc_internal *i = to_fc_internal(shost->transportt);
/*
* if a scan is pending, flush the SCSI Host work_q so that
if (rport->flags & FC_RPORT_SCAN_PENDING)
scsi_flush_work(shost);
+ /* Delete SCSI target and sdevs */
+ if (rport->scsi_target_id != -1)
+ fc_starget_delete(&rport->stgt_delete_work);
+ else if (i->f->dev_loss_tmo_callbk)
+ i->f->dev_loss_tmo_callbk(rport);
+ else if (i->f->terminate_rport_io)
+ i->f->terminate_rport_io(rport);
+
transport_remove_device(dev);
device_del(dev);
transport_destroy_device(dev);
if (fci->f->dd_fcrport_size)
rport->dd_data = &rport[1];
rport->channel = channel;
+ rport->fast_io_fail_tmo = -1;
- INIT_WORK(&rport->dev_loss_work, fc_timeout_deleted_rport, rport);
- INIT_WORK(&rport->scan_work, fc_scsi_scan_rport, rport);
- INIT_WORK(&rport->stgt_delete_work, fc_starget_delete, rport);
- INIT_WORK(&rport->rport_delete_work, fc_rport_final_delete, rport);
+ INIT_DELAYED_WORK(&rport->dev_loss_work, fc_timeout_deleted_rport);
+ INIT_DELAYED_WORK(&rport->fail_io_work, fc_timeout_fail_rport_io);
+ INIT_WORK(&rport->scan_work, fc_scsi_scan_rport);
+ INIT_WORK(&rport->stgt_delete_work, fc_starget_delete);
+ INIT_WORK(&rport->rport_delete_work, fc_rport_final_delete);
spin_lock_irqsave(shost->host_lock, flags);
}
if (match) {
- struct work_struct *work =
+ struct delayed_work *work =
&rport->dev_loss_work;
memcpy(&rport->node_name, &ids->node_name,
/* restart the target */
/*
- * Stop the target timer first. Take no action
+ * Stop the target timers first. Take no action
* on the del_timer failure as the state
* machine state change will validate the
* transaction.
*/
+ if (!cancel_delayed_work(&rport->fail_io_work))
+ fc_flush_devloss(shost);
if (!cancel_delayed_work(work))
fc_flush_devloss(shost);
fc_remote_port_delete(struct fc_rport *rport)
{
struct Scsi_Host *shost = rport_to_shost(rport);
+ struct fc_internal *i = to_fc_internal(shost->transportt);
int timeout = rport->dev_loss_tmo;
unsigned long flags;
scsi_target_block(&rport->dev);
+ /* see if we need to kill io faster than waiting for device loss */
+ if ((rport->fast_io_fail_tmo != -1) &&
+ (rport->fast_io_fail_tmo < timeout) && (i->f->terminate_rport_io))
+ fc_queue_devloss_work(shost, &rport->fail_io_work,
+ rport->fast_io_fail_tmo * HZ);
+
/* cap the length the devices can be blocked until they are deleted */
fc_queue_devloss_work(shost, &rport->dev_loss_work, timeout * HZ);
}
* machine state change will validate the
* transaction.
*/
+ if (!cancel_delayed_work(&rport->fail_io_work))
+ fc_flush_devloss(shost);
if (!cancel_delayed_work(&rport->dev_loss_work))
fc_flush_devloss(shost);
* was a SCSI target (thus was blocked), and failed
* to return in the alloted time.
*
- * @data: rport target that failed to reappear in the alloted time.
+ * @work: rport target that failed to reappear in the alloted time.
**/
static void
-fc_timeout_deleted_rport(void *data)
+fc_timeout_deleted_rport(struct work_struct *work)
{
- struct fc_rport *rport = (struct fc_rport *)data;
+ struct fc_rport *rport =
+ container_of(work, struct fc_rport, dev_loss_work.work);
struct Scsi_Host *shost = rport_to_shost(rport);
struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
unsigned long flags;
fc_queue_work(shost, &rport->stgt_delete_work);
}
+/**
+ * fc_timeout_fail_rport_io - Timeout handler for a fast io failing on a
+ * disconnected SCSI target.
+ *
+ * @work: rport to terminate io on.
+ *
+ * Notes: Only requests the failure of the io, not that all are flushed
+ * prior to returning.
+ **/
+static void
+fc_timeout_fail_rport_io(struct work_struct *work)
+{
+ struct fc_rport *rport =
+ container_of(work, struct fc_rport, fail_io_work.work);
+ struct Scsi_Host *shost = rport_to_shost(rport);
+ struct fc_internal *i = to_fc_internal(shost->transportt);
+
+ if (rport->port_state != FC_PORTSTATE_BLOCKED)
+ return;
+
+ i->f->terminate_rport_io(rport);
+}
+
/**
* fc_scsi_scan_rport - called to perform a scsi scan on a remote port.
*
- * @data: remote port to be scanned.
+ * @work: remote port to be scanned.
**/
static void
-fc_scsi_scan_rport(void *data)
+fc_scsi_scan_rport(struct work_struct *work)
{
- struct fc_rport *rport = (struct fc_rport *)data;
+ struct fc_rport *rport =
+ container_of(work, struct fc_rport, scan_work);
struct Scsi_Host *shost = rport_to_shost(rport);
unsigned long flags;