// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/etherdevice.h>
#include "ionic.h"
#include "ionic_dev.h"
#include "ionic_lif.h"
static void ionic_watchdog_cb(struct timer_list *t)
{
struct ionic *ionic = timer_container_of(ionic, t, watchdog_timer);
struct ionic_lif *lif = ionic->lif;
struct ionic_deferred_work *work;
int hb;
mod_timer(&ionic->watchdog_timer,
round_jiffies(jiffies + ionic->watchdog_period));
if (!lif)
return;
hb = ionic_heartbeat_check(ionic);
dev_dbg(ionic->dev, "%s: hb %d running %d UP %d\n",
__func__, hb, netif_running(lif->netdev),
test_bit(IONIC_LIF_F_UP, lif->state));
if (hb >= 0 &&
!test_bit(IONIC_LIF_F_FW_RESET, lif->state))
ionic_link_status_check_request(lif, CAN_NOT_SLEEP);
if (test_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state) &&
!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
work = kzalloc_obj(*work, GFP_ATOMIC);
if (!work) {
netdev_err(lif->netdev, "rxmode change dropped\n");
return;
}
work->type = IONIC_DW_TYPE_RX_MODE;
netdev_dbg(lif->netdev, "deferred: rx_mode\n");
ionic_lif_deferred_enqueue(lif, work);
}
}
static void ionic_napi_schedule_do_softirq(struct napi_struct *napi)
{
local_bh_disable();
napi_schedule(napi);
local_bh_enable();
}
void ionic_doorbell_napi_work(struct work_struct *work)
{
struct ionic_qcq *qcq = container_of(work, struct ionic_qcq,
doorbell_napi_work);
unsigned long now, then, dif;
now = READ_ONCE(jiffies);
then = qcq->q.dbell_jiffies;
dif = now - then;
if (dif > qcq->q.dbell_deadline)
ionic_napi_schedule_do_softirq(&qcq->napi);
}
static int ionic_get_preferred_cpu(struct ionic *ionic,
struct ionic_intr_info *intr)
{
int cpu;
cpu = cpumask_first_and(*intr->affinity_mask, cpu_online_mask);
if (cpu >= nr_cpu_ids)
cpu = cpumask_local_spread(0, dev_to_node(ionic->dev));
return cpu;
}
static void ionic_queue_dbell_napi_work(struct ionic *ionic,
struct ionic_qcq *qcq)
{
int cpu;
if (!(qcq->flags & IONIC_QCQ_F_INTR))
return;
cpu = ionic_get_preferred_cpu(ionic, &qcq->intr);
queue_work_on(cpu, ionic->wq, &qcq->doorbell_napi_work);
}
static void ionic_doorbell_check_dwork(struct work_struct *work)
{
struct ionic *ionic = container_of(work, struct ionic,
doorbell_check_dwork.work);
struct ionic_lif *lif = ionic->lif;
mutex_lock(&lif->queue_lock);
if (test_bit(IONIC_LIF_F_FW_STOPPING, lif->state) ||
test_bit(IONIC_LIF_F_FW_RESET, lif->state)) {
mutex_unlock(&lif->queue_lock);
return;
}
ionic_napi_schedule_do_softirq(&lif->adminqcq->napi);
if (test_bit(IONIC_LIF_F_UP, lif->state)) {