I'm announcing the release of the 4.19.281 kernel.
All users of the 4.19 kernel series must upgrade.
The updated 4.19.y git tree can be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git linux-4.19.y
and can be browsed at the normal kernel.org git web browser:
https://git.kernel.org/?p=linux/kernel/git/stable/linux-stable.git;a=summary
thanks,
greg k-h
------------
Documentation/sound/hd-audio/models.rst | 2
Makefile | 2
arch/arm64/kvm/guest.c | 83 +++++++++++++++++++-----
arch/x86/kernel/sysfb_efi.c | 8 ++
arch/x86/kvm/vmx/vmx.c | 10 ++
arch/x86/pci/fixup.c | 21 ++++++
crypto/asymmetric_keys/verify_pefile.c | 12 ++-
drivers/gpio/gpio-davinci.c | 2
drivers/hwtracing/coresight/coresight-etm4x.c | 2
drivers/i2c/busses/i2c-imx-lpi2c.c | 2
drivers/iio/dac/cio-dac.c | 4 -
drivers/mtd/mtdblock.c | 12 ++-
drivers/mtd/ubi/build.c | 21 ++++--
drivers/mtd/ubi/wl.c | 5 -
drivers/net/ethernet/cadence/macb_main.c | 4 +
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 8 ++
drivers/net/ethernet/sun/niu.c | 2
drivers/pinctrl/pinctrl-amd.c | 56 ++++++++++++----
drivers/power/supply/cros_usbpd-charger.c | 2
drivers/pwm/pwm-cros-ec.c | 1
drivers/scsi/ses.c | 20 ++---
drivers/tty/serial/sh-sci.c | 9 ++
drivers/usb/serial/cp210x.c | 1
drivers/usb/serial/option.c | 10 ++
drivers/watchdog/sbsa_gwdt.c | 1
fs/nfs/nfs4_fs.h | 2
fs/nfs/nfs4proc.c | 25 ++++---
fs/nfs/nfs4state.c | 8 +-
fs/nilfs2/segment.c | 3
fs/nilfs2/super.c | 2
fs/nilfs2/the_nilfs.c | 12 ++-
include/linux/ftrace.h | 2
kernel/cgroup/cpuset.c | 4 -
kernel/events/core.c | 2
kernel/trace/ring_buffer.c | 13 +++
mm/swapfile.c | 3
net/9p/trans_xen.c | 4 +
net/bluetooth/hidp/core.c | 2
net/bluetooth/l2cap_core.c | 24 +-----
net/core/netpoll.c | 19 +++++
net/ipv4/icmp.c | 5 +
net/ipv6/ip6_output.c | 7 +-
net/ipv6/udp.c | 8 +-
net/mac80211/sta_info.c | 3
net/sctp/socket.c | 4 +
net/sctp/stream_interleave.c | 3
sound/i2c/cs8427.c | 7 +-
sound/pci/emu10k1/emupcm.c | 4 -
sound/pci/hda/patch_realtek.c | 1
sound/pci/hda/patch_sigmatel.c | 10 ++
50 files changed, 349 insertions(+), 128 deletions(-)
Alexander Stein (1):
i2c: imx-lpi2c: clean rx/tx buffers upon new message
Bang Li (1):
mtdblock: tolerate corrected bit-flips
Basavaraj Natikar (1):
x86/PCI: Add quirk for AMD XHCI controller that loses MSI-X state in D3hot
Biju Das (2):
tty: serial: sh-sci: Fix transmit end interrupt handler
tty: serial: sh-sci: Fix Rx on RZ/G2L SCI
Bjørn Mork (1):
USB: serial: option: add Quectel RM500U-CN modem
Dave Martin (2):
KVM: arm64: Factor out core register ID enumeration
KVM: arm64: Filter out invalid core register IDs in KVM_GET_REG_LIST
Denis Plotnikov (1):
qlcnic: check pci_reset_function result
Dhruva Gole (1):
gpio: davinci: Add irq chip flag to skip set wake
Enrico Sau (1):
USB: serial: option: add Telit FE990 compositions
Eric Dumazet (2):
icmp: guard against too small mtu
udp6: fix potential access to stale information
Felix Fietkau (1):
wifi: mac80211: fix invalid drv_sta_pre_rcu_remove calls for non-uploaded sta
George Cherian (1):
watchdog: sbsa_wdog: Make sure the timeout programming is within the limits
Grant Grundler (1):
power: supply: cros_usbpd: reclassify "default case!" as debug
Greg Kroah-Hartman (1):
Linux 4.19.281
Hans de Goede (1):
efi: sysfb_efi: Add quirk for Lenovo Yoga Book X91F/L
Harshit Mogalapalli (1):
niu: Fix missing unwind goto in niu_alloc_channels()
Jakub Kicinski (1):
net: don't let netpoll invoke NAPI if in xmit context
Jeremy Soller (1):
ALSA: hda/realtek: Add quirk for Clevo X370SNW
Jiri Kosina (1):
scsi: ses: Handle enclosure with just a primary component gracefully
John Keeping (1):
ftrace: Mark get_lock_parent_ip() __always_inline
Kan Liang (1):
perf/core: Fix the same task check in perf_event_set_output
Kees Jan Koster (1):
USB: serial: cp210x: add Silicon Labs IFS-USB-DATACABLE IDs
Kornel Dulęba (2):
pinctrl: amd: Disable and mask interrupts on resume
Revert "pinctrl: amd: Disable and mask interrupts on resume"
Lee Jones (1):
mtd: ubi: wl: Fix a couple of kernel-doc issues
Linus Walleij (1):
pinctrl: amd: Use irqchip template
Luiz Augusto von Dentz (1):
Bluetooth: L2CAP: Fix use-after-free in l2cap_disconnect_{req,rsp}
Marc Zyngier (1):
arm64: KVM: Fix system register enumeration
Min Li (1):
Bluetooth: Fix race condition in hidp_session_thread
Oswald Buddenhagen (4):
ALSA: emu10k1: fix capture interrupt handler unlinking
ALSA: hda/sigmatel: add pin overrides for Intel DP45SG motherboard
ALSA: i2c/cs8427: fix iec958 mixer control deactivation
ALSA: hda/sigmatel: fix S/PDIF out on Intel D*45* motherboards
Paolo Bonzini (1):
KVM: nVMX: add missing consistency checks for CR0 and CR4
Robbie Harwood (1):
verify_pefile: relax wrapper length check
Roman Gushchin (1):
net: macb: fix a memory corruption in extended buffer descriptor mode
Rongwei Wang (1):
mm/swap: fix swap_info_struct race between swapoff and get_swap_pages()
Ryusuke Konishi (2):
nilfs2: fix potential UAF of struct nilfs_sc_info in nilfs_segctor_thread()
nilfs2: fix sysfs interface lifetime
Sachi King (1):
pinctrl: amd: disable and mask interrupts on probe
Sandeep Singh (1):
pinctrl: Added IRQF_SHARED flag for amd-pinctrl driver
Steve Clevenger (1):
coresight-etm4: Fix for() loop drvdata->nr_addr_cmp range bug
Trond Myklebust (3):
NFSv4: Convert struct nfs4_state to use refcount_t
NFSv4: Check the return value of update_open_stateid()
NFSv4: Fix hangs when recovering open state after a server reboot
Uwe Kleine-König (1):
pwm: cros-ec: Explicitly set .polarity in .get_state()
Waiman Long (1):
cgroup/cpuset: Wake up cpuset_attach_wq tasks in cpuset_cancel_attach()
William Breathitt Gray (1):
iio: dac: cio-dac: Fix max DAC write value check for 12-bit
Xin Long (2):
sctp: check send stream number after wait_for_sndbuf
sctp: fix a potential overflow in sctp_ifwdtsn_skip
ZhaoLong Wang (1):
ubi: Fix deadlock caused by recursively holding work_sem
Zheng Wang (1):
9p/xen : Fix use after free bug in xen_9pfs_front_remove due to race condition
Zheng Yejian (1):
ring-buffer: Fix race while reader and writer are on the same page
Zhihao Cheng (1):
ubi: Fix failure attaching when vid_hdr offset equals to (sub)page size
Ziyang Xuan (1):
ipv6: Fix an uninit variable access bug in __ip6_make_skb()
The following commit has been merged into the timers/core branch of tip:
Commit-ID: 2aaae4bf41b101f7e58e8b06778b1cd9a1dddf94
Gitweb: https://git.kernel.org/tip/2aaae4bf41b101f7e58e8b06778b1cd9a1dddf94
Author: Thomas Gleixner <tglx(a)linutronix.de>
AuthorDate: Mon, 17 Apr 2023 15:37:55 +02:00
Committer: Thomas Gleixner <tglx(a)linutronix.de>
CommitterDate: Thu, 20 Apr 2023 09:47:26 +02:00
posix-cpu-timers: Implement the missing timer_wait_running callback
For some unknown reason the introduction of the timer_wait_running callback
missed to fixup posix CPU timers, which went unnoticed for almost four years.
Marco reported recently that the WARN_ON() in timer_wait_running()
triggers with a posix CPU timer test case.
Posix CPU timers have two execution models for expiring timers depending on
CONFIG_POSIX_CPU_TIMERS_TASK_WORK:
1) If not enabled, the expiry happens in hard interrupt context so
spin waiting on the remote CPU is reasonably time bound.
Implement an empty stub function for that case.
2) If enabled, the expiry happens in task work before returning to user
space or guest mode. The expired timers are marked as firing and moved
from the timer queue to a local list head with sighand lock held. Once
the timers are moved, sighand lock is dropped and the expiry happens in
fully preemptible context. That means the expiring task can be scheduled
out, migrated, interrupted etc. So spin waiting on it is more than
suboptimal.
The timer wheel has a timer_wait_running() mechanism for RT, which uses
a per CPU timer-base expiry lock which is held by the expiry code and the
task waiting for the timer function to complete blocks on that lock.
This does not work in the same way for posix CPU timers as there is no
timer base and expiry for process wide timers can run on any task
belonging to that process, but the concept of waiting on an expiry lock
can be used too in a slightly different way:
- Add a mutex to struct posix_cputimers_work. This struct is per task
and used to schedule the expiry task work from the timer interrupt.
- Add a task_struct pointer to struct cpu_timer which is used to store
a the task which runs the expiry. That's filled in when the task
moves the expired timers to the local expiry list. That's not
affecting the size of the k_itimer union as there are bigger union
members already
- Let the task take the expiry mutex around the expiry function
- Let the waiter acquire a task reference with rcu_read_lock() held and
block on the expiry mutex
This avoids spin-waiting on a task which might not even be on a CPU and
works nicely for RT too.
Fixes: ec8f954a40da ("posix-timers: Use a callback for cancel synchronization on PREEMPT_RT")
Reported-by: Marco Elver <elver(a)google.com>
Signed-off-by: Thomas Gleixner <tglx(a)linutronix.de>
Tested-by: Marco Elver <elver(a)google.com>
Tested-by: Sebastian Andrzej Siewior <bigeasy(a)linutronix.de>
Reviewed-by: Frederic Weisbecker <frederic(a)kernel.org>
Cc: stable(a)vger.kernel.org
Link: https://lore.kernel.org/r/87zg764ojw.ffs@tglx
---
include/linux/posix-timers.h | 17 ++++---
kernel/time/posix-cpu-timers.c | 81 +++++++++++++++++++++++++++------
kernel/time/posix-timers.c | 4 ++-
3 files changed, 82 insertions(+), 20 deletions(-)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 2c6e99c..d607f51 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -4,6 +4,7 @@
#include <linux/spinlock.h>
#include <linux/list.h>
+#include <linux/mutex.h>
#include <linux/alarmtimer.h>
#include <linux/timerqueue.h>
@@ -62,16 +63,18 @@ static inline int clockid_to_fd(const clockid_t clk)
* cpu_timer - Posix CPU timer representation for k_itimer
* @node: timerqueue node to queue in the task/sig
* @head: timerqueue head on which this timer is queued
- * @task: Pointer to target task
+ * @pid: Pointer to target task PID
* @elist: List head for the expiry list
* @firing: Timer is currently firing
+ * @handling: Pointer to the task which handles expiry
*/
struct cpu_timer {
- struct timerqueue_node node;
- struct timerqueue_head *head;
- struct pid *pid;
- struct list_head elist;
- int firing;
+ struct timerqueue_node node;
+ struct timerqueue_head *head;
+ struct pid *pid;
+ struct list_head elist;
+ int firing;
+ struct task_struct __rcu *handling;
};
static inline bool cpu_timer_enqueue(struct timerqueue_head *head,
@@ -135,10 +138,12 @@ struct posix_cputimers {
/**
* posix_cputimers_work - Container for task work based posix CPU timer expiry
* @work: The task work to be scheduled
+ * @mutex: Mutex held around expiry in context of this task work
* @scheduled: @work has been scheduled already, no further processing
*/
struct posix_cputimers_work {
struct callback_head work;
+ struct mutex mutex;
unsigned int scheduled;
};
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index 2f5e9b3..93c5a19 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -846,6 +846,8 @@ static u64 collect_timerqueue(struct timerqueue_head *head,
return expires;
ctmr->firing = 1;
+ /* See posix_cpu_timer_wait_running() */
+ rcu_assign_pointer(ctmr->handling, current);
cpu_timer_dequeue(ctmr);
list_add_tail(&ctmr->elist, firing);
}
@@ -1161,7 +1163,49 @@ static void handle_posix_cpu_timers(struct task_struct *tsk);
#ifdef CONFIG_POSIX_CPU_TIMERS_TASK_WORK
static void posix_cpu_timers_work(struct callback_head *work)
{
+ struct posix_cputimers_work *cw = container_of(work, typeof(*cw), work);
+
+ mutex_lock(&cw->mutex);
handle_posix_cpu_timers(current);
+ mutex_unlock(&cw->mutex);
+}
+
+/*
+ * Invoked from the posix-timer core when a cancel operation failed because
+ * the timer is marked firing. The caller holds rcu_read_lock(), which
+ * protects the timer and the task which is expiring it from being freed.
+ */
+static void posix_cpu_timer_wait_running(struct k_itimer *timr)
+{
+ struct task_struct *tsk = rcu_dereference(timr->it.cpu.handling);
+
+ /* Has the handling task completed expiry already? */
+ if (!tsk)
+ return;
+
+ /* Ensure that the task cannot go away */
+ get_task_struct(tsk);
+ /* Now drop the RCU protection so the mutex can be locked */
+ rcu_read_unlock();
+ /* Wait on the expiry mutex */
+ mutex_lock(&tsk->posix_cputimers_work.mutex);
+ /* Release it immediately again. */
+ mutex_unlock(&tsk->posix_cputimers_work.mutex);
+ /* Drop the task reference. */
+ put_task_struct(tsk);
+ /* Relock RCU so the callsite is balanced */
+ rcu_read_lock();
+}
+
+static void posix_cpu_timer_wait_running_nsleep(struct k_itimer *timr)
+{
+ /* Ensure that timr->it.cpu.handling task cannot go away */
+ rcu_read_lock();
+ spin_unlock_irq(&timr->it_lock);
+ posix_cpu_timer_wait_running(timr);
+ rcu_read_unlock();
+ /* @timr is on stack and is valid */
+ spin_lock_irq(&timr->it_lock);
}
/*
@@ -1177,6 +1221,7 @@ void clear_posix_cputimers_work(struct task_struct *p)
sizeof(p->posix_cputimers_work.work));
init_task_work(&p->posix_cputimers_work.work,
posix_cpu_timers_work);
+ mutex_init(&p->posix_cputimers_work.mutex);
p->posix_cputimers_work.scheduled = false;
}
@@ -1255,6 +1300,18 @@ static inline void __run_posix_cpu_timers(struct task_struct *tsk)
lockdep_posixtimer_exit();
}
+static void posix_cpu_timer_wait_running(struct k_itimer *timr)
+{
+ cpu_relax();
+}
+
+static void posix_cpu_timer_wait_running_nsleep(struct k_itimer *timr)
+{
+ spin_unlock_irq(&timr.it_lock);
+ cpu_relax();
+ spin_lock_irq(&timr.it_lock);
+}
+
static inline bool posix_cpu_timers_work_scheduled(struct task_struct *tsk)
{
return false;
@@ -1363,6 +1420,8 @@ static void handle_posix_cpu_timers(struct task_struct *tsk)
*/
if (likely(cpu_firing >= 0))
cpu_timer_fire(timer);
+ /* See posix_cpu_timer_wait_running() */
+ rcu_assign_pointer(timer->it.cpu.handling, NULL);
spin_unlock(&timer->it_lock);
}
}
@@ -1497,23 +1556,16 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags,
expires = cpu_timer_getexpires(&timer.it.cpu);
error = posix_cpu_timer_set(&timer, 0, &zero_it, &it);
if (!error) {
- /*
- * Timer is now unarmed, deletion can not fail.
- */
+ /* Timer is now unarmed, deletion can not fail. */
posix_cpu_timer_del(&timer);
+ } else {
+ while (error == TIMER_RETRY) {
+ posix_cpu_timer_wait_running_nsleep(&timer);
+ error = posix_cpu_timer_del(&timer);
+ }
}
- spin_unlock_irq(&timer.it_lock);
- while (error == TIMER_RETRY) {
- /*
- * We need to handle case when timer was or is in the
- * middle of firing. In other cases we already freed
- * resources.
- */
- spin_lock_irq(&timer.it_lock);
- error = posix_cpu_timer_del(&timer);
- spin_unlock_irq(&timer.it_lock);
- }
+ spin_unlock_irq(&timer.it_lock);
if ((it.it_value.tv_sec | it.it_value.tv_nsec) == 0) {
/*
@@ -1623,6 +1675,7 @@ const struct k_clock clock_posix_cpu = {
.timer_del = posix_cpu_timer_del,
.timer_get = posix_cpu_timer_get,
.timer_rearm = posix_cpu_timer_rearm,
+ .timer_wait_running = posix_cpu_timer_wait_running,
};
const struct k_clock clock_process = {
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 0c8a87a..808a247 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -846,6 +846,10 @@ static struct k_itimer *timer_wait_running(struct k_itimer *timer,
rcu_read_lock();
unlock_timer(timer, *flags);
+ /*
+ * kc->timer_wait_running() might drop RCU lock. So @timer
+ * cannot be touched anymore after the function returns!
+ */
if (!WARN_ON_ONCE(!kc->timer_wait_running))
kc->timer_wait_running(timer);
From: Xiaoyu Li <xiaoyu.li(a)corigine.com>
Before the referenced commit, if fewer interrupts are supported by
hardware than requested, then pci_msix_vec_count() returned the
former. However, after the referenced commit, an error is returned
for this condition. This causes a regression in the NFP driver
preventing probe from completing.
This situation may occur because the firmware allows sharing of
more than one queue per interrupt vector. And, thus, it is valid for
the firmware to advertise the number of queues it does. However,
interrupt sharing is not currently implemented by the NFP driver as
it seems likely - though not tested - that any gains obtained by
having more queues would be mitigated by sharing of interrupts.
Address this problem by limiting the number of vectors requested to
the number supported by hardware.
Also, make correct the max/min_irq types. They were unsigned
previously but should be signed.
Fixes: bab65e48cb06 ("PCI/MSI: Sanitize MSI-X checks")
CC: stable(a)vger.kernel.org
Signed-off-by: Xiaoyu Li <xiaoyu.li(a)corigine.com>
Acked-by: Simon Horman <simon.horman(a)corigine.com>
Signed-off-by: Louis Peens <louis.peens(a)corigine.com>
---
Changes: V1-->V2
* Updated the max/min_irq types to be signed instead of unsigned
* Fixed formatting of commit message to be better aligned at 72 chars
* Also updated the commit message to better explain why this is even
possible to happen, in response to the question from V1.
drivers/net/ethernet/netronome/nfp/nfp_net.h | 4 ++--
drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 12 +++++++++---
drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 9 +++++----
drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c | 8 ++++----
4 files changed, 20 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h
index 939cfce15830..960f69325287 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h
@@ -971,9 +971,9 @@ int nfp_net_mbox_reconfig_and_unlock(struct nfp_net *nn, u32 mbox_cmd);
void nfp_net_mbox_reconfig_post(struct nfp_net *nn, u32 update);
int nfp_net_mbox_reconfig_wait_posted(struct nfp_net *nn);
-unsigned int
+int
nfp_net_irqs_alloc(struct pci_dev *pdev, struct msix_entry *irq_entries,
- unsigned int min_irqs, unsigned int want_irqs);
+ int min_irqs, int want_irqs);
void nfp_net_irqs_disable(struct pci_dev *pdev);
void
nfp_net_irqs_assign(struct nfp_net *nn, struct msix_entry *irq_entries,
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index 62f0bf91d1e1..ae309ea48356 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -362,14 +362,20 @@ int nfp_net_mbox_reconfig_and_unlock(struct nfp_net *nn, u32 mbox_cmd)
* @min_irqs: Minimal acceptable number of interrupts
* @wanted_irqs: Target number of interrupts to allocate
*
- * Return: Number of irqs obtained or 0 on error.
+ * Return: Number of irqs obtained or an errno.
*/
-unsigned int
+int
nfp_net_irqs_alloc(struct pci_dev *pdev, struct msix_entry *irq_entries,
- unsigned int min_irqs, unsigned int wanted_irqs)
+ int min_irqs, int wanted_irqs)
{
unsigned int i;
int got_irqs;
+ int max_irqs;
+
+ max_irqs = pci_msix_vec_count(pdev);
+ if (max_irqs < 0)
+ return max_irqs;
+ wanted_irqs = min_t(int, max_irqs, wanted_irqs);
for (i = 0; i < wanted_irqs; i++)
irq_entries[i].entry = i;
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c
index cbe4972ba104..c1ac380542b5 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c
@@ -222,7 +222,8 @@ static void nfp_net_pf_clean_vnic(struct nfp_pf *pf, struct nfp_net *nn)
static int nfp_net_pf_alloc_irqs(struct nfp_pf *pf)
{
- unsigned int wanted_irqs, num_irqs, vnics_left, irqs_left;
+ unsigned int vnics_left, irqs_left;
+ int wanted_irqs, num_irqs;
struct nfp_net *nn;
/* Get MSI-X vectors */
@@ -237,10 +238,10 @@ static int nfp_net_pf_alloc_irqs(struct nfp_pf *pf)
num_irqs = nfp_net_irqs_alloc(pf->pdev, pf->irq_entries,
NFP_NET_MIN_VNIC_IRQS * pf->num_vnics,
wanted_irqs);
- if (!num_irqs) {
- nfp_warn(pf->cpp, "Unable to allocate MSI-X vectors\n");
+ if (num_irqs < 0) {
+ nfp_warn(pf->cpp, "Unable to allocate MSI-X vectors (err=%d)\n", num_irqs);
kfree(pf->irq_entries);
- return -ENOMEM;
+ return num_irqs;
}
/* Distribute IRQs to vNICs */
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c
index e19bb0150cb5..5f89c7198606 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c
@@ -84,7 +84,7 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev,
u32 tx_bar_sz, rx_bar_sz;
int tx_bar_no, rx_bar_no;
struct nfp_net_vf *vf;
- unsigned int num_irqs;
+ int num_irqs;
u8 __iomem *ctrl_bar;
struct nfp_net *nn;
u32 startq;
@@ -255,9 +255,9 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev,
NFP_NET_MIN_VNIC_IRQS,
NFP_NET_NON_Q_VECTORS +
nn->dp.num_r_vecs);
- if (!num_irqs) {
- nn_warn(nn, "Unable to allocate MSI-X Vectors. Exiting\n");
- err = -EIO;
+ if (num_irqs < 0) {
+ nn_warn(nn, "Unable to allocate MSI-X Vectors. Exiting (err=%d)\n", num_irqs);
+ err = num_irqs;
goto err_unmap_rx;
}
nfp_net_irqs_assign(nn, vf->irq_entries, num_irqs);
--
2.34.1