Commit 3ad348d9 authored by James Smart's avatar James Smart Committed by Martin K. Petersen

scsi: lpfc: Fix oops when fewer hdwqs than cpus

When tearing down the adapter for a reset, online/offline, or driver
unload, the queue free routine would hit a GPF oops.  This only occurs on
conditions where the number of hardware queues created is fewer than the
number of cpus in the system. In this condition cpus share a hardware
queue. And of course, it's the 2nd cpu that shares a hardware that
attempted to free it a second time and hit the oops.

Fix by reworking the cpu to hardware queue mapping such that:
Assignment of hardware queues to cpus occur in two passes:
first pass: is first time assignment of a hardware queue to a cpu.
  This will set the LPFC_CPU_FIRST_IRQ flag for the cpu.
second pass: for cpus that did not get a hardware queue they will
  be assigned one from a primary cpu (one set in first pass).

Deletion of hardware queues is driven by cpu itteration, and queues
will only be deleted if the LPFC_CPU_FIRST_IRQ flag is set.

Also contains a few small cleanup fixes and a little better logging.
Signed-off-by: default avatarDick Kennedy <dick.kennedy@broadcom.com>
Signed-off-by: default avatarJames Smart <jsmart2021@gmail.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 4b0a42be
...@@ -8876,7 +8876,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba) ...@@ -8876,7 +8876,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
} }
qdesc->qe_valid = 1; qdesc->qe_valid = 1;
qdesc->hdwq = cpup->hdwq; qdesc->hdwq = cpup->hdwq;
qdesc->chann = cpu; /* First CPU this EQ is affinitised to */ qdesc->chann = cpu; /* First CPU this EQ is affinitized to */
qdesc->last_cpu = qdesc->chann; qdesc->last_cpu = qdesc->chann;
/* Save the allocated EQ in the Hardware Queue */ /* Save the allocated EQ in the Hardware Queue */
...@@ -10723,7 +10723,7 @@ lpfc_find_hyper(struct lpfc_hba *phba, int cpu, ...@@ -10723,7 +10723,7 @@ lpfc_find_hyper(struct lpfc_hba *phba, int cpu,
static void static void
lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors)
{ {
int i, cpu, idx, new_cpu, start_cpu, first_cpu; int i, cpu, idx, next_idx, new_cpu, start_cpu, first_cpu;
int max_phys_id, min_phys_id; int max_phys_id, min_phys_id;
int max_core_id, min_core_id; int max_core_id, min_core_id;
struct lpfc_vector_map_info *cpup; struct lpfc_vector_map_info *cpup;
...@@ -10765,8 +10765,8 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) ...@@ -10765,8 +10765,8 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors)
#endif #endif
lpfc_printf_log(phba, KERN_INFO, LOG_INIT, lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"3328 CPU physid %d coreid %d\n", "3328 CPU %d physid %d coreid %d flag x%x\n",
cpup->phys_id, cpup->core_id); cpu, cpup->phys_id, cpup->core_id, cpup->flag);
if (cpup->phys_id > max_phys_id) if (cpup->phys_id > max_phys_id)
max_phys_id = cpup->phys_id; max_phys_id = cpup->phys_id;
...@@ -10805,17 +10805,17 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) ...@@ -10805,17 +10805,17 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors)
cpup->eq = idx; cpup->eq = idx;
cpup->irq = pci_irq_vector(phba->pcidev, idx); cpup->irq = pci_irq_vector(phba->pcidev, idx);
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"3336 Set Affinity: CPU %d "
"irq %d eq %d\n",
cpu, cpup->irq, cpup->eq);
/* If this is the first CPU thats assigned to this /* If this is the first CPU thats assigned to this
* vector, set LPFC_CPU_FIRST_IRQ. * vector, set LPFC_CPU_FIRST_IRQ.
*/ */
if (!i) if (!i)
cpup->flag |= LPFC_CPU_FIRST_IRQ; cpup->flag |= LPFC_CPU_FIRST_IRQ;
i++; i++;
lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
"3336 Set Affinity: CPU %d "
"irq %d eq %d flag x%x\n",
cpu, cpup->irq, cpup->eq, cpup->flag);
} }
} }
...@@ -10929,69 +10929,103 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) ...@@ -10929,69 +10929,103 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors)
} }
} }
/* Assign hdwq indices that are unique across all cpus in the map
* that are also FIRST_CPUs.
*/
idx = 0;
for_each_present_cpu(cpu) {
cpup = &phba->sli4_hba.cpu_map[cpu];
/* Only FIRST IRQs get a hdwq index assignment. */
if (!(cpup->flag & LPFC_CPU_FIRST_IRQ))
continue;
/* 1 to 1, the first LPFC_CPU_FIRST_IRQ cpus to a unique hdwq */
cpup->hdwq = idx;
idx++;
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"3333 Set Affinity: CPU %d (phys %d core %d): "
"hdwq %d eq %d irq %d flg x%x\n",
cpu, cpup->phys_id, cpup->core_id,
cpup->hdwq, cpup->eq, cpup->irq, cpup->flag);
}
/* Finally we need to associate a hdwq with each cpu_map entry /* Finally we need to associate a hdwq with each cpu_map entry
* This will be 1 to 1 - hdwq to cpu, unless there are less * This will be 1 to 1 - hdwq to cpu, unless there are less
* hardware queues then CPUs. For that case we will just round-robin * hardware queues then CPUs. For that case we will just round-robin
* the available hardware queues as they get assigned to CPUs. * the available hardware queues as they get assigned to CPUs.
* The next_idx is the idx from the FIRST_CPU loop above to account
* for irq_chann < hdwq. The idx is used for round-robin assignments
* and needs to start at 0.
*/ */
idx = 0; next_idx = idx;
start_cpu = 0; start_cpu = 0;
idx = 0;
for_each_present_cpu(cpu) { for_each_present_cpu(cpu) {
cpup = &phba->sli4_hba.cpu_map[cpu]; cpup = &phba->sli4_hba.cpu_map[cpu];
if (idx >= phba->cfg_hdw_queue) {
/* We need to reuse a Hardware Queue for another CPU,
* so be smart about it and pick one that has its
* IRQ/EQ mapped to the same phys_id (CPU package).
* and core_id.
*/
new_cpu = start_cpu;
for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
new_cpup = &phba->sli4_hba.cpu_map[new_cpu];
if ((new_cpup->hdwq != LPFC_VECTOR_MAP_EMPTY) &&
(new_cpup->phys_id == cpup->phys_id) &&
(new_cpup->core_id == cpup->core_id))
goto found_hdwq;
new_cpu = cpumask_next(
new_cpu, cpu_present_mask);
if (new_cpu == nr_cpumask_bits)
new_cpu = first_cpu;
}
/* If we can't match both phys_id and core_id, /* FIRST cpus are already mapped. */
* settle for just a phys_id match. if (cpup->flag & LPFC_CPU_FIRST_IRQ)
*/ continue;
new_cpu = start_cpu;
for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) { /* If the cfg_irq_chann < cfg_hdw_queue, set the hdwq
new_cpup = &phba->sli4_hba.cpu_map[new_cpu]; * of the unassigned cpus to the next idx so that all
if ((new_cpup->hdwq != LPFC_VECTOR_MAP_EMPTY) && * hdw queues are fully utilized.
(new_cpup->phys_id == cpup->phys_id)) */
goto found_hdwq; if (next_idx < phba->cfg_hdw_queue) {
new_cpu = cpumask_next( cpup->hdwq = next_idx;
new_cpu, cpu_present_mask); next_idx++;
if (new_cpu == nr_cpumask_bits) continue;
new_cpu = first_cpu; }
/* Not a First CPU and all hdw_queues are used. Reuse a
* Hardware Queue for another CPU, so be smart about it
* and pick one that has its IRQ/EQ mapped to the same phys_id
* (CPU package) and core_id.
*/
new_cpu = start_cpu;
for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
new_cpup = &phba->sli4_hba.cpu_map[new_cpu];
if (new_cpup->hdwq != LPFC_VECTOR_MAP_EMPTY &&
new_cpup->phys_id == cpup->phys_id &&
new_cpup->core_id == cpup->core_id) {
goto found_hdwq;
} }
new_cpu = cpumask_next(new_cpu, cpu_present_mask);
if (new_cpu == nr_cpumask_bits)
new_cpu = first_cpu;
}
/* Otherwise just round robin on cfg_hdw_queue */ /* If we can't match both phys_id and core_id,
cpup->hdwq = idx % phba->cfg_hdw_queue; * settle for just a phys_id match.
goto logit; */
found_hdwq: new_cpu = start_cpu;
/* We found an available entry, copy the IRQ info */ for (i = 0; i < phba->sli4_hba.num_present_cpu; i++) {
start_cpu = cpumask_next(new_cpu, cpu_present_mask); new_cpup = &phba->sli4_hba.cpu_map[new_cpu];
if (start_cpu == nr_cpumask_bits) if (new_cpup->hdwq != LPFC_VECTOR_MAP_EMPTY &&
start_cpu = first_cpu; new_cpup->phys_id == cpup->phys_id)
cpup->hdwq = new_cpup->hdwq; goto found_hdwq;
} else {
/* 1 to 1, CPU to hdwq */ new_cpu = cpumask_next(new_cpu, cpu_present_mask);
cpup->hdwq = idx; if (new_cpu == nr_cpumask_bits)
new_cpu = first_cpu;
} }
logit:
/* Otherwise just round robin on cfg_hdw_queue */
cpup->hdwq = idx % phba->cfg_hdw_queue;
idx++;
goto logit;
found_hdwq:
/* We found an available entry, copy the IRQ info */
start_cpu = cpumask_next(new_cpu, cpu_present_mask);
if (start_cpu == nr_cpumask_bits)
start_cpu = first_cpu;
cpup->hdwq = new_cpup->hdwq;
logit:
lpfc_printf_log(phba, KERN_ERR, LOG_INIT, lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"3335 Set Affinity: CPU %d (phys %d core %d): " "3335 Set Affinity: CPU %d (phys %d core %d): "
"hdwq %d eq %d irq %d flg x%x\n", "hdwq %d eq %d irq %d flg x%x\n",
cpu, cpup->phys_id, cpup->core_id, cpu, cpup->phys_id, cpup->core_id,
cpup->hdwq, cpup->eq, cpup->irq, cpup->flag); cpup->hdwq, cpup->eq, cpup->irq, cpup->flag);
idx++;
} }
/* The cpu_map array will be used later during initialization /* The cpu_map array will be used later during initialization
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment