/* * dev_loss_tmo: the default number of seconds that the FC transport * should insulate the loss of a remote port. * The maximum will be capped by the value of SCSI_DEVICE_BLOCK_MAX_TIMEOUT.
*/ staticunsignedint fc_dev_loss_tmo = 60; /* seconds */
module_param_named(dev_loss_tmo, fc_dev_loss_tmo, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(dev_loss_tmo, "Maximum number of seconds that the FC transport should" " insulate the loss of a remote port. Once this value is" " exceeded, the scsi target is removed. Value should be" " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT if" " fast_io_fail_tmo is not set.");
/* * Redefine so that we can have same named attributes in the * sdev/starget/host objects.
*/ #define FC_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \ struct device_attribute device_attr_##_prefix##_##_name = \
__ATTR(_name,_mode,_show,_store)
#define fc_enum_name_search(title, table_type, table) \ staticconstchar *get_fc_##title##_name(enum table_type table_key) \
{ \ int i; \ char *name = NULL; \
\ for (i = 0; i < ARRAY_SIZE(table); i++) { \ if (table[i].value == table_key) { \
name = table[i].name; \ break; \
} \
} \ return name; \
}
/* * For attributes : each object has : * An array of the actual attributes structures * An array of null-terminated pointers to the attribute * structures - used for mid-layer interaction. * * The attribute containers for the starget and host are are * part of the midlayer. As the remote port is specific to the * fc transport, we must provide the attribute container.
*/ struct device_attribute private_starget_attrs[
FC_STARGET_NUM_ATTRS]; struct device_attribute *starget_attrs[FC_STARGET_NUM_ATTRS + 1];
/* * if parent is remote port, use values from remote port. * Otherwise, this host uses the fc_transport, but not the * remote port interface. As such, initialize to known non-values.
*/ if (rport) {
fc_starget_node_name(starget) = rport->node_name;
fc_starget_port_name(starget) = rport->port_name;
fc_starget_port_id(starget) = rport->port_id;
} else {
fc_starget_node_name(starget) = -1;
fc_starget_port_name(starget) = -1;
fc_starget_port_id(starget) = -1;
}
/* * Setup and Remove actions for remote ports are handled * in the service functions below.
*/ static DECLARE_TRANSPORT_CLASS(fc_rport_class, "fc_remote_ports",
NULL,
NULL,
NULL);
/* * Setup and Remove actions for virtual ports are handled * in the service functions below.
*/ static DECLARE_TRANSPORT_CLASS(fc_vport_class, "fc_vports",
NULL,
NULL,
NULL);
/* * Netlink Infrastructure
*/
static atomic_t fc_event_seq;
/** * fc_get_event_number - Obtain the next sequential FC event number * * Notes: * We could have inlined this, but it would have required fc_event_seq to * be exposed. For now, live with the subroutine call. * Atomic used to avoid lock/unlock...
*/
u32
fc_get_event_number(void)
{ return atomic_add_return(1, &fc_event_seq);
}
EXPORT_SYMBOL(fc_get_event_number);
/** * fc_host_post_fc_event - routine to do the work of posting an event * on an fc_host. * @shost: host the event occurred on * @event_number: fc event number obtained from get_fc_event_number() * @event_code: fc_host event being posted * @data_len: amount, in bytes, of event data * @data_buf: pointer to event data * @vendor_id: value for Vendor id * * Notes: * This routine assumes no locks are held on entry.
*/ void
fc_host_post_fc_event(struct Scsi_Host *shost, u32 event_number, enum fc_host_event_code event_code,
u32 data_len, char *data_buf, u64 vendor_id)
{ struct sk_buff *skb; struct nlmsghdr *nlh; struct fc_nl_event *event; constchar *name;
size_t len, padding; int err;
if (!data_buf || data_len < 4)
data_len = 0;
if (!scsi_nl_sock) {
err = -ENOENT; goto send_fail;
}
len = FC_NL_MSGALIGN(sizeof(*event) - sizeof(event->event_data) + data_len);
/** * fc_host_post_event - called to post an even on an fc_host. * @shost: host the event occurred on * @event_number: fc event number obtained from get_fc_event_number() * @event_code: fc_host event being posted * @event_data: 32bits of data for the event being posted * * Notes: * This routine assumes no locks are held on entry.
*/ void
fc_host_post_event(struct Scsi_Host *shost, u32 event_number, enum fc_host_event_code event_code, u32 event_data)
{
fc_host_post_fc_event(shost, event_number, event_code,
(u32)sizeof(u32), (char *)&event_data, 0);
}
EXPORT_SYMBOL(fc_host_post_event);
/** * fc_host_post_vendor_event - called to post a vendor unique event * on an fc_host * @shost: host the event occurred on * @event_number: fc event number obtained from get_fc_event_number() * @data_len: amount, in bytes, of vendor unique data * @data_buf: pointer to vendor unique data * @vendor_id: Vendor id * * Notes: * This routine assumes no locks are held on entry.
*/ void
fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
u32 data_len, char * data_buf, u64 vendor_id)
{
fc_host_post_fc_event(shost, event_number, FCH_EVT_VENDOR_UNIQUE,
data_len, data_buf, vendor_id);
}
EXPORT_SYMBOL(fc_host_post_vendor_event);
/** * fc_find_rport_by_wwpn - find the fc_rport pointer for a given wwpn * @shost: host the fc_rport is associated with * @wwpn: wwpn of the fc_rport device * * Notes: * This routine assumes no locks are held on entry.
*/ struct fc_rport *
fc_find_rport_by_wwpn(struct Scsi_Host *shost, u64 wwpn)
{ struct fc_rport *rport; unsignedlong flags;
spin_lock_irqsave(shost->host_lock, flags);
list_for_each_entry(rport, &fc_host_rports(shost), peers) { if (rport->port_state != FC_PORTSTATE_ONLINE) continue;
/** * fc_host_fpin_rcv - routine to process a received FPIN. * @shost: host the FPIN was received on * @fpin_len: length of FPIN payload, in bytes * @fpin_buf: pointer to FPIN payload * @event_acknowledge: 1, if LLDD handles this event. * Notes: * This routine assumes no locks are held on entry.
*/ void
fc_host_fpin_rcv(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf,
u8 event_acknowledge)
{ struct fc_els_fpin *fpin = (struct fc_els_fpin *)fpin_buf; struct fc_tlv_desc *tlv;
u32 bytes_remain;
u32 dtag; enum fc_host_event_code event_code =
event_acknowledge ? FCH_EVT_LINK_FPIN_ACK : FCH_EVT_LINK_FPIN;
if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||
(rport->port_state == FC_PORTSTATE_DELETED) ||
(rport->port_state == FC_PORTSTATE_NOTPRESENT)) return -EBUSY; /* * Check for overflow; dev_loss_tmo is u32
*/ if (val > UINT_MAX) return -EINVAL;
/* * If fast_io_fail is off we have to cap * dev_loss_tmo at SCSI_DEVICE_BLOCK_MAX_TIMEOUT
*/ if (rport->fast_io_fail_tmo == -1 &&
val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT) return -EINVAL;
ret = get_fc_port_state_match(buf, &port_state); if (ret) return -EINVAL; if (port_state == FC_PORTSTATE_MARGINAL) { /* * Change the state to Marginal only if the * current rport state is Online * Allow only Online->Marginal
*/ if (rport->port_state == FC_PORTSTATE_ONLINE)
rport->port_state = port_state; elseif (port_state != rport->port_state) return -EINVAL;
} elseif (port_state == FC_PORTSTATE_ONLINE) { /* * Change the state to Online only if the * current rport state is Marginal * Allow only Marginal->Online
*/ if (rport->port_state == FC_PORTSTATE_MARGINAL)
rport->port_state = port_state; elseif (port_state != rport->port_state) return -EINVAL;
} else return -EINVAL; return count;
}
/* * Note: in the target show function we recognize when the remote * 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.
*/ #define fc_starget_show_function(field, format_string, sz, cast) \ static ssize_t \
show_fc_starget_##field (struct device *dev, \ struct device_attribute *attr, char *buf) \
{ \ struct scsi_target *starget = transport_class_to_starget(dev); \ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \ struct fc_internal *i = to_fc_internal(shost->transportt); \ struct fc_rport *rport = starget_to_rport(starget); \ if (rport) \
fc_starget_##field(starget) = rport->field; \ elseif (i->f->get_starget_##field) \
i->f->get_starget_##field(starget); \ return snprintf(buf, sz, format_string, \
cast fc_starget_##field(starget)); \
}
/* Validate and store the new name */ for (i=0, j=0; i < 16; i++) { int value;
value = hex_to_bin(*ns++); if (value >= 0)
j = (j << 4) | value; else return -EINVAL; if (i % 2) {
wwn[i/2] = j & 0xff;
j = 0;
}
}
*nm = wwn_to_u64(wwn);
return 0;
}
/* * "Short-cut" sysfs variable to create a new vport on a FC Host. * Input is a string of the form "<WWPN>:<WWNN>". Other attributes * will default to a NPIV-based FCP_Initiator; The WWNs are specified * as hex characters, and may *not* contain any prefixes (e.g. 0x, x, etc)
*/ static ssize_t
store_fc_host_vport_create(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ struct Scsi_Host *shost = transport_class_to_shost(dev); struct fc_vport_identifiers vid; struct fc_vport *vport; unsignedint cnt=count; int stat;
memset(&vid, 0, sizeof(vid));
/* count may include a LF at end of string */ if (buf[cnt-1] == '\n')
cnt--;
/* validate we have enough characters for WWPN */ if ((cnt != (16+1+16)) || (buf[16] != ':')) return -EINVAL;
stat = fc_parse_wwn(&buf[0], &vid.port_name); if (stat) return stat;
stat = fc_parse_wwn(&buf[17], &vid.node_name); if (stat) return stat;
/* we only allow support on Channel 0 !!! */
stat = fc_vport_setup(shost, 0, &shost->shost_gendev, &vid, &vport); return stat ? stat : count;
} static FC_DEVICE_ATTR(host, vport_create, S_IWUSR, NULL,
store_fc_host_vport_create);
/* * "Short-cut" sysfs variable to delete a vport on a FC Host. * Vport is identified by a string containing "<WWPN>:<WWNN>". * The WWNs are specified as hex characters, and may *not* contain * any prefixes (e.g. 0x, x, etc)
*/ static ssize_t
store_fc_host_vport_delete(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ struct Scsi_Host *shost = transport_class_to_shost(dev); struct fc_host_attrs *fc_host = shost_to_fc_host(shost); struct fc_vport *vport;
u64 wwpn, wwnn; unsignedlong flags; unsignedint cnt=count; int stat, match;
/* count may include a LF at end of string */ if (buf[cnt-1] == '\n')
cnt--;
/* validate we have enough characters for WWPN */ if ((cnt != (16+1+16)) || (buf[16] != ':')) return -EINVAL;
stat = fc_parse_wwn(&buf[0], &wwpn); if (stat) return stat;
stat = fc_parse_wwn(&buf[17], &wwnn); if (stat) return stat;
spin_lock_irqsave(shost->host_lock, flags);
match = 0; /* we only allow support on Channel 0 !!! */
list_for_each_entry(vport, &fc_host->vports, peers) { if ((vport->channel == 0) &&
(vport->port_name == wwpn) && (vport->node_name == wwnn)) { if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) break;
vport->flags |= FC_VPORT_DELETING;
match = 1; break;
}
}
spin_unlock_irqrestore(shost->host_lock, flags);
if (!match) return -ENODEV;
stat = fc_vport_terminate(vport); return stat ? stat : count;
} static FC_DEVICE_ATTR(host, vport_delete, S_IWUSR, NULL,
store_fc_host_vport_delete);
i = to_fc_internal(shost->transportt); return &i->vport_attr_cont.ac == cont;
}
/** * fc_eh_timed_out - FC Transport I/O timeout intercept handler * @scmd: The SCSI command which timed out * * This routine protects against error handlers getting invoked while a * rport is in a blocked state, typically due to a temporarily loss of * connectivity. If the error handlers are allowed to proceed, requests * to abort i/o, reset the target, etc will likely fail as there is no way * to communicate with the device to perform the requested function. These * failures may result in the midlayer taking the device offline, requiring * manual intervention to restore operation. * * This routine, called whenever an i/o times out, validates the state of * the underlying rport. If the rport is blocked, it returns * EH_RESET_TIMER, which will continue to reschedule the timeout. * Eventually, either the device will return, or devloss_tmo will fire, * and when the timeout then fires, it will be handled normally. * If the rport is not blocked, normal error handling continues. * * Notes: * This routine assumes no locks are held on entry.
*/ enum scsi_timeout_action fc_eh_timed_out(struct scsi_cmnd *scmd)
{ struct fc_rport *rport = starget_to_rport(scsi_target(scmd->device));
if (rport->port_state == FC_PORTSTATE_BLOCKED) return SCSI_EH_RESET_TIMER;
/* * Called by fc_user_scan to locate an rport on the shost that * matches the channel and target id, and invoke scsi_scan_target() * on the rport.
*/ staticvoid
fc_user_scan_tgt(struct Scsi_Host *shost, uint channel, uint id, u64 lun)
{ struct fc_rport *rport; unsignedlong flags;
spin_lock_irqsave(shost->host_lock, flags);
list_for_each_entry(rport, &fc_host_rports(shost), peers) { if (rport->scsi_target_id == -1) continue;
if ((rport->port_state != FC_PORTSTATE_ONLINE) &&
(rport->port_state != FC_PORTSTATE_MARGINAL)) continue;
if ((channel == rport->channel) &&
(id == rport->scsi_target_id)) {
spin_unlock_irqrestore(shost->host_lock, flags);
scsi_scan_target(&rport->dev, channel, id, lun,
SCSI_SCAN_MANUAL); return;
}
}
/* * Called via sysfs scan routines. Necessary, as the FC transport * wants to place all target objects below the rport object. So this * routine must invoke the scsi_scan_target() routine with the rport * object as the parent.
*/ staticint
fc_user_scan(struct Scsi_Host *shost, uint channel, uint id, u64 lun)
{
uint chlo, chhi;
uint tgtlo, tgthi;
/* Transport-managed attributes */
SETUP_PRIVATE_HOST_ATTRIBUTE_RW(dev_loss_tmo);
SETUP_PRIVATE_HOST_ATTRIBUTE_RW(tgtid_bind_type); if (ft->issue_fc_host_lip)
SETUP_PRIVATE_HOST_ATTRIBUTE_RW(issue_lip); if (ft->vport_create)
SETUP_PRIVATE_HOST_ATTRIBUTE_RW(vport_create); if (ft->vport_delete)
SETUP_PRIVATE_HOST_ATTRIBUTE_RW(vport_delete);
/** * fc_queue_work - Queue work to the fc_host workqueue. * @shost: Pointer to Scsi_Host bound to fc_host. * @work: Work to queue for execution. * * Return value: * 1 - work queued for execution * 0 - work is already queued * -EINVAL - work queue doesn't exist
*/ staticint
fc_queue_work(struct Scsi_Host *shost, struct work_struct *work)
{ if (unlikely(!fc_host_work_q(shost))) {
printk(KERN_ERR "ERROR: FC host '%s' attempted to queue work, " "when no workqueue created.\n", shost->hostt->name);
dump_stack();
return -EINVAL;
}
return queue_work(fc_host_work_q(shost), work);
}
/** * fc_flush_work - Flush a fc_host's workqueue. * @shost: Pointer to Scsi_Host bound to fc_host.
*/ staticvoid
fc_flush_work(struct Scsi_Host *shost)
{ if (!fc_host_work_q(shost)) {
printk(KERN_ERR "ERROR: FC host '%s' attempted to flush work, " "when no workqueue created.\n", shost->hostt->name);
dump_stack(); return;
}
flush_workqueue(fc_host_work_q(shost));
}
/** * fc_queue_devloss_work - Schedule work for the fc_host devloss workqueue. * @shost: Pointer to Scsi_Host bound to fc_host. * @rport: rport associated with the devloss work * @work: Work to queue for execution. * @delay: jiffies to delay the work queuing * * Return value: * 1 on success / 0 already queued / < 0 for error
*/ staticint
fc_queue_devloss_work(struct Scsi_Host *shost, struct fc_rport *rport, struct delayed_work *work, unsignedlong delay)
{ if (unlikely(!rport->devloss_work_q)) {
printk(KERN_ERR "ERROR: FC host '%s' attempted to queue work, " "when no workqueue created.\n", shost->hostt->name);
dump_stack();
/** * fc_flush_devloss - Flush a fc_host's devloss workqueue. * @shost: Pointer to Scsi_Host bound to fc_host. * @rport: rport associated with the devloss work
*/ staticvoid
fc_flush_devloss(struct Scsi_Host *shost, struct fc_rport *rport)
{ if (unlikely(!rport->devloss_work_q)) {
printk(KERN_ERR "ERROR: FC host '%s' attempted to flush work, " "when no workqueue created.\n", shost->hostt->name);
dump_stack(); return;
}
flush_workqueue(rport->devloss_work_q);
}
/** * fc_remove_host - called to terminate any fc_transport-related elements for a scsi host. * @shost: Which &Scsi_Host * * This routine is expected to be called immediately preceding the * a driver's call to scsi_remove_host(). * * WARNING: A driver utilizing the fc_transport, which fails to call * this routine prior to scsi_remove_host(), will leave dangling * objects in /sys/class/fc_remote_ports. Access to any of these * objects can result in a system crash !!! * * Notes: * This routine assumes no locks are held on entry.
*/ void
fc_remove_host(struct Scsi_Host *shost)
{ struct fc_vport *vport = NULL, *next_vport = NULL; struct fc_rport *rport = NULL, *next_rport = NULL; struct workqueue_struct *work_q; struct fc_host_attrs *fc_host = shost_to_fc_host(shost); unsignedlong flags;
/* flush all scan work items */
scsi_flush_work(shost);
/* flush all stgt delete, and rport delete work items, then kill it */ if (fc_host->work_q) {
work_q = fc_host->work_q;
fc_host->work_q = NULL;
destroy_workqueue(work_q);
}
}
EXPORT_SYMBOL(fc_remove_host);
/* Involve the LLDD if possible to terminate all io on the rport. */ if (i->f->terminate_rport_io)
i->f->terminate_rport_io(rport);
/* * Must unblock to flush queued IO. scsi-ml will fail incoming reqs.
*/
scsi_target_unblock(&rport->dev, SDEV_TRANSPORT_OFFLINE);
}
/** * fc_starget_delete - called to delete the scsi descendants of an rport * @work: remote port to be operated on. * * Deletes target and all sdevs.
*/ staticvoid
fc_starget_delete(struct work_struct *work)
{ struct fc_rport *rport =
container_of(work, struct fc_rport, stgt_delete_work);
/** * fc_rport_final_delete - finish rport termination and delete it. * @work: remote port to be deleted.
*/ staticvoid
fc_rport_final_delete(struct work_struct *work)
{ 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); struct fc_internal *i = to_fc_internal(shost->transportt); struct workqueue_struct *work_q; unsignedlong flags; int do_callback = 0;
fc_terminate_rport_io(rport);
/* * if a scan is pending, flush the SCSI Host work_q so that * that we can reclaim the rport scan work element.
*/ if (rport->flags & FC_RPORT_SCAN_PENDING)
scsi_flush_work(shost);
/* * Cancel any outstanding timers. These should really exist * only when rmmod'ing the LLDD and we're asking for * immediate termination of the rports
*/
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, rport); if (!cancel_delayed_work(&rport->dev_loss_work))
fc_flush_devloss(shost, rport);
cancel_work_sync(&rport->scan_work);
spin_lock_irqsave(shost->host_lock, flags);
rport->flags &= ~FC_RPORT_DEVLOSS_PENDING;
}
spin_unlock_irqrestore(shost->host_lock, flags);
/* Delete SCSI target and sdevs */ if (rport->scsi_target_id != -1)
fc_starget_delete(&rport->stgt_delete_work);
/* * Notify the driver that the rport is now dead. The LLDD will * also guarantee that any communication to the rport is terminated * * Avoid this call if we already called it when we preserved the * rport for the binding.
*/
spin_lock_irqsave(shost->host_lock, flags); if (!(rport->flags & FC_RPORT_DEVLOSS_CALLBK_DONE) &&
(i->f->dev_loss_tmo_callbk)) {
rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
do_callback = 1;
}
spin_unlock_irqrestore(shost->host_lock, flags);
if (do_callback)
i->f->dev_loss_tmo_callbk(rport);
transport_remove_device(dev);
device_del(dev);
transport_destroy_device(dev);
scsi_host_put(shost); /* for fc_host->rport list */
put_device(dev); /* for self-reference */
}
/** * fc_remote_port_create - allocates and creates a remote FC port. * @shost: scsi host the remote port is connected to. * @channel: Channel on shost port connected to. * @ids: The world wide names, fc address, and FC4 port * roles for the remote port. * * Allocates and creates the remoter port structure, including the * class and sysfs creation. * * Notes: * This routine assumes no locks are held on entry.
*/ staticstruct fc_rport *
fc_remote_port_create(struct Scsi_Host *shost, int channel, struct fc_rport_identifiers *ids)
{ struct fc_host_attrs *fc_host = shost_to_fc_host(shost); struct fc_internal *fci = to_fc_internal(shost->transportt); struct fc_rport *rport; struct device *dev; unsignedlong flags; int error;
size_t size;
rport->number = fc_host->next_rport_number++; if ((rport->roles & FC_PORT_ROLE_FCP_TARGET) ||
(rport->roles & FC_PORT_ROLE_FCP_DUMMY_INITIATOR))
rport->scsi_target_id = fc_host->next_target_id++; else
rport->scsi_target_id = -1;
list_add_tail(&rport->peers, &fc_host->rports);
scsi_host_get(shost); /* for fc_host->rport list */
spin_unlock_irqrestore(shost->host_lock, flags);
rport->devloss_work_q = alloc_workqueue("fc_dl_%d_%d", 0, 0,
shost->host_no, rport->number); if (!rport->devloss_work_q) {
printk(KERN_ERR "FC Remote Port alloc_workqueue failed\n"); /* * Note that we have not yet called device_initialize() / get_device() * Cannot reclaim incremented rport->number because we released host_lock
*/
spin_lock_irqsave(shost->host_lock, flags);
list_del(&rport->peers);
scsi_host_put(shost); /* for fc_host->rport list */
spin_unlock_irqrestore(shost->host_lock, flags);
kfree(rport); return NULL;
}
error = device_add(dev); if (error) {
printk(KERN_ERR "FC Remote Port device_add failed\n"); goto delete_rport;
}
transport_add_device(dev);
transport_configure_device(dev);
fc_bsg_rportadd(shost, rport); /* ignore any bsg add error - we just can't do sgio */
if (rport->roles & FC_PORT_ROLE_FCP_TARGET) { /* initiate a scan of the target */
rport->flags |= FC_RPORT_SCAN_PENDING;
scsi_queue_work(shost, &rport->scan_work);
}
return rport;
delete_rport:
transport_destroy_device(dev);
spin_lock_irqsave(shost->host_lock, flags);
list_del(&rport->peers);
scsi_host_put(shost); /* for fc_host->rport list */
spin_unlock_irqrestore(shost->host_lock, flags);
put_device(dev->parent);
kfree(rport); return NULL;
}
/** * fc_remote_port_add - notify fc transport of the existence of a remote FC port. * @shost: scsi host the remote port is connected to. * @channel: Channel on shost port connected to. * @ids: The world wide names, fc address, and FC4 port * roles for the remote port. * * The LLDD calls this routine to notify the transport of the existence * of a remote port. The LLDD provides the unique identifiers (wwpn,wwn) * of the port, it's FC address (port_id), and the FC4 roles that are * active for the port. * * For ports that are FCP targets (aka scsi targets), the FC transport * maintains consistent target id bindings on behalf of the LLDD. * A consistent target id binding is an assignment of a target id to * a remote port identifier, which persists while the scsi host is * attached. The remote port can disappear, then later reappear, and * it's target id assignment remains the same. This allows for shifts * in FC addressing (if binding by wwpn or wwnn) with no apparent * changes to the scsi subsystem which is based on scsi host number and * target id values. Bindings are only valid during the attachment of * the scsi host. If the host detaches, then later re-attaches, target * id bindings may change. * * This routine is responsible for returning a remote port structure. * The routine will search the list of remote ports it maintains * internally on behalf of consistent target id mappings. If found, the * remote port structure will be reused. Otherwise, a new remote port * structure will be allocated. * * Whenever a remote port is allocated, a new fc_remote_port class * device is created. * * Should not be called from interrupt context. * * Notes: * This routine assumes no locks are held on entry.
*/ struct fc_rport *
fc_remote_port_add(struct Scsi_Host *shost, int channel, struct fc_rport_identifiers *ids)
{ struct fc_internal *fci = to_fc_internal(shost->transportt); struct fc_host_attrs *fc_host = shost_to_fc_host(shost); struct fc_rport *rport; unsignedlong flags; int match = 0;
/* ensure any stgt delete functions are done */
fc_flush_work(shost);
/* * Search the list of "active" rports, for an rport that has been * deleted, but we've held off the real delete while the target * is in a "blocked" state.
*/
spin_lock_irqsave(shost->host_lock, flags);
switch (fc_host->tgtid_bind_type) { case FC_TGTID_BIND_BY_WWPN: case FC_TGTID_BIND_NONE: if (rport->port_name == ids->port_name)
match = 1; break; case FC_TGTID_BIND_BY_WWNN: if (rport->node_name == ids->node_name)
match = 1; break; case FC_TGTID_BIND_BY_ID: if (rport->port_id == ids->port_id)
match = 1; break;
}
if (fci->f->dd_fcrport_size)
memset(rport->dd_data, 0,
fci->f->dd_fcrport_size);
/* * If we were not a target, cancel the * io terminate and rport timers, and * we're done. * * If we were a target, but our new role * doesn't indicate a target, leave the * timers running expecting the role to * change as the target fully logs in. If * it doesn't, the target will be torn down. * * If we were a target, and our role shows * we're still a target, cancel the timers * and kick off a scan.
*/
/* was a target, not in roles */ if ((rport->scsi_target_id != -1) &&
(!(ids->roles & FC_PORT_ROLE_FCP_TARGET))) return rport;
/* * Stop the fail io and dev_loss timers. * If they flush, the port_state will * be checked and will NOOP the function.
*/ if (!cancel_delayed_work(&rport->fail_io_work))
fc_flush_devloss(shost, rport); if (!cancel_delayed_work(&rport->dev_loss_work))
fc_flush_devloss(shost, rport);
/* if target, initiate a scan */ if (rport->scsi_target_id != -1) {
scsi_target_unblock(&rport->dev,
SDEV_RUNNING);
spin_lock_irqsave(shost->host_lock,
flags);
rport->flags |= FC_RPORT_SCAN_PENDING;
scsi_queue_work(shost,
&rport->scan_work);
spin_unlock_irqrestore(shost->host_lock,
flags);
}
fc_bsg_goose_queue(rport);
return rport;
}
}
}
/* * Search the bindings array * Note: if never a FCP target, you won't be on this list
*/ if (fc_host->tgtid_bind_type != FC_TGTID_BIND_NONE) {
/* search for a matching consistent binding */
list_for_each_entry(rport, &fc_host->rport_bindings,
peers) { if (rport->channel != channel) continue;
switch (fc_host->tgtid_bind_type) { case FC_TGTID_BIND_BY_WWPN: if (rport->port_name == ids->port_name)
match = 1; break; case FC_TGTID_BIND_BY_WWNN: if (rport->node_name == ids->node_name)
match = 1; break; case FC_TGTID_BIND_BY_ID: if (rport->port_id == ids->port_id)
match = 1; break; case FC_TGTID_BIND_NONE: /* to keep compiler happy */ break;
}
if (match) {
list_move_tail(&rport->peers, &fc_host->rports); break;
}
}
/** * fc_remote_port_delete - notifies the fc transport that a remote port is no longer in existence. * @rport: The remote port that no longer exists * * The LLDD calls this routine to notify the transport that a remote * port is no longer part of the topology. Note: Although a port * may no longer be part of the topology, it may persist in the remote * ports displayed by the fc_host. We do this under 2 conditions: * * 1) If the port was a scsi target, we delay its deletion by "blocking" it. * This allows the port to temporarily disappear, then reappear without * disrupting the SCSI device tree attached to it. During the "blocked" * period the port will still exist. * * 2) If the port was a scsi target and disappears for longer than we * expect, we'll delete the port and the tear down the SCSI device tree * attached to it. However, we want to semi-persist the target id assigned * to that port if it eventually does exist. The port structure will * remain (although with minimal information) so that the target id * bindings also remain. * * If the remote port is not an FCP Target, it will be fully torn down * and deallocated, including the fc_remote_port class device. * * If the remote port is an FCP Target, the port will be placed in a * temporary blocked state. From the LLDD's perspective, the rport no * longer exists. From the SCSI midlayer's perspective, the SCSI target * exists, but all sdevs on it are blocked from further I/O. The following * is then expected. * * If the remote port does not return (signaled by a LLDD call to * fc_remote_port_add()) within the dev_loss_tmo timeout, then the * scsi target is removed - killing all outstanding i/o and removing the * scsi devices attached to it. The port structure will be marked Not * Present and be partially cleared, leaving only enough information to * recognize the remote port relative to the scsi target id binding if * it later appears. The port will remain as long as there is a valid * binding (e.g. until the user changes the binding type or unloads the * scsi host with the binding). * * If the remote port returns within the dev_loss_tmo value (and matches * according to the target id binding type), the port structure will be * reused. If it is no longer a SCSI target, the target will be torn * down. If it continues to be a SCSI target, then the target will be * unblocked (allowing i/o to be resumed), and a scan will be activated * to ensure that all luns are detected. * * Called from normal process context only - cannot be called from interrupt. * * Notes: * This routine assumes no locks are held on entry.
*/ void
fc_remote_port_delete(struct fc_rport *rport)
{ struct Scsi_Host *shost = rport_to_shost(rport); unsignedlong timeout = rport->dev_loss_tmo; unsignedlong flags;
/* * No need to flush the fc_host work_q's, as all adds are synchronous. * * We do need to reclaim the rport scan work element, so eventually * (in fc_rport_final_delete()) we'll flush the scsi host work_q if * there's still a scan pending.
*/
/* * In the past, we if this was not an FCP-Target, we would * unconditionally just jump to deleting the rport. * However, rports can be used as node containers by the LLDD, * and its not appropriate to just terminate the rport at the * first sign of a loss in connectivity. The LLDD may want to * send ELS traffic to re-validate the login. If the rport is * immediately deleted, it makes it inappropriate for a node * container. * So... we now unconditionally wait dev_loss_tmo before * destroying an rport.
*/
rport->port_state = FC_PORTSTATE_BLOCKED;
rport->flags |= FC_RPORT_DEVLOSS_PENDING;
spin_unlock_irqrestore(shost->host_lock, flags);
scsi_block_targets(shost, &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))
fc_queue_devloss_work(shost, rport, &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, &rport->dev_loss_work,
timeout * HZ);
}
EXPORT_SYMBOL(fc_remote_port_delete);
/** * fc_remote_port_rolechg - notifies the fc transport that the roles on a remote may have changed. * @rport: The remote port that changed. * @roles: New roles for this port. * * Description: The LLDD calls this routine to notify the transport that the * roles on a remote port may have changed. The largest effect of this is * if a port now becomes a FCP Target, it must be allocated a * scsi target id. If the port is no longer a FCP target, any * scsi target id value assigned to it will persist in case the * role changes back to include FCP Target. No changes in the scsi * midlayer will be invoked if the role changes (in the expectation * that the role will be resumed. If it doesn't normal error processing * will take place). * * Should not be called from interrupt context. * * Notes: * This routine assumes no locks are held on entry.
*/ void
fc_remote_port_rolechg(struct fc_rport *rport, u32 roles)
{ struct Scsi_Host *shost = rport_to_shost(rport); struct fc_host_attrs *fc_host = shost_to_fc_host(shost); unsignedlong flags; int create = 0;
if (create) { /* * There may have been a delete timer running on the * port. Ensure that it is cancelled as we now know * the port is an FCP Target. * Note: we know the rport exists and is in an online * state as the LLDD would not have had an rport * reference to pass us. * * Take no action on the timer_delete() failure as the state * machine state change will validate the * transaction.
*/ if (!cancel_delayed_work(&rport->fail_io_work))
fc_flush_devloss(shost, rport); if (!cancel_delayed_work(&rport->dev_loss_work))
fc_flush_devloss(shost, rport);
/* ensure any stgt delete functions are done */
fc_flush_work(shost);
scsi_target_unblock(&rport->dev, SDEV_RUNNING); /* initiate a scan of the target */
spin_lock_irqsave(shost->host_lock, flags);
rport->flags |= FC_RPORT_SCAN_PENDING;
scsi_queue_work(shost, &rport->scan_work);
spin_unlock_irqrestore(shost->host_lock, flags);
}
}
EXPORT_SYMBOL(fc_remote_port_rolechg);
/** * fc_timeout_deleted_rport - Timeout handler for a deleted remote port. * @work: rport target that failed to reappear in the allotted time. * * Description: An attempt to delete a remote port blocks, and if it fails * to return in the allotted time this gets called.
*/ staticvoid
fc_timeout_deleted_rport(struct work_struct *work)
{ struct fc_rport *rport =
container_of(work, struct fc_rport, dev_loss_work.work); struct Scsi_Host *shost = rport_to_shost(rport); struct fc_internal *i = to_fc_internal(shost->transportt); struct fc_host_attrs *fc_host = shost_to_fc_host(shost); unsignedlong flags; int do_callback = 0;
spin_lock_irqsave(shost->host_lock, flags);
rport->flags &= ~FC_RPORT_DEVLOSS_PENDING;
/* * If the port is ONLINE, then it came back. If it was a SCSI * target, validate it still is. If not, tear down the * scsi_target on it.
*/ if (((rport->port_state == FC_PORTSTATE_ONLINE) ||
(rport->port_state == FC_PORTSTATE_MARGINAL)) &&
(rport->scsi_target_id != -1) &&
!(rport->roles & FC_PORT_ROLE_FCP_TARGET)) {
dev_printk(KERN_ERR, &rport->dev, "blocked FC remote port time out: no longer" " a FCP target, removing starget\n");
spin_unlock_irqrestore(shost->host_lock, flags);
scsi_target_unblock(&rport->dev, SDEV_TRANSPORT_OFFLINE);
fc_queue_work(shost, &rport->stgt_delete_work); return;
}
/* NOOP state - we're flushing workq's */ if (rport->port_state != FC_PORTSTATE_BLOCKED) {
spin_unlock_irqrestore(shost->host_lock, flags);
dev_printk(KERN_ERR, &rport->dev, "blocked FC remote port time out: leaving" " rport%s alone\n",
(rport->scsi_target_id != -1) ? " and starget" : ""); return;
}
if ((fc_host->tgtid_bind_type == FC_TGTID_BIND_NONE) ||
(rport->scsi_target_id == -1)) {
list_del(&rport->peers);
rport->port_state = FC_PORTSTATE_DELETED;
dev_printk(KERN_ERR, &rport->dev, "blocked FC remote port time out: removing" " rport%s\n",
(rport->scsi_target_id != -1) ? " and starget" : "");
fc_queue_work(shost, &rport->rport_delete_work);
spin_unlock_irqrestore(shost->host_lock, flags); return;
}
dev_printk(KERN_ERR, &rport->dev, "blocked FC remote port time out: removing target and " "saving binding\n");
/* * Note: We do not remove or clear the hostdata area. This allows * host-specific target data to persist along with the * scsi_target_id. It's up to the host to manage it's hostdata area.
*/
/* * Reinitialize port attributes that may change if the port comes back.
*/
rport->maxframe_size = -1;
rport->supported_classes = FC_COS_UNSPECIFIED;
rport->roles = FC_PORT_ROLE_UNKNOWN;
rport->port_state = FC_PORTSTATE_NOTPRESENT;
rport->flags &= ~FC_RPORT_FAST_FAIL_TIMEDOUT;
/* * Pre-emptively kill I/O rather than waiting for the work queue * item to teardown the starget. (FCOE libFC folks prefer this * and to have the rport_port_id still set when it's done).
*/
spin_unlock_irqrestore(shost->host_lock, flags);
fc_terminate_rport_io(rport);
spin_lock_irqsave(shost->host_lock, flags);
if (rport->port_state == FC_PORTSTATE_NOTPRESENT) { /* still missing */
/* remove the identifiers that aren't used in the consisting binding */ switch (fc_host->tgtid_bind_type) { case FC_TGTID_BIND_BY_WWPN:
rport->node_name = -1;
rport->port_id = -1; break; case FC_TGTID_BIND_BY_WWNN:
rport->port_name = -1;
rport->port_id = -1; break; case FC_TGTID_BIND_BY_ID:
rport->node_name = -1;
rport->port_name = -1; break; case FC_TGTID_BIND_NONE: /* to keep compiler happy */ break;
}
/* * As this only occurs if the remote port (scsi target) * went away and didn't come back - we'll remove * all attached scsi devices.
*/
rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
fc_queue_work(shost, &rport->stgt_delete_work);
do_callback = 1;
}
spin_unlock_irqrestore(shost->host_lock, flags);
/* * Notify the driver that the rport is now dead. The LLDD will * also guarantee that any communication to the rport is terminated * * Note: we set the CALLBK_DONE flag above to correspond
*/ if (do_callback && i->f->dev_loss_tmo_callbk)
i->f->dev_loss_tmo_callbk(rport);
}
/** * 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.
*/ staticvoid
fc_timeout_fail_rport_io(struct work_struct *work)
{ struct fc_rport *rport =
container_of(work, struct fc_rport, fail_io_work.work);
if (rport->port_state != FC_PORTSTATE_BLOCKED) return;
/** * fc_block_rport() - Block SCSI eh thread for blocked fc_rport. * @rport: Remote port that scsi_eh is trying to recover. * * This routine can be called from a FC LLD scsi_eh callback. It * blocks the scsi_eh thread until the fc_rport leaves the * FC_PORTSTATE_BLOCKED, or the fast_io_fail_tmo fires. This is * necessary to avoid the scsi_eh failing recovery actions for blocked * rports which would lead to offlined SCSI devices. * * Returns: 0 if the fc_rport left the state FC_PORTSTATE_BLOCKED. * FAST_IO_FAIL if the fast_io_fail_tmo fired, this should be * passed back to scsi_eh.
*/ int fc_block_rport(struct fc_rport *rport)
{ struct Scsi_Host *shost = rport_to_shost(rport); unsignedlong flags;
if (rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT) return FAST_IO_FAIL;
return 0;
}
EXPORT_SYMBOL(fc_block_rport);
/** * fc_block_scsi_eh - Block SCSI eh thread for blocked fc_rport * @cmnd: SCSI command that scsi_eh is trying to recover * * This routine can be called from a FC LLD scsi_eh callback. It * blocks the scsi_eh thread until the fc_rport leaves the * FC_PORTSTATE_BLOCKED, or the fast_io_fail_tmo fires. This is * necessary to avoid the scsi_eh failing recovery actions for blocked * rports which would lead to offlined SCSI devices. * * Returns: 0 if the fc_rport left the state FC_PORTSTATE_BLOCKED. * FAST_IO_FAIL if the fast_io_fail_tmo fired, this should be * passed back to scsi_eh.
*/ int fc_block_scsi_eh(struct scsi_cmnd *cmnd)
{ struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
/* * fc_eh_should_retry_cmd - Checks if the cmd should be retried or not * @scmd: The SCSI command to be checked * * This checks the rport state to decide if a cmd is * retryable. * * Returns: true if the rport state is not in marginal state.
*/ bool fc_eh_should_retry_cmd(struct scsi_cmnd *scmd)
{ struct fc_rport *rport = starget_to_rport(scsi_target(scmd->device));
/** * fc_vport_setup - allocates and creates a FC virtual port. * @shost: scsi host the virtual port is connected to. * @channel: Channel on shost port connected to. * @pdev: parent device for vport * @ids: The world wide names, FC4 port roles, etc for * the virtual port. * @ret_vport: The pointer to the created vport. * * Allocates and creates the vport structure, calls the parent host * to instantiate the vport, this completes w/ class and sysfs creation. * * Notes: * This routine assumes no locks are held on entry.
*/ staticint
fc_vport_setup(struct Scsi_Host *shost, int channel, struct device *pdev, struct fc_vport_identifiers *ids, struct fc_vport **ret_vport)
{ struct fc_host_attrs *fc_host = shost_to_fc_host(shost); struct fc_internal *fci = to_fc_internal(shost->transportt); struct fc_vport *vport; struct device *dev; unsignedlong flags;
size_t size; int error;
error = device_add(dev); if (error) {
printk(KERN_ERR "FC Virtual Port device_add failed\n"); goto delete_vport;
}
transport_add_device(dev);
transport_configure_device(dev);
error = fci->f->vport_create(vport, ids->disable); if (error) {
printk(KERN_ERR "FC Virtual Port LLDD Create failed\n"); goto delete_vport_all;
}
/* * if the parent isn't the physical adapter's Scsi_Host, ensure * the Scsi_Host at least contains a symlink to the vport.
*/ if (pdev != &shost->shost_gendev) {
error = sysfs_create_link(&shost->shost_gendev.kobj,
&dev->kobj, dev_name(dev)); if (error)
printk(KERN_ERR "%s: Cannot create vport symlinks for " "%s, err=%d\n",
__func__, dev_name(dev), error);
}
spin_lock_irqsave(shost->host_lock, flags);
vport->flags &= ~FC_VPORT_CREATING;
spin_unlock_irqrestore(shost->host_lock, flags);
dev_printk(KERN_NOTICE, pdev, "%s created via shost%d channel %d\n", dev_name(dev),
shost->host_no, channel);
*ret_vport = vport;
return 0;
delete_vport_all:
transport_remove_device(dev);
device_del(dev);
delete_vport:
transport_destroy_device(dev);
spin_lock_irqsave(shost->host_lock, flags);
list_del(&vport->peers);
scsi_host_put(shost); /* for fc_host->vport list */
fc_host->npiv_vports_inuse--;
spin_unlock_irqrestore(shost->host_lock, flags);
put_device(dev->parent);
kfree(vport);
return error;
}
/** * fc_vport_create - Admin App or LLDD requests creation of a vport * @shost: scsi host the virtual port is connected to. * @channel: channel on shost port connected to. * @ids: The world wide names, FC4 port roles, etc for * the virtual port. * * Notes: * This routine assumes no locks are held on entry.
*/ struct fc_vport *
fc_vport_create(struct Scsi_Host *shost, int channel, struct fc_vport_identifiers *ids)
{ int stat; struct fc_vport *vport;
stat = fc_vport_setup(shost, channel, &shost->shost_gendev,
ids, &vport); return stat ? NULL : vport;
}
EXPORT_SYMBOL(fc_vport_create);
/** * fc_vport_terminate - Admin App or LLDD requests termination of a vport * @vport: fc_vport to be terminated * * Calls the LLDD vport_delete() function, then deallocates and removes * the vport from the shost and object tree. * * Notes: * This routine assumes no locks are held on entry.
*/ int
fc_vport_terminate(struct fc_vport *vport)
{ struct Scsi_Host *shost = vport_to_shost(vport); struct fc_host_attrs *fc_host = shost_to_fc_host(shost); struct fc_internal *i = to_fc_internal(shost->transportt); struct device *dev = &vport->dev; unsignedlong flags; int stat;
if (i->f->vport_delete)
stat = i->f->vport_delete(vport); else
stat = -ENOENT;
spin_lock_irqsave(shost->host_lock, flags);
vport->flags &= ~FC_VPORT_DELETING; if (!stat) {
vport->flags |= FC_VPORT_DELETED;
list_del(&vport->peers);
fc_host->npiv_vports_inuse--;
scsi_host_put(shost); /* for fc_host->vport list */
}
spin_unlock_irqrestore(shost->host_lock, flags);
if (stat) return stat;
if (dev->parent != &shost->shost_gendev)
sysfs_remove_link(&shost->shost_gendev.kobj, dev_name(dev));
transport_remove_device(dev);
device_del(dev);
transport_destroy_device(dev);
/* * Removing our self-reference should mean our * release function gets called, which will drop the remaining * parent reference and free the data structure.
*/
put_device(dev); /* for self-reference */
/** * fc_vport_sched_delete - workq-based delete request for a vport * @work: vport to be deleted.
*/ staticvoid
fc_vport_sched_delete(struct work_struct *work)
{ struct fc_vport *vport =
container_of(work, struct fc_vport, vport_delete_work); int stat;
stat = fc_vport_terminate(vport); if (stat)
dev_printk(KERN_ERR, vport->dev.parent, "%s: %s could not be deleted created via " "shost%d channel %d - error %d\n", __func__,
dev_name(&vport->dev), vport->shost->host_no,
vport->channel, stat);
}
/* * BSG support
*/
/** * fc_bsg_job_timeout - handler for when a bsg request timesout * @req: request that timed out
*/ staticenum blk_eh_timer_return
fc_bsg_job_timeout(struct request *req)
{ struct bsg_job *job = blk_mq_rq_to_pdu(req); struct Scsi_Host *shost = fc_bsg_to_shost(job); struct fc_rport *rport = fc_bsg_to_rport(job); struct fc_internal *i = to_fc_internal(shost->transportt); int err = 0, inflight = 0;
if (rport && rport->port_state == FC_PORTSTATE_BLOCKED) return BLK_EH_RESET_TIMER;
inflight = bsg_job_get(job);
if (inflight && i->f->bsg_timeout) { /* call LLDD to abort the i/o as it has timed out */
err = i->f->bsg_timeout(job); if (err == -EAGAIN) {
bsg_job_put(job); return BLK_EH_RESET_TIMER;
} elseif (err)
printk(KERN_ERR "ERROR: FC BSG request timeout - LLD " "abort failed with status %d\n", err);
}
/* the blk_end_sync_io() doesn't check the error */ if (inflight)
blk_mq_end_request(req, BLK_STS_IOERR); return BLK_EH_DONE;
}
/** * fc_bsg_host_dispatch - process fc host bsg requests and dispatch to LLDD * @shost: scsi host rport attached to * @job: bsg job to be processed
*/ staticint fc_bsg_host_dispatch(struct Scsi_Host *shost, struct bsg_job *job)
{ struct fc_internal *i = to_fc_internal(shost->transportt); struct fc_bsg_request *bsg_request = job->request; struct fc_bsg_reply *bsg_reply = job->reply; int cmdlen = sizeof(uint32_t); /* start with length of msgcode */ int ret;
/* check if we really have all the request data needed */ if (job->request_len < cmdlen) {
ret = -ENOMSG; goto fail_host_msg;
}
/* Validate the host command */ switch (bsg_request->msgcode) { case FC_BSG_HST_ADD_RPORT:
cmdlen += sizeof(struct fc_bsg_host_add_rport); break;
case FC_BSG_HST_DEL_RPORT:
cmdlen += sizeof(struct fc_bsg_host_del_rport); break;
case FC_BSG_HST_ELS_NOLOGIN:
cmdlen += sizeof(struct fc_bsg_host_els); /* there better be a xmt and rcv payloads */ if ((!job->request_payload.payload_len) ||
(!job->reply_payload.payload_len)) {
ret = -EINVAL; goto fail_host_msg;
} break;
case FC_BSG_HST_CT:
cmdlen += sizeof(struct fc_bsg_host_ct); /* there better be xmt and rcv payloads */ if ((!job->request_payload.payload_len) ||
(!job->reply_payload.payload_len)) {
ret = -EINVAL; goto fail_host_msg;
} break;
case FC_BSG_HST_VENDOR:
cmdlen += sizeof(struct fc_bsg_host_vendor); if ((shost->hostt->vendor_id == 0L) ||
(bsg_request->rqst_data.h_vendor.vendor_id !=
shost->hostt->vendor_id)) {
ret = -ESRCH; goto fail_host_msg;
} break;
default:
ret = -EBADR; goto fail_host_msg;
}
ret = i->f->bsg_request(job); if (!ret) return 0;
fail_host_msg: /* return the errno failure code as the only status */
BUG_ON(job->reply_len < sizeof(uint32_t));
bsg_reply->reply_payload_rcv_len = 0;
bsg_reply->result = ret;
job->reply_len = sizeof(uint32_t);
bsg_job_done(job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len); return 0;
}
/* * fc_bsg_goose_queue - restart rport queue in case it was stopped * @rport: rport to be restarted
*/ staticvoid
fc_bsg_goose_queue(struct fc_rport *rport)
{ struct request_queue *q = rport->rqst_q;
if (q)
blk_mq_run_hw_queues(q, true);
}
/** * fc_bsg_rport_dispatch - process rport bsg requests and dispatch to LLDD * @shost: scsi host rport attached to * @job: bsg job to be processed
*/ staticint fc_bsg_rport_dispatch(struct Scsi_Host *shost, struct bsg_job *job)
{ struct fc_internal *i = to_fc_internal(shost->transportt); struct fc_bsg_request *bsg_request = job->request; struct fc_bsg_reply *bsg_reply = job->reply; int cmdlen = sizeof(uint32_t); /* start with length of msgcode */ int ret;
/* check if we really have all the request data needed */ if (job->request_len < cmdlen) {
ret = -ENOMSG; goto fail_rport_msg;
}
/* Validate the rport command */ switch (bsg_request->msgcode) { case FC_BSG_RPT_ELS:
cmdlen += sizeof(struct fc_bsg_rport_els); goto check_bidi;
case FC_BSG_RPT_CT:
cmdlen += sizeof(struct fc_bsg_rport_ct);
check_bidi: /* there better be xmt and rcv payloads */ if ((!job->request_payload.payload_len) ||
(!job->reply_payload.payload_len)) {
ret = -EINVAL; goto fail_rport_msg;
} break; default:
ret = -EBADR; goto fail_rport_msg;
}
ret = i->f->bsg_request(job); if (!ret) return 0;
fail_rport_msg: /* return the errno failure code as the only status */
BUG_ON(job->reply_len < sizeof(uint32_t));
bsg_reply->reply_payload_rcv_len = 0;
bsg_reply->result = ret;
job->reply_len = sizeof(uint32_t);
bsg_job_done(job, bsg_reply->result,
bsg_reply->reply_payload_rcv_len); return 0;
}
/** * fc_bsg_rportadd - Create and add the bsg hooks so we can receive requests * @shost: shost that rport is attached to * @rport: rport that the bsg hooks are being attached to
*/ staticint
fc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport)
{ struct device *dev = &rport->dev; struct fc_internal *i = to_fc_internal(shost->transportt); struct queue_limits lim; struct request_queue *q;
/** * fc_bsg_remove - Deletes the bsg hooks on fchosts/rports * @q: the request_queue that is to be torn down. * * Notes: * Before unregistering the queue empty any requests that are blocked * *
*/ staticvoid
fc_bsg_remove(struct request_queue *q)
{
bsg_remove_queue(q);
}
/* Original Author: Martin Hicks */
MODULE_AUTHOR("James Smart");
MODULE_DESCRIPTION("FC Transport Attributes");
MODULE_LICENSE("GPL");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.