Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/nvme/host/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -5165,6 +5165,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
WRITE_ONCE(ctrl->state, NVME_CTRL_NEW);
ctrl->passthru_err_log_enabled = false;
clear_bit(NVME_CTRL_FAILFAST_EXPIRED, &ctrl->flags);
clear_bit(NVME_CTRL_MARGINAL, &ctrl->flags);
spin_lock_init(&ctrl->lock);
mutex_init(&ctrl->namespaces_lock);

Expand Down
20 changes: 20 additions & 0 deletions drivers/nvme/host/fc.c
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,10 @@ nvme_fc_ctrl_connectivity_loss(struct nvme_fc_ctrl *ctrl)
"Reconnect", ctrl->cnum);

set_bit(ASSOC_FAILED, &ctrl->flags);

/* clear 'marginal' flag as controller will be reset */
clear_bit(NVME_CTRL_MARGINAL, &ctrl->flags);

nvme_reset_ctrl(&ctrl->ctrl);
}

Expand Down Expand Up @@ -891,6 +895,22 @@ nvme_fc_set_remoteport_devloss(struct nvme_fc_remote_port *portptr,
}
EXPORT_SYMBOL_GPL(nvme_fc_set_remoteport_devloss);

void
nvme_fc_set_remoteport_fpin(struct nvme_fc_remote_port *portptr, bool marginal)
{
struct nvme_fc_rport *rport = remoteport_to_rport(portptr);
struct nvme_fc_ctrl *ctrl;

spin_lock_irq(&rport->lock);
list_for_each_entry(ctrl, &rport->ctrl_list, ctrl_list) {
if (marginal)
set_bit(NVME_CTRL_MARGINAL, &ctrl->ctrl.flags);
else
clear_bit(NVME_CTRL_MARGINAL, &ctrl->ctrl.flags);
}
spin_unlock_irq(&rport->lock);
}
EXPORT_SYMBOL_GPL(nvme_fc_set_remoteport_fpin);

/* *********************** FC-NVME DMA Handling **************************** */

Expand Down
122 changes: 110 additions & 12 deletions drivers/nvme/host/multipath.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,44 @@ static bool nvme_path_is_disabled(struct nvme_ns *ns)
return false;
}

/*
* Returns true if the new distance is better than the old one.
*/
static bool is_best_distance(bool found_is_marginal, bool marginal,
int old_distance, int distance)
{
if (found_is_marginal) {
if (marginal) {
/*
* A marginal path has already been found,
* or this is the first path found.
* This one is also marginal, but closer
* to the NUMA node, so prefer it.
*/
if (distance < old_distance)
return true;
} else {
/* Found a non-marginal path, use it over a marginal one. */
return true;
}
} else {
/* A non-marginal path has already found. This one is marginal, so skip it. */
if (marginal)
return false;

/* Found a closer non-marginal path, use it. */
if (distance < old_distance)
return true;
}

return false;
}

