mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 14:21:47 +00:00
0558f33c06
In commit 87c8331fcf
("[SCSI] libsas: prevent domain rediscovery
competing with ata error handling") introduced disco mutex to prevent
rediscovery competing with ata error handling and put the whole
revalidation in the mutex. But the rphy add/remove needs to wait for the
error handling which also grabs the disco mutex. This may leads to dead
lock.So the probe and destruct event were introduce to do the rphy
add/remove asynchronously and out of the lock.
The asynchronously processed workers makes the whole discovery process
not atomic, the other events may interrupt the process. For example,
if a loss of signal event inserted before the probe event, the
sas_deform_port() is called and the port will be deleted.
And sas_port_delete() may run before the destruct event, but the
port-x:x is the top parent of end device or expander. This leads to
a kernel WARNING such as:
[ 82.042979] sysfs group 'power' not found for kobject 'phy-1:0:22'
[ 82.042983] ------------[ cut here ]------------
[ 82.042986] WARNING: CPU: 54 PID: 1714 at fs/sysfs/group.c:237
sysfs_remove_group+0x94/0xa0
[ 82.043059] Call trace:
[ 82.043082] [<ffff0000082e7624>] sysfs_remove_group+0x94/0xa0
[ 82.043085] [<ffff00000864e320>] dpm_sysfs_remove+0x60/0x70
[ 82.043086] [<ffff00000863ee10>] device_del+0x138/0x308
[ 82.043089] [<ffff00000869a2d0>] sas_phy_delete+0x38/0x60
[ 82.043091] [<ffff00000869a86c>] do_sas_phy_delete+0x6c/0x80
[ 82.043093] [<ffff00000863dc20>] device_for_each_child+0x58/0xa0
[ 82.043095] [<ffff000008696f80>] sas_remove_children+0x40/0x50
[ 82.043100] [<ffff00000869d1bc>] sas_destruct_devices+0x64/0xa0
[ 82.043102] [<ffff0000080e93bc>] process_one_work+0x1fc/0x4b0
[ 82.043104] [<ffff0000080e96c0>] worker_thread+0x50/0x490
[ 82.043105] [<ffff0000080f0364>] kthread+0xfc/0x128
[ 82.043107] [<ffff0000080836c0>] ret_from_fork+0x10/0x50
Make probe and destruct a direct call in the disco and revalidate function,
but put them outside the lock. The whole discovery or revalidate won't
be interrupted by other events. And the DISCE_PROBE and DISCE_DESTRUCT
event are deleted as a result of the direct call.
Introduce a new list to destruct the sas_port and put the port delete after
the destruct. This makes sure the right order of destroying the sysfs
kobject and fix the warning above.
In sas_ex_revalidate_domain() have a loop to find all broadcasted
device, and sometimes we have a chance to find the same expander twice.
Because the sas_port will be deleted at the end of the whole revalidate
process, sas_port with the same name cannot be added before this.
Otherwise the sysfs will complain of creating duplicate filename. Since
the LLDD will send broadcast for every device change, we can only
process one expander's revalidation.
[mkp: kbuild test robot warning]
Signed-off-by: Jason Yan <yanaijie@huawei.com>
CC: John Garry <john.garry@huawei.com>
CC: Johannes Thumshirn <jthumshirn@suse.de>
CC: Ewan Milne <emilne@redhat.com>
CC: Christoph Hellwig <hch@lst.de>
CC: Tomas Henzl <thenzl@redhat.com>
CC: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Hannes Reinecke <hare@suse.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
245 lines
6.8 KiB
C
245 lines
6.8 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef SCSI_TRANSPORT_SAS_H
|
|
#define SCSI_TRANSPORT_SAS_H
|
|
|
|
#include <linux/transport_class.h>
|
|
#include <linux/types.h>
|
|
#include <linux/mutex.h>
|
|
#include <scsi/sas.h>
|
|
#include <linux/bsg-lib.h>
|
|
|
|
struct scsi_transport_template;
|
|
struct sas_rphy;
|
|
struct request;
|
|
|
|
#if !IS_ENABLED(CONFIG_SCSI_SAS_ATTRS)
|
|
static inline int scsi_is_sas_rphy(const struct device *sdev)
|
|
{
|
|
return 0;
|
|
}
|
|
#else
|
|
extern int scsi_is_sas_rphy(const struct device *);
|
|
#endif
|
|
|
|
static inline int sas_protocol_ata(enum sas_protocol proto)
|
|
{
|
|
return ((proto & SAS_PROTOCOL_SATA) ||
|
|
(proto & SAS_PROTOCOL_STP))? 1 : 0;
|
|
}
|
|
|
|
enum sas_linkrate {
|
|
/* These Values are defined in the SAS standard */
|
|
SAS_LINK_RATE_UNKNOWN = 0,
|
|
SAS_PHY_DISABLED = 1,
|
|
SAS_PHY_RESET_PROBLEM = 2,
|
|
SAS_SATA_SPINUP_HOLD = 3,
|
|
SAS_SATA_PORT_SELECTOR = 4,
|
|
SAS_PHY_RESET_IN_PROGRESS = 5,
|
|
SAS_LINK_RATE_1_5_GBPS = 8,
|
|
SAS_LINK_RATE_G1 = SAS_LINK_RATE_1_5_GBPS,
|
|
SAS_LINK_RATE_3_0_GBPS = 9,
|
|
SAS_LINK_RATE_G2 = SAS_LINK_RATE_3_0_GBPS,
|
|
SAS_LINK_RATE_6_0_GBPS = 10,
|
|
SAS_LINK_RATE_12_0_GBPS = 11,
|
|
/* These are virtual to the transport class and may never
|
|
* be signalled normally since the standard defined field
|
|
* is only 4 bits */
|
|
SAS_LINK_RATE_FAILED = 0x10,
|
|
SAS_PHY_VIRTUAL = 0x11,
|
|
};
|
|
|
|
struct sas_identify {
|
|
enum sas_device_type device_type;
|
|
enum sas_protocol initiator_port_protocols;
|
|
enum sas_protocol target_port_protocols;
|
|
u64 sas_address;
|
|
u8 phy_identifier;
|
|
};
|
|
|
|
struct sas_phy {
|
|
struct device dev;
|
|
int number;
|
|
int enabled;
|
|
|
|
/* phy identification */
|
|
struct sas_identify identify;
|
|
|
|
/* phy attributes */
|
|
enum sas_linkrate negotiated_linkrate;
|
|
enum sas_linkrate minimum_linkrate_hw;
|
|
enum sas_linkrate minimum_linkrate;
|
|
enum sas_linkrate maximum_linkrate_hw;
|
|
enum sas_linkrate maximum_linkrate;
|
|
|
|
/* link error statistics */
|
|
u32 invalid_dword_count;
|
|
u32 running_disparity_error_count;
|
|
u32 loss_of_dword_sync_count;
|
|
u32 phy_reset_problem_count;
|
|
|
|
/* for the list of phys belonging to a port */
|
|
struct list_head port_siblings;
|
|
|
|
/* available to the lldd */
|
|
void *hostdata;
|
|
};
|
|
|
|
#define dev_to_phy(d) \
|
|
container_of((d), struct sas_phy, dev)
|
|
#define transport_class_to_phy(dev) \
|
|
dev_to_phy((dev)->parent)
|
|
#define phy_to_shost(phy) \
|
|
dev_to_shost((phy)->dev.parent)
|
|
|
|
struct request_queue;
|
|
struct sas_rphy {
|
|
struct device dev;
|
|
struct sas_identify identify;
|
|
struct list_head list;
|
|
struct request_queue *q;
|
|
u32 scsi_target_id;
|
|
};
|
|
|
|
#define dev_to_rphy(d) \
|
|
container_of((d), struct sas_rphy, dev)
|
|
#define transport_class_to_rphy(dev) \
|
|
dev_to_rphy((dev)->parent)
|
|
#define rphy_to_shost(rphy) \
|
|
dev_to_shost((rphy)->dev.parent)
|
|
#define target_to_rphy(targ) \
|
|
dev_to_rphy((targ)->dev.parent)
|
|
|
|
struct sas_end_device {
|
|
struct sas_rphy rphy;
|
|
/* flags */
|
|
unsigned ready_led_meaning:1;
|
|
unsigned tlr_supported:1;
|
|
unsigned tlr_enabled:1;
|
|
/* parameters */
|
|
u16 I_T_nexus_loss_timeout;
|
|
u16 initiator_response_timeout;
|
|
};
|
|
#define rphy_to_end_device(r) \
|
|
container_of((r), struct sas_end_device, rphy)
|
|
|
|
struct sas_expander_device {
|
|
int level;
|
|
int next_port_id;
|
|
|
|
#define SAS_EXPANDER_VENDOR_ID_LEN 8
|
|
char vendor_id[SAS_EXPANDER_VENDOR_ID_LEN+1];
|
|
#define SAS_EXPANDER_PRODUCT_ID_LEN 16
|
|
char product_id[SAS_EXPANDER_PRODUCT_ID_LEN+1];
|
|
#define SAS_EXPANDER_PRODUCT_REV_LEN 4
|
|
char product_rev[SAS_EXPANDER_PRODUCT_REV_LEN+1];
|
|
#define SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN 8
|
|
char component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN+1];
|
|
u16 component_id;
|
|
u8 component_revision_id;
|
|
|
|
struct sas_rphy rphy;
|
|
|
|
};
|
|
#define rphy_to_expander_device(r) \
|
|
container_of((r), struct sas_expander_device, rphy)
|
|
|
|
struct sas_port {
|
|
struct device dev;
|
|
|
|
int port_identifier;
|
|
int num_phys;
|
|
/* port flags */
|
|
unsigned int is_backlink:1;
|
|
|
|
/* the other end of the link */
|
|
struct sas_rphy *rphy;
|
|
|
|
struct mutex phy_list_mutex;
|
|
struct list_head phy_list;
|
|
struct list_head del_list; /* libsas only */
|
|
};
|
|
|
|
#define dev_to_sas_port(d) \
|
|
container_of((d), struct sas_port, dev)
|
|
#define transport_class_to_sas_port(dev) \
|
|
dev_to_sas_port((dev)->parent)
|
|
|
|
struct sas_phy_linkrates {
|
|
enum sas_linkrate maximum_linkrate;
|
|
enum sas_linkrate minimum_linkrate;
|
|
};
|
|
|
|
/* The functions by which the transport class and the driver communicate */
|
|
struct sas_function_template {
|
|
int (*get_linkerrors)(struct sas_phy *);
|
|
int (*get_enclosure_identifier)(struct sas_rphy *, u64 *);
|
|
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 *);
|
|
void (*smp_handler)(struct bsg_job *, struct Scsi_Host *,
|
|
struct sas_rphy *);
|
|
};
|
|
|
|
|
|
void sas_remove_children(struct device *);
|
|
extern void sas_remove_host(struct Scsi_Host *);
|
|
|
|
extern struct sas_phy *sas_phy_alloc(struct device *, int);
|
|
extern void sas_phy_free(struct sas_phy *);
|
|
extern int sas_phy_add(struct sas_phy *);
|
|
extern void sas_phy_delete(struct sas_phy *);
|
|
extern int scsi_is_sas_phy(const struct device *);
|
|
|
|
u64 sas_get_address(struct scsi_device *);
|
|
unsigned int sas_tlr_supported(struct scsi_device *);
|
|
unsigned int sas_is_tlr_enabled(struct scsi_device *);
|
|
void sas_disable_tlr(struct scsi_device *);
|
|
void sas_enable_tlr(struct scsi_device *);
|
|
|
|
extern struct sas_rphy *sas_end_device_alloc(struct sas_port *);
|
|
extern struct sas_rphy *sas_expander_alloc(struct sas_port *, enum sas_device_type);
|
|
void sas_rphy_free(struct sas_rphy *);
|
|
extern int sas_rphy_add(struct sas_rphy *);
|
|
extern void sas_rphy_remove(struct sas_rphy *);
|
|
extern void sas_rphy_delete(struct sas_rphy *);
|
|
extern void sas_rphy_unlink(struct sas_rphy *);
|
|
|
|
struct sas_port *sas_port_alloc(struct device *, int);
|
|
struct sas_port *sas_port_alloc_num(struct device *);
|
|
int sas_port_add(struct sas_port *);
|
|
void sas_port_free(struct sas_port *);
|
|
void sas_port_delete(struct sas_port *);
|
|
void sas_port_add_phy(struct sas_port *, struct sas_phy *);
|
|
void sas_port_delete_phy(struct sas_port *, struct sas_phy *);
|
|
void sas_port_mark_backlink(struct sas_port *);
|
|
int scsi_is_sas_port(const struct device *);
|
|
struct sas_phy *sas_port_get_phy(struct sas_port *port);
|
|
static inline void sas_port_put_phy(struct sas_phy *phy)
|
|
{
|
|
if (phy)
|
|
put_device(&phy->dev);
|
|
}
|
|
|
|
extern struct scsi_transport_template *
|
|
sas_attach_transport(struct sas_function_template *);
|
|
extern void sas_release_transport(struct scsi_transport_template *);
|
|
int sas_read_port_mode_page(struct scsi_device *);
|
|
|
|
static inline int
|
|
scsi_is_sas_expander_device(struct device *dev)
|
|
{
|
|
struct sas_rphy *rphy;
|
|
if (!scsi_is_sas_rphy(dev))
|
|
return 0;
|
|
rphy = dev_to_rphy(dev);
|
|
return rphy->identify.device_type == SAS_FANOUT_EXPANDER_DEVICE ||
|
|
rphy->identify.device_type == SAS_EDGE_EXPANDER_DEVICE;
|
|
}
|
|
|
|
#define scsi_is_sas_phy_local(phy) scsi_is_host_device((phy)->dev.parent)
|
|
|
|
#endif /* SCSI_TRANSPORT_SAS_H */
|