forked from Minki/linux
ca9033ba69
In the iowait structure, two iowait_work entries were included to queue a given object: one for normal IB operations, and the other for TID RDMA operations. For non-TID RDMA operations, the iowait_work structure for TID RDMA is initialized to contain a NULL function (not used). When the QP is reset, the function iowait_cancel_work will be called to cancel any pending work. The problem is that this function will call cancel_work_sync() for both iowait_work entries, even though the one for TID RDMA is not used at all. Eventually, the call cascades to __flush_work(), wherein a WARN_ON will be triggered due to the fact that work->func is NULL. The WARN_ON was introduced in commit4d43d395fe
("workqueue: Try to catch flush_work() without INIT_WORK().") This patch fixes the issue by making sure that a work function is present for TID RDMA before calling cancel_work_sync in iowait_cancel_work. Fixes:4d43d395fe
("workqueue: Try to catch flush_work() without INIT_WORK().") Fixes:5da0fc9dbf
("IB/hfi1: Prepare resource waits for dual leg") Link: https://lore.kernel.org/r/20191219211941.58387.39883.stgit@awfm-01.aw.intel.com Reviewed-by: Mike Marciniszyn <mike.marciniszyn@intel.com> Signed-off-by: Kaike Wan <kaike.wan@intel.com> Signed-off-by: Dennis Dalessandro <dennis.dalessandro@intel.com> Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
129 lines
3.3 KiB
C
129 lines
3.3 KiB
C
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
|
|
/*
|
|
* Copyright(c) 2018 Intel Corporation.
|
|
*
|
|
*/
|
|
#include "iowait.h"
|
|
#include "trace_iowait.h"
|
|
|
|
/* 1 priority == 16 starve_cnt */
|
|
#define IOWAIT_PRIORITY_STARVE_SHIFT 4
|
|
|
|
void iowait_set_flag(struct iowait *wait, u32 flag)
|
|
{
|
|
trace_hfi1_iowait_set(wait, flag);
|
|
set_bit(flag, &wait->flags);
|
|
}
|
|
|
|
bool iowait_flag_set(struct iowait *wait, u32 flag)
|
|
{
|
|
return test_bit(flag, &wait->flags);
|
|
}
|
|
|
|
inline void iowait_clear_flag(struct iowait *wait, u32 flag)
|
|
{
|
|
trace_hfi1_iowait_clear(wait, flag);
|
|
clear_bit(flag, &wait->flags);
|
|
}
|
|
|
|
/**
|
|
* iowait_init() - initialize wait structure
|
|
* @wait: wait struct to initialize
|
|
* @tx_limit: limit for overflow queuing
|
|
* @func: restart function for workqueue
|
|
* @sleep: sleep function for no space
|
|
* @resume: wakeup function for no space
|
|
*
|
|
* This function initializes the iowait
|
|
* structure embedded in the QP or PQ.
|
|
*
|
|
*/
|
|
void iowait_init(struct iowait *wait, u32 tx_limit,
|
|
void (*func)(struct work_struct *work),
|
|
void (*tidfunc)(struct work_struct *work),
|
|
int (*sleep)(struct sdma_engine *sde,
|
|
struct iowait_work *wait,
|
|
struct sdma_txreq *tx,
|
|
uint seq,
|
|
bool pkts_sent),
|
|
void (*wakeup)(struct iowait *wait, int reason),
|
|
void (*sdma_drained)(struct iowait *wait),
|
|
void (*init_priority)(struct iowait *wait))
|
|
{
|
|
int i;
|
|
|
|
wait->count = 0;
|
|
INIT_LIST_HEAD(&wait->list);
|
|
init_waitqueue_head(&wait->wait_dma);
|
|
init_waitqueue_head(&wait->wait_pio);
|
|
atomic_set(&wait->sdma_busy, 0);
|
|
atomic_set(&wait->pio_busy, 0);
|
|
wait->tx_limit = tx_limit;
|
|
wait->sleep = sleep;
|
|
wait->wakeup = wakeup;
|
|
wait->sdma_drained = sdma_drained;
|
|
wait->init_priority = init_priority;
|
|
wait->flags = 0;
|
|
for (i = 0; i < IOWAIT_SES; i++) {
|
|
wait->wait[i].iow = wait;
|
|
INIT_LIST_HEAD(&wait->wait[i].tx_head);
|
|
if (i == IOWAIT_IB_SE)
|
|
INIT_WORK(&wait->wait[i].iowork, func);
|
|
else
|
|
INIT_WORK(&wait->wait[i].iowork, tidfunc);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* iowait_cancel_work - cancel all work in iowait
|
|
* @w: the iowait struct
|
|
*/
|
|
void iowait_cancel_work(struct iowait *w)
|
|
{
|
|
cancel_work_sync(&iowait_get_ib_work(w)->iowork);
|
|
/* Make sure that the iowork for TID RDMA is used */
|
|
if (iowait_get_tid_work(w)->iowork.func)
|
|
cancel_work_sync(&iowait_get_tid_work(w)->iowork);
|
|
}
|
|
|
|
/**
|
|
* iowait_set_work_flag - set work flag based on leg
|
|
* @w - the iowait work struct
|
|
*/
|
|
int iowait_set_work_flag(struct iowait_work *w)
|
|
{
|
|
if (w == &w->iow->wait[IOWAIT_IB_SE]) {
|
|
iowait_set_flag(w->iow, IOWAIT_PENDING_IB);
|
|
return IOWAIT_IB_SE;
|
|
}
|
|
iowait_set_flag(w->iow, IOWAIT_PENDING_TID);
|
|
return IOWAIT_TID_SE;
|
|
}
|
|
|
|
/**
|
|
* iowait_priority_update_top - update the top priority entry
|
|
* @w: the iowait struct
|
|
* @top: a pointer to the top priority entry
|
|
* @idx: the index of the current iowait in an array
|
|
* @top_idx: the array index for the iowait entry that has the top priority
|
|
*
|
|
* This function is called to compare the priority of a given
|
|
* iowait with the given top priority entry. The top index will
|
|
* be returned.
|
|
*/
|
|
uint iowait_priority_update_top(struct iowait *w,
|
|
struct iowait *top,
|
|
uint idx, uint top_idx)
|
|
{
|
|
u8 cnt, tcnt;
|
|
|
|
/* Convert priority into starve_cnt and compare the total.*/
|
|
cnt = (w->priority << IOWAIT_PRIORITY_STARVE_SHIFT) + w->starved_cnt;
|
|
tcnt = (top->priority << IOWAIT_PRIORITY_STARVE_SHIFT) +
|
|
top->starved_cnt;
|
|
if (cnt > tcnt)
|
|
return idx;
|
|
else
|
|
return top_idx;
|
|
}
|