static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head, int node)
{
int found_distance = INT_MAX, fallback_distance = INT_MAX, distance;
struct nvme_ns *found = NULL, *fallback = NULL, *ns;
bool found_is_marginal = true, fallback_is_marginal = true;

list_for_each_entry_srcu(ns, &head->list, siblings,
srcu_read_lock_held(&head->srcu)) {
Expand All @@ -323,22 +357,34 @@ static struct nvme_ns *__nvme_find_path(struct nvme_ns_head *head, int node)

switch (ns->ana_state) {
case NVME_ANA_OPTIMIZED:
if (distance < found_distance) {
if (is_best_distance(found_is_marginal, nvme_ctrl_is_marginal(ns->ctrl),
found_distance, distance)) {
found_distance = distance;
found = ns;
found_is_marginal = nvme_ctrl_is_marginal(ns->ctrl);
}
break;
case NVME_ANA_NONOPTIMIZED:
if (distance < fallback_distance) {
if (is_best_distance(fallback_is_marginal, nvme_ctrl_is_marginal(ns->ctrl),
fallback_distance, distance)) {
fallback_distance = distance;
fallback = ns;
fallback_is_marginal = nvme_ctrl_is_marginal(ns->ctrl);
}
break;
default:
break;
}
}

/*
* Use non-optimized path only if it is not marginal
* and no optimized path is marginal.
*/
if (found_is_marginal && !fallback_is_marginal)
found = fallback;

/* No optimized path found, use the fallback */
if (!found)
found = fallback;
if (found)
Expand All @@ -359,6 +405,7 @@ static struct nvme_ns *nvme_next_ns(struct nvme_ns_head *head,
static struct nvme_ns *nvme_round_robin_path(struct nvme_ns_head *head)
{
struct nvme_ns *ns, *found = NULL;
bool found_is_marginal = true;
int node = numa_node_id();
struct nvme_ns *old = srcu_dereference(head->current_path[node],
&head->srcu);
Expand All @@ -379,22 +426,58 @@ static struct nvme_ns *nvme_round_robin_path(struct nvme_ns_head *head)
continue;

if (ns->ana_state == NVME_ANA_OPTIMIZED) {
if (found_is_marginal && nvme_ctrl_is_marginal(ns->ctrl)) {
/*
* A marginal path has already found,
* or this is the first path found.
* This one is also marginal, but optimized,
* so prefer it.
*/
found = ns;
found_is_marginal = 1;
continue;
}


/*
* A non-marginal path has already found.
* This one is marginal, so skip it.
*/
if (nvme_ctrl_is_marginal(ns->ctrl))
continue;

/* Found a non-marginal, optimized path use it. */
found = ns;
goto out;
}
if (ns->ana_state == NVME_ANA_NONOPTIMIZED)
if (ns->ana_state == NVME_ANA_NONOPTIMIZED) {
/*
* A path has already found. This one is marginal,
* so skip it.
*/
if (found && nvme_ctrl_is_marginal(ns->ctrl))
continue;
found = ns;
found_is_marginal = nvme_ctrl_is_marginal(ns->ctrl);
}
}

/*
* The loop above skips the current path for round-robin semantics.
* Fall back to the current path if either:
* - no other optimized path found and current is optimized,
* - no other non-marginal optimized path found and current is,
* optimized and not marginal.
* - no other usable path found and current is usable.
*/
if (!nvme_path_is_disabled(old) &&
(old->ana_state == NVME_ANA_OPTIMIZED ||
(!found && old->ana_state == NVME_ANA_NONOPTIMIZED)))
/* no other usable path found and current is usable. */
if (!nvme_path_is_disabled(old) && !found)
return old;
/*
* no other non-marginal optimized path found and current is,
* optimized and not marginal.
*/
if (!nvme_path_is_disabled(old) && !nvme_ctrl_is_marginal(old->ctrl) &&
(old->ana_state == NVME_ANA_OPTIMIZED || found_is_marginal))
return old;

if (!found)
Expand All @@ -407,7 +490,9 @@ static struct nvme_ns *nvme_round_robin_path(struct nvme_ns_head *head)
static struct nvme_ns *nvme_queue_depth_path(struct nvme_ns_head *head)
{
struct nvme_ns *best_opt = NULL, *best_nonopt = NULL, *ns;
unsigned int min_depth_opt = UINT_MAX, min_depth_nonopt = UINT_MAX;
int min_depth_opt = INT_MAX, min_depth_nonopt = INT_MAX;
bool opt_is_marginal = true, nonopt_is_marginal = true, marginal;

unsigned int depth;

list_for_each_entry_srcu(ns, &head->list, siblings,
Expand All @@ -416,35 +501,48 @@ static struct nvme_ns *nvme_queue_depth_path(struct nvme_ns_head *head)
continue;

depth = atomic_read(&ns->ctrl->nr_active);
marginal = nvme_ctrl_is_marginal(ns->ctrl);

switch (ns->ana_state) {
case NVME_ANA_OPTIMIZED:
if (depth < min_depth_opt) {
if (is_best_distance(opt_is_marginal, marginal,
min_depth_opt, depth)) {
min_depth_opt = depth;
best_opt = ns;
opt_is_marginal = marginal;
}
break;
case NVME_ANA_NONOPTIMIZED:
if (depth < min_depth_nonopt) {
if (is_best_distance(nonopt_is_marginal, marginal,
min_depth_nonopt, depth)) {
min_depth_nonopt = depth;
best_nonopt = ns;
nonopt_is_marginal = marginal;
}
break;
default:
break;
}

if (min_depth_opt == 0)
if (min_depth_opt == 0 && !opt_is_marginal)
return best_opt;
}

/*
* Prefer non-marginal non-optimized path
* over a marginal optimized path.
*/
if (opt_is_marginal && !nonopt_is_marginal && best_nonopt)
return best_nonopt;

return best_opt ? best_opt : best_nonopt;
}

static inline bool nvme_path_is_optimized(struct nvme_ns *ns)
{
return nvme_ctrl_state(ns->ctrl) == NVME_CTRL_LIVE &&
ns->ana_state == NVME_ANA_OPTIMIZED;
ns->ana_state == NVME_ANA_OPTIMIZED &&
!nvme_ctrl_is_marginal(ns->ctrl);
}

static struct nvme_ns *nvme_numa_path(struct nvme_ns_head *head)
Expand Down
6 changes: 6 additions & 0 deletions drivers/nvme/host/nvme.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ enum nvme_ctrl_flags {
NVME_CTRL_SKIP_ID_CNS_CS = 4,
NVME_CTRL_DIRTY_CAPABILITY = 5,
NVME_CTRL_FROZEN = 6,
NVME_CTRL_MARGINAL = 7,
};

struct nvme_ctrl {
Expand Down Expand Up @@ -479,6 +480,11 @@ static inline enum nvme_ctrl_state nvme_ctrl_state(struct nvme_ctrl *ctrl)
return READ_ONCE(ctrl->state);
}

static inline bool nvme_ctrl_is_marginal(struct nvme_ctrl *ctrl)
{
return test_bit(NVME_CTRL_MARGINAL, &ctrl->flags);
}

enum nvme_iopolicy {
NVME_IOPOLICY_NUMA,
NVME_IOPOLICY_RR,
Expand Down
4 changes: 3 additions & 1 deletion drivers/nvme/host/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,9 @@ static ssize_t nvme_sysfs_show_state(struct device *dev,
};

if (state < ARRAY_SIZE(state_name) && state_name[state])
return sysfs_emit(buf, "%s\n", state_name[state]);
return sysfs_emit(buf, "%s\n",
(nvme_ctrl_is_marginal(ctrl)) ? "marginal" :
state_name[state]);

return sysfs_emit(buf, "unknown state\n");
}
Expand Down
1 change: 1 addition & 0 deletions drivers/scsi/lpfc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
ccflags-$(GCOV) := -fprofile-arcs -ftest-coverage
ccflags-$(GCOV) += -O0

ccflags-y += -Wflex-array-member-not-at-end
ifdef WARNINGS_BECOME_ERRORS
ccflags-y += -Werror
endif
Expand Down
28 changes: 28 additions & 0 deletions drivers/scsi/lpfc/lpfc_attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -7134,6 +7134,30 @@ lpfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
#endif
}

static void
lpfc_set_rport_marginal(struct fc_rport *rport, bool marginal)
{
struct lpfc_rport_data *rdata = rport->dd_data;
struct lpfc_nodelist *ndlp = rdata->pnode;
struct lpfc_nvme_rport *nrport = NULL;

/* Break early if NVME_FC is not enabled */
if (!IS_ENABLED(CONFIG_NVME_FC))
return;

if (!ndlp) {
dev_info(&rport->dev, "Cannot find remote node to "
"set rport dev loss tmo, port_id x%x\n",
rport->port_id);
return;
}

nrport = lpfc_ndlp_get_nrport(ndlp);

if (nrport && nrport->remoteport)
nvme_fc_set_remoteport_fpin(nrport->remoteport, marginal);
}

/*
* lpfc_rport_show_function - Return rport target information
*
Expand Down Expand Up @@ -7244,6 +7268,8 @@ struct fc_function_template lpfc_transport_functions = {
.set_rport_dev_loss_tmo = lpfc_set_rport_loss_tmo,
.show_rport_dev_loss_tmo = 1,

.set_rport_marginal = lpfc_set_rport_marginal,

.get_starget_port_id = lpfc_get_starget_port_id,
.show_starget_port_id = 1,

Expand Down Expand Up @@ -7315,6 +7341,8 @@ struct fc_function_template lpfc_vport_transport_functions = {
.set_rport_dev_loss_tmo = lpfc_set_rport_loss_tmo,
.show_rport_dev_loss_tmo = 1,

.set_rport_marginal = lpfc_set_rport_marginal,

.get_starget_port_id = lpfc_get_starget_port_id,
.show_starget_port_id = 1,

Expand Down
Loading