Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b3e09da authored by Dan Williams's avatar Dan Williams Committed by James Bottomley
Browse files

[SCSI] libsas: perform sas-transport resets in shost->workq context



Extend the sas transport class to allow transport users to attach extra
data to a sas_phy (->hostdata).  Use this area in libsas to move resets
to workq context in preparation for scheduling ata device resets through
libata-eh.

Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent b52df417
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@
#include "sas_internal.h"
#include "sas_dump.h"

static void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work)
void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work)
{
	if (!test_bit(SAS_HA_REGISTERED, &ha->state))
		return;
+58 −1
Original line number Diff line number Diff line
@@ -290,9 +290,66 @@ int sas_set_phy_speed(struct sas_phy *phy,
	return ret;
}

static void sas_phy_release(struct sas_phy *phy)
{
	kfree(phy->hostdata);
	phy->hostdata = NULL;
}

static void phy_reset_work(struct work_struct *work)
{
	struct sas_phy_data *d = container_of(work, typeof(*d), reset_work);

	d->reset_result = sas_phy_reset(d->phy, d->hard_reset);
}

static int sas_phy_setup(struct sas_phy *phy)
{
	struct sas_phy_data *d = kzalloc(sizeof(*d), GFP_KERNEL);

	if (!d)
		return -ENOMEM;

	mutex_init(&d->event_lock);
	INIT_WORK(&d->reset_work, phy_reset_work);
	d->phy = phy;
	phy->hostdata = d;

	return 0;
}

static int queue_phy_reset(struct sas_phy *phy, int hard_reset)
{
	struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
	struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
	struct sas_phy_data *d = phy->hostdata;
	int rc;

	if (!d)
		return -ENOMEM;

	/* libsas workqueue coordinates ata-eh reset with discovery */
	mutex_lock(&d->event_lock);
	d->reset_result = 0;
	d->hard_reset = hard_reset;

	spin_lock_irq(&ha->state_lock);
	sas_queue_work(ha, &d->reset_work);
	spin_unlock_irq(&ha->state_lock);

	rc = sas_drain_work(ha);
	if (rc == 0)
		rc = d->reset_result;
	mutex_unlock(&d->event_lock);

	return rc;
}

static struct sas_function_template sft = {
	.phy_enable = sas_phy_enable,
	.phy_reset = sas_phy_reset,
	.phy_reset = queue_phy_reset,
	.phy_setup = sas_phy_setup,
	.phy_release = sas_phy_release,
	.set_phy_speed = sas_set_phy_speed,
	.get_linkerrors = sas_get_linkerrors,
	.smp_handler = sas_smp_handler,
+10 −0
Original line number Diff line number Diff line
@@ -38,6 +38,15 @@
#define TO_SAS_TASK(_scsi_cmd)  ((void *)(_scsi_cmd)->host_scribble)
#define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0)

struct sas_phy_data {
	/* let reset be performed in sas_queue_work() context */
	struct sas_phy *phy;
	struct mutex event_lock;
	int hard_reset;
	int reset_result;
	struct work_struct reset_work;
};

void sas_scsi_recover_host(struct Scsi_Host *shost);

int sas_show_class(enum sas_class class, char *buf);
@@ -66,6 +75,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work);
void sas_porte_link_reset_err(struct work_struct *work);
void sas_porte_timer_event(struct work_struct *work);
void sas_porte_hard_reset(struct work_struct *work);
void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work);

int sas_notify_lldd_dev_found(struct domain_device *);
void sas_notify_lldd_dev_gone(struct domain_device *);
+17 −1
Original line number Diff line number Diff line
@@ -652,9 +652,21 @@ sas_phy_linkerror_attr(running_disparity_error_count);
sas_phy_linkerror_attr(loss_of_dword_sync_count);
sas_phy_linkerror_attr(phy_reset_problem_count);

static int sas_phy_setup(struct transport_container *tc, struct device *dev,
			 struct device *cdev)
{
	struct sas_phy *phy = dev_to_phy(dev);
	struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
	struct sas_internal *i = to_sas_internal(shost->transportt);

	if (i->f->phy_setup)
		i->f->phy_setup(phy);

	return 0;
}

static DECLARE_TRANSPORT_CLASS(sas_phy_class,
		"sas_phy", NULL, NULL, NULL);
		"sas_phy", sas_phy_setup, NULL, NULL);

static int sas_phy_match(struct attribute_container *cont, struct device *dev)
{
@@ -678,7 +690,11 @@ static int sas_phy_match(struct attribute_container *cont, struct device *dev)
static void sas_phy_release(struct device *dev)
{
	struct sas_phy *phy = dev_to_phy(dev);
	struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
	struct sas_internal *i = to_sas_internal(shost->transportt);

	if (i->f->phy_release)
		i->f->phy_release(phy);
	put_device(dev->parent);
	kfree(phy);
}
+4 −1
Original line number Diff line number Diff line
@@ -75,7 +75,8 @@ struct sas_phy {
	/* for the list of phys belonging to a port */
	struct list_head	port_siblings;

	struct work_struct      reset_work;
	/* available to the lldd */
	void			*hostdata;
};

#define dev_to_phy(d) \
@@ -169,6 +170,8 @@ struct sas_function_template {
	int (*get_bay_identifier)(struct sas_rphy *);
	int (*phy_reset)(struct sas_phy *, int);
	int (*phy_enable)(struct sas_phy *, int);
	int (*phy_setup)(struct sas_phy *);
	void (*phy_release)(struct sas_phy *);
	int (*set_phy_speed)(struct sas_phy *, struct sas_phy_linkrates *);
	int (*smp_handler)(struct Scsi_Host *, struct sas_rphy *, struct request *);
};