Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
Pull SCSI updates from James Bottomley: "This is mostly updates of the usual driver suspects: arcmsr, scsi_debug, mpt3sas, lpfc, cxlflash, qla2xxx, aacraid, megaraid_sas, hisi_sas. We also have a rework of the libsas hotplug handling to make it more robust, a slew of 32 bit time conversions and fixes, and a host of the usual minor updates and style changes. The biggest potential for regressions is the libsas hotplug changes, but so far they seem stable under testing" * tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: (313 commits) scsi: qla2xxx: Fix logo flag for qlt_free_session_done() scsi: arcmsr: avoid do_gettimeofday scsi: core: Add VENDOR_SPECIFIC sense code definitions scsi: qedi: Drop cqe response during connection recovery scsi: fas216: fix sense buffer initialization scsi: ibmvfc: Remove unneeded semicolons scsi: hisi_sas: fix a bug in hisi_sas_dev_gone() scsi: hisi_sas: directly attached disk LED feature for v2 hw scsi: hisi_sas: devicetree: bindings: add LED feature for v2 hw scsi: megaraid_sas: NVMe passthrough command support scsi: megaraid: use ktime_get_real for firmware time scsi: fnic: use 64-bit timestamps scsi: qedf: Fix error return code in __qedf_probe() scsi: devinfo: fix format of the device list scsi: qla2xxx: Update driver version to 10.00.00.05-k scsi: qla2xxx: Add XCB counters to debugfs scsi: qla2xxx: Fix queue ID for async abort with Multiqueue scsi: qla2xxx: Fix warning for code intentation in __qla24xx_handle_gpdb_event() scsi: qla2xxx: Fix warning during port_name debug print scsi: qla2xxx: Fix warning in qla2x00_async_iocb_timeout() ...
This commit is contained in:
@@ -730,7 +730,6 @@ int sas_discover_sata(struct domain_device *dev)
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
sas_discover_event(dev->port, DISCE_PROBE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -212,13 +212,9 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
static void sas_probe_devices(struct work_struct *work)
|
||||
static void sas_probe_devices(struct asd_sas_port *port)
|
||||
{
|
||||
struct domain_device *dev, *n;
|
||||
struct sas_discovery_event *ev = to_sas_discovery_event(work);
|
||||
struct asd_sas_port *port = ev->port;
|
||||
|
||||
clear_bit(DISCE_PROBE, &port->disc.pending);
|
||||
|
||||
/* devices must be domain members before link recovery and probe */
|
||||
list_for_each_entry(dev, &port->disco_list, disco_list_node) {
|
||||
@@ -294,7 +290,6 @@ int sas_discover_end_dev(struct domain_device *dev)
|
||||
res = sas_notify_lldd_dev_found(dev);
|
||||
if (res)
|
||||
return res;
|
||||
sas_discover_event(dev->port, DISCE_PROBE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -353,13 +348,9 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d
|
||||
sas_put_device(dev);
|
||||
}
|
||||
|
||||
static void sas_destruct_devices(struct work_struct *work)
|
||||
void sas_destruct_devices(struct asd_sas_port *port)
|
||||
{
|
||||
struct domain_device *dev, *n;
|
||||
struct sas_discovery_event *ev = to_sas_discovery_event(work);
|
||||
struct asd_sas_port *port = ev->port;
|
||||
|
||||
clear_bit(DISCE_DESTRUCT, &port->disc.pending);
|
||||
|
||||
list_for_each_entry_safe(dev, n, &port->destroy_list, disco_list_node) {
|
||||
list_del_init(&dev->disco_list_node);
|
||||
@@ -370,6 +361,16 @@ static void sas_destruct_devices(struct work_struct *work)
|
||||
}
|
||||
}
|
||||
|
||||
static void sas_destruct_ports(struct asd_sas_port *port)
|
||||
{
|
||||
struct sas_port *sas_port, *p;
|
||||
|
||||
list_for_each_entry_safe(sas_port, p, &port->sas_port_del_list, del_list) {
|
||||
list_del_init(&sas_port->del_list);
|
||||
sas_port_delete(sas_port);
|
||||
}
|
||||
}
|
||||
|
||||
void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev)
|
||||
{
|
||||
if (!test_bit(SAS_DEV_DESTROY, &dev->state) &&
|
||||
@@ -384,7 +385,6 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev)
|
||||
if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) {
|
||||
sas_rphy_unlink(dev->rphy);
|
||||
list_move_tail(&dev->disco_list_node, &port->destroy_list);
|
||||
sas_discover_event(dev->port, DISCE_DESTRUCT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,6 +490,8 @@ static void sas_discover_domain(struct work_struct *work)
|
||||
port->port_dev = NULL;
|
||||
}
|
||||
|
||||
sas_probe_devices(port);
|
||||
|
||||
SAS_DPRINTK("DONE DISCOVERY on port %d, pid:%d, result:%d\n", port->id,
|
||||
task_pid_nr(current), error);
|
||||
}
|
||||
@@ -523,6 +525,10 @@ static void sas_revalidate_domain(struct work_struct *work)
|
||||
port->id, task_pid_nr(current), res);
|
||||
out:
|
||||
mutex_unlock(&ha->disco_mutex);
|
||||
|
||||
sas_destruct_devices(port);
|
||||
sas_destruct_ports(port);
|
||||
sas_probe_devices(port);
|
||||
}
|
||||
|
||||
/* ---------- Events ---------- */
|
||||
@@ -534,7 +540,7 @@ static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work *sw)
|
||||
* workqueue, or known to be submitted from a context that is
|
||||
* not racing against draining
|
||||
*/
|
||||
scsi_queue_work(ha->core.shost, &sw->work);
|
||||
queue_work(ha->disco_q, &sw->work);
|
||||
}
|
||||
|
||||
static void sas_chain_event(int event, unsigned long *pending,
|
||||
@@ -578,10 +584,8 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port)
|
||||
static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = {
|
||||
[DISCE_DISCOVER_DOMAIN] = sas_discover_domain,
|
||||
[DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain,
|
||||
[DISCE_PROBE] = sas_probe_devices,
|
||||
[DISCE_SUSPEND] = sas_suspend_devices,
|
||||
[DISCE_RESUME] = sas_resume_devices,
|
||||
[DISCE_DESTRUCT] = sas_destruct_devices,
|
||||
};
|
||||
|
||||
disc->pending = 0;
|
||||
|
@@ -29,7 +29,8 @@
|
||||
|
||||
int sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw)
|
||||
{
|
||||
int rc = 0;
|
||||
/* it's added to the defer_q when draining so return succeed */
|
||||
int rc = 1;
|
||||
|
||||
if (!test_bit(SAS_HA_REGISTERED, &ha->state))
|
||||
return 0;
|
||||
@@ -39,24 +40,20 @@ int sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw)
|
||||
if (list_empty(&sw->drain_node))
|
||||
list_add_tail(&sw->drain_node, &ha->defer_q);
|
||||
} else
|
||||
rc = scsi_queue_work(ha->core.shost, &sw->work);
|
||||
rc = queue_work(ha->event_q, &sw->work);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sas_queue_event(int event, unsigned long *pending,
|
||||
struct sas_work *work,
|
||||
static int sas_queue_event(int event, struct sas_work *work,
|
||||
struct sas_ha_struct *ha)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
if (!test_and_set_bit(event, pending)) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ha->lock, flags);
|
||||
rc = sas_queue_work(ha, work);
|
||||
spin_unlock_irqrestore(&ha->lock, flags);
|
||||
}
|
||||
spin_lock_irqsave(&ha->lock, flags);
|
||||
rc = sas_queue_work(ha, work);
|
||||
spin_unlock_irqrestore(&ha->lock, flags);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -64,21 +61,25 @@ static int sas_queue_event(int event, unsigned long *pending,
|
||||
|
||||
void __sas_drain_work(struct sas_ha_struct *ha)
|
||||
{
|
||||
struct workqueue_struct *wq = ha->core.shost->work_q;
|
||||
struct sas_work *sw, *_sw;
|
||||
int ret;
|
||||
|
||||
set_bit(SAS_HA_DRAINING, &ha->state);
|
||||
/* flush submitters */
|
||||
spin_lock_irq(&ha->lock);
|
||||
spin_unlock_irq(&ha->lock);
|
||||
|
||||
drain_workqueue(wq);
|
||||
drain_workqueue(ha->event_q);
|
||||
drain_workqueue(ha->disco_q);
|
||||
|
||||
spin_lock_irq(&ha->lock);
|
||||
clear_bit(SAS_HA_DRAINING, &ha->state);
|
||||
list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) {
|
||||
list_del_init(&sw->drain_node);
|
||||
sas_queue_work(ha, sw);
|
||||
ret = sas_queue_work(ha, sw);
|
||||
if (ret != 1)
|
||||
sas_free_event(to_asd_sas_event(&sw->work));
|
||||
|
||||
}
|
||||
spin_unlock_irq(&ha->lock);
|
||||
}
|
||||
@@ -115,33 +116,78 @@ void sas_enable_revalidation(struct sas_ha_struct *ha)
|
||||
struct asd_sas_port *port = ha->sas_port[i];
|
||||
const int ev = DISCE_REVALIDATE_DOMAIN;
|
||||
struct sas_discovery *d = &port->disc;
|
||||
struct asd_sas_phy *sas_phy;
|
||||
|
||||
if (!test_and_clear_bit(ev, &d->pending))
|
||||
continue;
|
||||
|
||||
sas_queue_event(ev, &d->pending, &d->disc_work[ev].work, ha);
|
||||
if (list_empty(&port->phy_list))
|
||||
continue;
|
||||
|
||||
sas_phy = container_of(port->phy_list.next, struct asd_sas_phy,
|
||||
port_phy_el);
|
||||
ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
|
||||
}
|
||||
mutex_unlock(&ha->disco_mutex);
|
||||
}
|
||||
|
||||
|
||||
static void sas_port_event_worker(struct work_struct *work)
|
||||
{
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
|
||||
sas_port_event_fns[ev->event](work);
|
||||
sas_free_event(ev);
|
||||
}
|
||||
|
||||
static void sas_phy_event_worker(struct work_struct *work)
|
||||
{
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
|
||||
sas_phy_event_fns[ev->event](work);
|
||||
sas_free_event(ev);
|
||||
}
|
||||
|
||||
static int sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event)
|
||||
{
|
||||
struct asd_sas_event *ev;
|
||||
struct sas_ha_struct *ha = phy->ha;
|
||||
int ret;
|
||||
|
||||
BUG_ON(event >= PORT_NUM_EVENTS);
|
||||
|
||||
return sas_queue_event(event, &phy->port_events_pending,
|
||||
&phy->port_events[event].work, ha);
|
||||
ev = sas_alloc_event(phy);
|
||||
if (!ev)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_SAS_EVENT(ev, sas_port_event_worker, phy, event);
|
||||
|
||||
ret = sas_queue_event(event, &ev->work, ha);
|
||||
if (ret != 1)
|
||||
sas_free_event(ev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event)
|
||||
{
|
||||
struct asd_sas_event *ev;
|
||||
struct sas_ha_struct *ha = phy->ha;
|
||||
int ret;
|
||||
|
||||
BUG_ON(event >= PHY_NUM_EVENTS);
|
||||
|
||||
return sas_queue_event(event, &phy->phy_events_pending,
|
||||
&phy->phy_events[event].work, ha);
|
||||
ev = sas_alloc_event(phy);
|
||||
if (!ev)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_SAS_EVENT(ev, sas_phy_event_worker, phy, event);
|
||||
|
||||
ret = sas_queue_event(event, &ev->work, ha);
|
||||
if (ret != 1)
|
||||
sas_free_event(ev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sas_init_events(struct sas_ha_struct *sas_ha)
|
||||
|
@@ -293,6 +293,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
|
||||
phy->phy->minimum_linkrate = dr->pmin_linkrate;
|
||||
phy->phy->maximum_linkrate = dr->pmax_linkrate;
|
||||
phy->phy->negotiated_linkrate = phy->linkrate;
|
||||
phy->phy->enabled = (phy->linkrate != SAS_PHY_DISABLED);
|
||||
|
||||
skip:
|
||||
if (new_phy)
|
||||
@@ -686,7 +687,7 @@ int sas_smp_get_phy_events(struct sas_phy *phy)
|
||||
res = smp_execute_task(dev, req, RPEL_REQ_SIZE,
|
||||
resp, RPEL_RESP_SIZE);
|
||||
|
||||
if (!res)
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
phy->invalid_dword_count = scsi_to_u32(&resp[12]);
|
||||
@@ -695,6 +696,7 @@ int sas_smp_get_phy_events(struct sas_phy *phy)
|
||||
phy->phy_reset_problem_count = scsi_to_u32(&resp[24]);
|
||||
|
||||
out:
|
||||
kfree(req);
|
||||
kfree(resp);
|
||||
return res;
|
||||
|
||||
@@ -1914,7 +1916,8 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent,
|
||||
sas_port_delete_phy(phy->port, phy->phy);
|
||||
sas_device_set_phy(found, phy->port);
|
||||
if (phy->port->num_phys == 0)
|
||||
sas_port_delete(phy->port);
|
||||
list_add_tail(&phy->port->del_list,
|
||||
&parent->port->sas_port_del_list);
|
||||
phy->port = NULL;
|
||||
}
|
||||
}
|
||||
@@ -2122,7 +2125,7 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev)
|
||||
struct domain_device *dev = NULL;
|
||||
|
||||
res = sas_find_bcast_dev(port_dev, &dev);
|
||||
while (res == 0 && dev) {
|
||||
if (res == 0 && dev) {
|
||||
struct expander_device *ex = &dev->ex_dev;
|
||||
int i = 0, phy_id;
|
||||
|
||||
@@ -2134,9 +2137,6 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev)
|
||||
res = sas_rediscover(dev, phy_id);
|
||||
i = phy_id + 1;
|
||||
} while (i < ex->num_phys);
|
||||
|
||||
dev = NULL;
|
||||
res = sas_find_bcast_dev(port_dev, &dev);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@@ -39,6 +39,7 @@
|
||||
#include "../scsi_sas_internal.h"
|
||||
|
||||
static struct kmem_cache *sas_task_cache;
|
||||
static struct kmem_cache *sas_event_cache;
|
||||
|
||||
struct sas_task *sas_alloc_task(gfp_t flags)
|
||||
{
|
||||
@@ -109,6 +110,7 @@ void sas_hash_addr(u8 *hashed, const u8 *sas_addr)
|
||||
|
||||
int sas_register_ha(struct sas_ha_struct *sas_ha)
|
||||
{
|
||||
char name[64];
|
||||
int error = 0;
|
||||
|
||||
mutex_init(&sas_ha->disco_mutex);
|
||||
@@ -122,6 +124,8 @@ int sas_register_ha(struct sas_ha_struct *sas_ha)
|
||||
INIT_LIST_HEAD(&sas_ha->defer_q);
|
||||
INIT_LIST_HEAD(&sas_ha->eh_dev_q);
|
||||
|
||||
sas_ha->event_thres = SAS_PHY_SHUTDOWN_THRES;
|
||||
|
||||
error = sas_register_phys(sas_ha);
|
||||
if (error) {
|
||||
printk(KERN_NOTICE "couldn't register sas phys:%d\n", error);
|
||||
@@ -140,10 +144,24 @@ int sas_register_ha(struct sas_ha_struct *sas_ha)
|
||||
goto Undo_ports;
|
||||
}
|
||||
|
||||
error = -ENOMEM;
|
||||
snprintf(name, sizeof(name), "%s_event_q", dev_name(sas_ha->dev));
|
||||
sas_ha->event_q = create_singlethread_workqueue(name);
|
||||
if (!sas_ha->event_q)
|
||||
goto Undo_ports;
|
||||
|
||||
snprintf(name, sizeof(name), "%s_disco_q", dev_name(sas_ha->dev));
|
||||
sas_ha->disco_q = create_singlethread_workqueue(name);
|
||||
if (!sas_ha->disco_q)
|
||||
goto Undo_event_q;
|
||||
|
||||
INIT_LIST_HEAD(&sas_ha->eh_done_q);
|
||||
INIT_LIST_HEAD(&sas_ha->eh_ata_q);
|
||||
|
||||
return 0;
|
||||
|
||||
Undo_event_q:
|
||||
destroy_workqueue(sas_ha->event_q);
|
||||
Undo_ports:
|
||||
sas_unregister_ports(sas_ha);
|
||||
Undo_phys:
|
||||
@@ -174,6 +192,9 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha)
|
||||
__sas_drain_work(sas_ha);
|
||||
mutex_unlock(&sas_ha->drain_mutex);
|
||||
|
||||
destroy_workqueue(sas_ha->disco_q);
|
||||
destroy_workqueue(sas_ha->event_q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -364,8 +385,6 @@ void sas_prep_resume_ha(struct sas_ha_struct *ha)
|
||||
struct asd_sas_phy *phy = ha->sas_phy[i];
|
||||
|
||||
memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
|
||||
phy->port_events_pending = 0;
|
||||
phy->phy_events_pending = 0;
|
||||
phy->frame_rcvd_size = 0;
|
||||
}
|
||||
}
|
||||
@@ -537,6 +556,37 @@ static struct sas_function_template sft = {
|
||||
.smp_handler = sas_smp_handler,
|
||||
};
|
||||
|
||||
static inline ssize_t phy_event_threshold_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct Scsi_Host *shost = class_to_shost(dev);
|
||||
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", sha->event_thres);
|
||||
}
|
||||
|
||||
static inline ssize_t phy_event_threshold_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct Scsi_Host *shost = class_to_shost(dev);
|
||||
struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
|
||||
|
||||
sha->event_thres = simple_strtol(buf, NULL, 10);
|
||||
|
||||
/* threshold cannot be set too small */
|
||||
if (sha->event_thres < 32)
|
||||
sha->event_thres = 32;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
DEVICE_ATTR(phy_event_threshold,
|
||||
S_IRUGO|S_IWUSR,
|
||||
phy_event_threshold_show,
|
||||
phy_event_threshold_store);
|
||||
EXPORT_SYMBOL_GPL(dev_attr_phy_event_threshold);
|
||||
|
||||
struct scsi_transport_template *
|
||||
sas_domain_attach_transport(struct sas_domain_function_template *dft)
|
||||
{
|
||||
@@ -555,20 +605,71 @@ sas_domain_attach_transport(struct sas_domain_function_template *dft)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sas_domain_attach_transport);
|
||||
|
||||
|
||||
struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy)
|
||||
{
|
||||
struct asd_sas_event *event;
|
||||
gfp_t flags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
|
||||
struct sas_ha_struct *sas_ha = phy->ha;
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(sas_ha->core.shost->transportt);
|
||||
|
||||
event = kmem_cache_zalloc(sas_event_cache, flags);
|
||||
if (!event)
|
||||
return NULL;
|
||||
|
||||
atomic_inc(&phy->event_nr);
|
||||
|
||||
if (atomic_read(&phy->event_nr) > phy->ha->event_thres) {
|
||||
if (i->dft->lldd_control_phy) {
|
||||
if (cmpxchg(&phy->in_shutdown, 0, 1) == 0) {
|
||||
sas_printk("The phy%02d bursting events, shut it down.\n",
|
||||
phy->id);
|
||||
sas_notify_phy_event(phy, PHYE_SHUTDOWN);
|
||||
}
|
||||
} else {
|
||||
/* Do not support PHY control, stop allocating events */
|
||||
WARN_ONCE(1, "PHY control not supported.\n");
|
||||
kmem_cache_free(sas_event_cache, event);
|
||||
atomic_dec(&phy->event_nr);
|
||||
event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void sas_free_event(struct asd_sas_event *event)
|
||||
{
|
||||
struct asd_sas_phy *phy = event->phy;
|
||||
|
||||
kmem_cache_free(sas_event_cache, event);
|
||||
atomic_dec(&phy->event_nr);
|
||||
}
|
||||
|
||||
/* ---------- SAS Class register/unregister ---------- */
|
||||
|
||||
static int __init sas_class_init(void)
|
||||
{
|
||||
sas_task_cache = KMEM_CACHE(sas_task, SLAB_HWCACHE_ALIGN);
|
||||
if (!sas_task_cache)
|
||||
return -ENOMEM;
|
||||
goto out;
|
||||
|
||||
sas_event_cache = KMEM_CACHE(asd_sas_event, SLAB_HWCACHE_ALIGN);
|
||||
if (!sas_event_cache)
|
||||
goto free_task_kmem;
|
||||
|
||||
return 0;
|
||||
free_task_kmem:
|
||||
kmem_cache_destroy(sas_task_cache);
|
||||
out:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void __exit sas_class_exit(void)
|
||||
{
|
||||
kmem_cache_destroy(sas_task_cache);
|
||||
kmem_cache_destroy(sas_event_cache);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Luben Tuikov <luben_tuikov@adaptec.com>");
|
||||
|
@@ -61,6 +61,9 @@ int sas_show_oob_mode(enum sas_oob_mode oob_mode, char *buf);
|
||||
int sas_register_phys(struct sas_ha_struct *sas_ha);
|
||||
void sas_unregister_phys(struct sas_ha_struct *sas_ha);
|
||||
|
||||
struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy);
|
||||
void sas_free_event(struct asd_sas_event *event);
|
||||
|
||||
int sas_register_ports(struct sas_ha_struct *sas_ha);
|
||||
void sas_unregister_ports(struct sas_ha_struct *sas_ha);
|
||||
|
||||
@@ -98,6 +101,10 @@ int sas_try_ata_reset(struct asd_sas_phy *phy);
|
||||
void sas_hae_reset(struct work_struct *work);
|
||||
|
||||
void sas_free_device(struct kref *kref);
|
||||
void sas_destruct_devices(struct asd_sas_port *port);
|
||||
|
||||
extern const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS];
|
||||
extern const work_func_t sas_port_event_fns[PORT_NUM_EVENTS];
|
||||
|
||||
#ifdef CONFIG_SCSI_SAS_HOST_SMP
|
||||
extern void sas_smp_host_handler(struct bsg_job *job, struct Scsi_Host *shost);
|
||||
|
@@ -35,7 +35,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work)
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
|
||||
clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending);
|
||||
phy->in_shutdown = 0;
|
||||
phy->error = 0;
|
||||
sas_deform_port(phy, 1);
|
||||
}
|
||||
@@ -45,7 +45,7 @@ static void sas_phye_oob_done(struct work_struct *work)
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
|
||||
clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending);
|
||||
phy->in_shutdown = 0;
|
||||
phy->error = 0;
|
||||
}
|
||||
|
||||
@@ -58,8 +58,6 @@ static void sas_phye_oob_error(struct work_struct *work)
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(sas_ha->core.shost->transportt);
|
||||
|
||||
clear_bit(PHYE_OOB_ERROR, &phy->phy_events_pending);
|
||||
|
||||
sas_deform_port(phy, 1);
|
||||
|
||||
if (!port && phy->enabled && i->dft->lldd_control_phy) {
|
||||
@@ -88,8 +86,6 @@ static void sas_phye_spinup_hold(struct work_struct *work)
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(sas_ha->core.shost->transportt);
|
||||
|
||||
clear_bit(PHYE_SPINUP_HOLD, &phy->phy_events_pending);
|
||||
|
||||
phy->error = 0;
|
||||
i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
|
||||
}
|
||||
@@ -99,8 +95,6 @@ static void sas_phye_resume_timeout(struct work_struct *work)
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
|
||||
clear_bit(PHYE_RESUME_TIMEOUT, &phy->phy_events_pending);
|
||||
|
||||
/* phew, lldd got the phy back in the nick of time */
|
||||
if (!phy->suspended) {
|
||||
dev_info(&phy->phy->dev, "resume timeout cancelled\n");
|
||||
@@ -113,45 +107,41 @@ static void sas_phye_resume_timeout(struct work_struct *work)
|
||||
}
|
||||
|
||||
|
||||
static void sas_phye_shutdown(struct work_struct *work)
|
||||
{
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
struct sas_ha_struct *sas_ha = phy->ha;
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(sas_ha->core.shost->transportt);
|
||||
|
||||
if (phy->enabled) {
|
||||
int ret;
|
||||
|
||||
phy->error = 0;
|
||||
phy->enabled = 0;
|
||||
ret = i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL);
|
||||
if (ret)
|
||||
sas_printk("lldd disable phy%02d returned %d\n",
|
||||
phy->id, ret);
|
||||
} else
|
||||
sas_printk("phy%02d is not enabled, cannot shutdown\n",
|
||||
phy->id);
|
||||
}
|
||||
|
||||
/* ---------- Phy class registration ---------- */
|
||||
|
||||
int sas_register_phys(struct sas_ha_struct *sas_ha)
|
||||
{
|
||||
int i;
|
||||
|
||||
static const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = {
|
||||
[PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal,
|
||||
[PHYE_OOB_DONE] = sas_phye_oob_done,
|
||||
[PHYE_OOB_ERROR] = sas_phye_oob_error,
|
||||
[PHYE_SPINUP_HOLD] = sas_phye_spinup_hold,
|
||||
[PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout,
|
||||
|
||||
};
|
||||
|
||||
static const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = {
|
||||
[PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed,
|
||||
[PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd,
|
||||
[PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err,
|
||||
[PORTE_TIMER_EVENT] = sas_porte_timer_event,
|
||||
[PORTE_HARD_RESET] = sas_porte_hard_reset,
|
||||
};
|
||||
|
||||
/* Now register the phys. */
|
||||
for (i = 0; i < sas_ha->num_phys; i++) {
|
||||
int k;
|
||||
struct asd_sas_phy *phy = sas_ha->sas_phy[i];
|
||||
|
||||
phy->error = 0;
|
||||
atomic_set(&phy->event_nr, 0);
|
||||
INIT_LIST_HEAD(&phy->port_phy_el);
|
||||
for (k = 0; k < PORT_NUM_EVENTS; k++) {
|
||||
INIT_SAS_WORK(&phy->port_events[k].work, sas_port_event_fns[k]);
|
||||
phy->port_events[k].phy = phy;
|
||||
}
|
||||
|
||||
for (k = 0; k < PHY_NUM_EVENTS; k++) {
|
||||
INIT_SAS_WORK(&phy->phy_events[k].work, sas_phy_event_fns[k]);
|
||||
phy->phy_events[k].phy = phy;
|
||||
}
|
||||
|
||||
phy->port = NULL;
|
||||
phy->ha = sas_ha;
|
||||
@@ -179,3 +169,12 @@ int sas_register_phys(struct sas_ha_struct *sas_ha)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = {
|
||||
[PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal,
|
||||
[PHYE_OOB_DONE] = sas_phye_oob_done,
|
||||
[PHYE_OOB_ERROR] = sas_phye_oob_error,
|
||||
[PHYE_SPINUP_HOLD] = sas_phye_spinup_hold,
|
||||
[PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout,
|
||||
[PHYE_SHUTDOWN] = sas_phye_shutdown,
|
||||
};
|
||||
|
@@ -66,6 +66,7 @@ static void sas_resume_port(struct asd_sas_phy *phy)
|
||||
rc = sas_notify_lldd_dev_found(dev);
|
||||
if (rc) {
|
||||
sas_unregister_dev(port, dev);
|
||||
sas_destruct_devices(port);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -192,6 +193,7 @@ static void sas_form_port(struct asd_sas_phy *phy)
|
||||
si->dft->lldd_port_formed(phy);
|
||||
|
||||
sas_discover_event(phy->port, DISCE_DISCOVER_DOMAIN);
|
||||
flush_workqueue(sas_ha->disco_q);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,6 +221,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone)
|
||||
|
||||
if (port->num_phys == 1) {
|
||||
sas_unregister_domain_devices(port, gone);
|
||||
sas_destruct_devices(port);
|
||||
sas_port_delete(port->port);
|
||||
port->port = NULL;
|
||||
} else {
|
||||
@@ -261,8 +264,6 @@ void sas_porte_bytes_dmaed(struct work_struct *work)
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
|
||||
clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending);
|
||||
|
||||
sas_form_port(phy);
|
||||
}
|
||||
|
||||
@@ -273,14 +274,15 @@ void sas_porte_broadcast_rcvd(struct work_struct *work)
|
||||
unsigned long flags;
|
||||
u32 prim;
|
||||
|
||||
clear_bit(PORTE_BROADCAST_RCVD, &phy->port_events_pending);
|
||||
|
||||
spin_lock_irqsave(&phy->sas_prim_lock, flags);
|
||||
prim = phy->sas_prim;
|
||||
spin_unlock_irqrestore(&phy->sas_prim_lock, flags);
|
||||
|
||||
SAS_DPRINTK("broadcast received: %d\n", prim);
|
||||
sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN);
|
||||
|
||||
if (phy->port)
|
||||
flush_workqueue(phy->port->ha->disco_q);
|
||||
}
|
||||
|
||||
void sas_porte_link_reset_err(struct work_struct *work)
|
||||
@@ -288,8 +290,6 @@ void sas_porte_link_reset_err(struct work_struct *work)
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
|
||||
clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending);
|
||||
|
||||
sas_deform_port(phy, 1);
|
||||
}
|
||||
|
||||
@@ -298,8 +298,6 @@ void sas_porte_timer_event(struct work_struct *work)
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
|
||||
clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending);
|
||||
|
||||
sas_deform_port(phy, 1);
|
||||
}
|
||||
|
||||
@@ -308,8 +306,6 @@ void sas_porte_hard_reset(struct work_struct *work)
|
||||
struct asd_sas_event *ev = to_asd_sas_event(work);
|
||||
struct asd_sas_phy *phy = ev->phy;
|
||||
|
||||
clear_bit(PORTE_HARD_RESET, &phy->port_events_pending);
|
||||
|
||||
sas_deform_port(phy, 1);
|
||||
}
|
||||
|
||||
@@ -323,6 +319,7 @@ static void sas_init_port(struct asd_sas_port *port,
|
||||
INIT_LIST_HEAD(&port->dev_list);
|
||||
INIT_LIST_HEAD(&port->disco_list);
|
||||
INIT_LIST_HEAD(&port->destroy_list);
|
||||
INIT_LIST_HEAD(&port->sas_port_del_list);
|
||||
spin_lock_init(&port->phy_list_lock);
|
||||
INIT_LIST_HEAD(&port->phy_list);
|
||||
port->ha = sas_ha;
|
||||
@@ -353,3 +350,11 @@ void sas_unregister_ports(struct sas_ha_struct *sas_ha)
|
||||
sas_deform_port(sas_ha->sas_phy[i], 0);
|
||||
|
||||
}
|
||||
|
||||
const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = {
|
||||
[PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed,
|
||||
[PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd,
|
||||
[PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err,
|
||||
[PORTE_TIMER_EVENT] = sas_porte_timer_event,
|
||||
[PORTE_HARD_RESET] = sas_porte_hard_reset,
|
||||
};
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "sas_internal.h"
|
||||
|
||||
@@ -959,21 +960,6 @@ void sas_target_destroy(struct scsi_target *starget)
|
||||
sas_put_device(found_dev);
|
||||
}
|
||||
|
||||
static void sas_parse_addr(u8 *sas_addr, const char *p)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < SAS_ADDR_SIZE; i++) {
|
||||
u8 h, l;
|
||||
if (!*p)
|
||||
break;
|
||||
h = isdigit(*p) ? *p-'0' : toupper(*p)-'A'+10;
|
||||
p++;
|
||||
l = isdigit(*p) ? *p-'0' : toupper(*p)-'A'+10;
|
||||
p++;
|
||||
sas_addr[i] = (h<<4) | l;
|
||||
}
|
||||
}
|
||||
|
||||
#define SAS_STRING_ADDR_SIZE 16
|
||||
|
||||
int sas_request_addr(struct Scsi_Host *shost, u8 *addr)
|
||||
@@ -990,7 +976,9 @@ int sas_request_addr(struct Scsi_Host *shost, u8 *addr)
|
||||
goto out;
|
||||
}
|
||||
|
||||
sas_parse_addr(addr, fw->data);
|
||||
res = hex2bin(addr, fw->data, strnlen(fw->data, SAS_ADDR_SIZE * 2) / 2);
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
release_firmware(fw);
|
||||
|
Reference in New Issue
Block a user