liquidio: Per queue oom work queue
Removed oom task unconditional rescheduling every 250ms and created per queue oom work queue for refilling buffers. The oom task refills only if the available descriptors is fallen to 64. There will be no packets coming in after hitting this level. So NAPI will not run until oom task refills the buffers. Signed-off-by: Intiyaz Basha <intiyaz.basha@cavium.com> Acked-by: Derek Chickles <derek.chickles@cavium.com> Signed-off-by: Felix Manlunas <felix.manlunas@cavium.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
fe2d22b172
commit
4b6e326b2d
|
@ -425,56 +425,73 @@ void octeon_pf_changed_vf_macaddr(struct octeon_device *oct, u8 *mac)
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void octeon_schedule_rxq_oom_work(struct octeon_device *oct,
|
||||||
|
struct octeon_droq *droq)
|
||||||
|
{
|
||||||
|
struct net_device *netdev = oct->props[0].netdev;
|
||||||
|
struct lio *lio = GET_LIO(netdev);
|
||||||
|
struct cavium_wq *wq = &lio->rxq_status_wq[droq->q_no];
|
||||||
|
|
||||||
|
queue_delayed_work(wq->wq, &wq->wk.work,
|
||||||
|
msecs_to_jiffies(LIO_OOM_POLL_INTERVAL_MS));
|
||||||
|
}
|
||||||
|
|
||||||
static void octnet_poll_check_rxq_oom_status(struct work_struct *work)
|
static void octnet_poll_check_rxq_oom_status(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct cavium_wk *wk = (struct cavium_wk *)work;
|
struct cavium_wk *wk = (struct cavium_wk *)work;
|
||||||
struct lio *lio = (struct lio *)wk->ctxptr;
|
struct lio *lio = (struct lio *)wk->ctxptr;
|
||||||
struct octeon_device *oct = lio->oct_dev;
|
struct octeon_device *oct = lio->oct_dev;
|
||||||
struct octeon_droq *droq;
|
int q_no = wk->ctxul;
|
||||||
int q, q_no = 0;
|
struct octeon_droq *droq = oct->droq[q_no];
|
||||||
|
|
||||||
if (ifstate_check(lio, LIO_IFSTATE_RUNNING)) {
|
if (!ifstate_check(lio, LIO_IFSTATE_RUNNING) || !droq)
|
||||||
for (q = 0; q < lio->linfo.num_rxpciq; q++) {
|
return;
|
||||||
q_no = lio->linfo.rxpciq[q].s.q_no;
|
|
||||||
droq = oct->droq[q_no];
|
if (octeon_retry_droq_refill(droq))
|
||||||
if (!droq)
|
octeon_schedule_rxq_oom_work(oct, droq);
|
||||||
continue;
|
|
||||||
octeon_droq_check_oom(droq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
queue_delayed_work(lio->rxq_status_wq.wq,
|
|
||||||
&lio->rxq_status_wq.wk.work,
|
|
||||||
msecs_to_jiffies(LIO_OOM_POLL_INTERVAL_MS));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int setup_rx_oom_poll_fn(struct net_device *netdev)
|
int setup_rx_oom_poll_fn(struct net_device *netdev)
|
||||||
{
|
{
|
||||||
struct lio *lio = GET_LIO(netdev);
|
struct lio *lio = GET_LIO(netdev);
|
||||||
struct octeon_device *oct = lio->oct_dev;
|
struct octeon_device *oct = lio->oct_dev;
|
||||||
|
struct cavium_wq *wq;
|
||||||
|
int q, q_no;
|
||||||
|
|
||||||
lio->rxq_status_wq.wq = alloc_workqueue("rxq-oom-status",
|
for (q = 0; q < oct->num_oqs; q++) {
|
||||||
|
q_no = lio->linfo.rxpciq[q].s.q_no;
|
||||||
|
wq = &lio->rxq_status_wq[q_no];
|
||||||
|
wq->wq = alloc_workqueue("rxq-oom-status",
|
||||||
WQ_MEM_RECLAIM, 0);
|
WQ_MEM_RECLAIM, 0);
|
||||||
if (!lio->rxq_status_wq.wq) {
|
if (!wq->wq) {
|
||||||
dev_err(&oct->pci_dev->dev, "unable to create cavium rxq oom status wq\n");
|
dev_err(&oct->pci_dev->dev, "unable to create cavium rxq oom status wq\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
INIT_DELAYED_WORK(&lio->rxq_status_wq.wk.work,
|
|
||||||
|
INIT_DELAYED_WORK(&wq->wk.work,
|
||||||
octnet_poll_check_rxq_oom_status);
|
octnet_poll_check_rxq_oom_status);
|
||||||
lio->rxq_status_wq.wk.ctxptr = lio;
|
wq->wk.ctxptr = lio;
|
||||||
queue_delayed_work(lio->rxq_status_wq.wq,
|
wq->wk.ctxul = q_no;
|
||||||
&lio->rxq_status_wq.wk.work,
|
}
|
||||||
msecs_to_jiffies(LIO_OOM_POLL_INTERVAL_MS));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanup_rx_oom_poll_fn(struct net_device *netdev)
|
void cleanup_rx_oom_poll_fn(struct net_device *netdev)
|
||||||
{
|
{
|
||||||
struct lio *lio = GET_LIO(netdev);
|
struct lio *lio = GET_LIO(netdev);
|
||||||
|
struct octeon_device *oct = lio->oct_dev;
|
||||||
|
struct cavium_wq *wq;
|
||||||
|
int q_no;
|
||||||
|
|
||||||
if (lio->rxq_status_wq.wq) {
|
for (q_no = 0; q_no < oct->num_oqs; q_no++) {
|
||||||
cancel_delayed_work_sync(&lio->rxq_status_wq.wk.work);
|
wq = &lio->rxq_status_wq[q_no];
|
||||||
flush_workqueue(lio->rxq_status_wq.wq);
|
if (wq->wq) {
|
||||||
destroy_workqueue(lio->rxq_status_wq.wq);
|
cancel_delayed_work_sync(&wq->wk.work);
|
||||||
|
flush_workqueue(wq->wq);
|
||||||
|
destroy_workqueue(wq->wq);
|
||||||
|
wq->wq = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1115,6 +1115,8 @@ static int lio_reset_queues(struct net_device *netdev, uint32_t num_qs)
|
||||||
* steps like updating sriov_info for the octeon device need to be done.
|
* steps like updating sriov_info for the octeon device need to be done.
|
||||||
*/
|
*/
|
||||||
if (queue_count_update) {
|
if (queue_count_update) {
|
||||||
|
cleanup_rx_oom_poll_fn(netdev);
|
||||||
|
|
||||||
lio_delete_glists(lio);
|
lio_delete_glists(lio);
|
||||||
|
|
||||||
/* Delete mbox for PF which is SRIOV disabled because sriov_info
|
/* Delete mbox for PF which is SRIOV disabled because sriov_info
|
||||||
|
@ -1214,6 +1216,11 @@ static int lio_reset_queues(struct net_device *netdev, uint32_t num_qs)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setup_rx_oom_poll_fn(netdev)) {
|
||||||
|
dev_err(&oct->pci_dev->dev, "lio_setup_rx_oom_poll_fn failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Send firmware the information about new number of queues
|
/* Send firmware the information about new number of queues
|
||||||
* if the interface is a VF or a PF that is SRIOV enabled.
|
* if the interface is a VF or a PF that is SRIOV enabled.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -333,8 +333,6 @@ int octeon_init_droq(struct octeon_device *oct,
|
||||||
* Returns:
|
* Returns:
|
||||||
* Success: Pointer to recv_info_t
|
* Success: Pointer to recv_info_t
|
||||||
* Failure: NULL.
|
* Failure: NULL.
|
||||||
* Locks:
|
|
||||||
* The droq->lock is held when this routine is called.
|
|
||||||
*/
|
*/
|
||||||
static inline struct octeon_recv_info *octeon_create_recv_info(
|
static inline struct octeon_recv_info *octeon_create_recv_info(
|
||||||
struct octeon_device *octeon_dev,
|
struct octeon_device *octeon_dev,
|
||||||
|
@ -433,8 +431,6 @@ octeon_droq_refill_pullup_descs(struct octeon_droq *droq,
|
||||||
* up buffers (that were not dispatched) to form a contiguous ring.
|
* up buffers (that were not dispatched) to form a contiguous ring.
|
||||||
* Returns:
|
* Returns:
|
||||||
* No of descriptors refilled.
|
* No of descriptors refilled.
|
||||||
* Locks:
|
|
||||||
* This routine is called with droq->lock held.
|
|
||||||
*/
|
*/
|
||||||
static u32
|
static u32
|
||||||
octeon_droq_refill(struct octeon_device *octeon_dev, struct octeon_droq *droq)
|
octeon_droq_refill(struct octeon_device *octeon_dev, struct octeon_droq *droq)
|
||||||
|
@ -449,8 +445,7 @@ octeon_droq_refill(struct octeon_device *octeon_dev, struct octeon_droq *droq)
|
||||||
|
|
||||||
while (droq->refill_count && (desc_refilled < droq->max_count)) {
|
while (droq->refill_count && (desc_refilled < droq->max_count)) {
|
||||||
/* If a valid buffer exists (happens if there is no dispatch),
|
/* If a valid buffer exists (happens if there is no dispatch),
|
||||||
* reuse
|
* reuse the buffer, else allocate.
|
||||||
* the buffer, else allocate.
|
|
||||||
*/
|
*/
|
||||||
if (!droq->recv_buf_list[droq->refill_idx].buffer) {
|
if (!droq->recv_buf_list[droq->refill_idx].buffer) {
|
||||||
pg_info =
|
pg_info =
|
||||||
|
@ -503,15 +498,16 @@ octeon_droq_refill(struct octeon_device *octeon_dev, struct octeon_droq *droq)
|
||||||
|
|
||||||
/** check if we can allocate packets to get out of oom.
|
/** check if we can allocate packets to get out of oom.
|
||||||
* @param droq - Droq being checked.
|
* @param droq - Droq being checked.
|
||||||
* @return does not return anything
|
* @return 1 if fails to refill minimum
|
||||||
*/
|
*/
|
||||||
void octeon_droq_check_oom(struct octeon_droq *droq)
|
int octeon_retry_droq_refill(struct octeon_droq *droq)
|
||||||
{
|
{
|
||||||
int desc_refilled;
|
|
||||||
struct octeon_device *oct = droq->oct_dev;
|
struct octeon_device *oct = droq->oct_dev;
|
||||||
|
int desc_refilled, reschedule = 1;
|
||||||
|
u32 pkts_credit;
|
||||||
|
|
||||||
if (readl(droq->pkts_credit_reg) <= CN23XX_SLI_DEF_BP) {
|
|
||||||
spin_lock_bh(&droq->lock);
|
spin_lock_bh(&droq->lock);
|
||||||
|
pkts_credit = readl(droq->pkts_credit_reg);
|
||||||
desc_refilled = octeon_droq_refill(oct, droq);
|
desc_refilled = octeon_droq_refill(oct, droq);
|
||||||
if (desc_refilled) {
|
if (desc_refilled) {
|
||||||
/* Flush the droq descriptor data to memory to be sure
|
/* Flush the droq descriptor data to memory to be sure
|
||||||
|
@ -522,9 +518,13 @@ void octeon_droq_check_oom(struct octeon_droq *droq)
|
||||||
writel(desc_refilled, droq->pkts_credit_reg);
|
writel(desc_refilled, droq->pkts_credit_reg);
|
||||||
/* make sure mmio write completes */
|
/* make sure mmio write completes */
|
||||||
mmiowb();
|
mmiowb();
|
||||||
|
|
||||||
|
if (pkts_credit + desc_refilled >= CN23XX_SLI_DEF_BP)
|
||||||
|
reschedule = 0;
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&droq->lock);
|
spin_unlock_bh(&droq->lock);
|
||||||
}
|
|
||||||
|
return reschedule;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32
|
static inline u32
|
||||||
|
@ -603,9 +603,9 @@ octeon_droq_fast_process_packets(struct octeon_device *oct,
|
||||||
struct octeon_droq *droq,
|
struct octeon_droq *droq,
|
||||||
u32 pkts_to_process)
|
u32 pkts_to_process)
|
||||||
{
|
{
|
||||||
|
u32 pkt, total_len = 0, pkt_count, retval;
|
||||||
struct octeon_droq_info *info;
|
struct octeon_droq_info *info;
|
||||||
union octeon_rh *rh;
|
union octeon_rh *rh;
|
||||||
u32 pkt, total_len = 0, pkt_count;
|
|
||||||
|
|
||||||
pkt_count = pkts_to_process;
|
pkt_count = pkts_to_process;
|
||||||
|
|
||||||
|
@ -709,30 +709,43 @@ octeon_droq_fast_process_packets(struct octeon_device *oct,
|
||||||
if (droq->refill_count >= droq->refill_threshold) {
|
if (droq->refill_count >= droq->refill_threshold) {
|
||||||
int desc_refilled = octeon_droq_refill(oct, droq);
|
int desc_refilled = octeon_droq_refill(oct, droq);
|
||||||
|
|
||||||
/* Flush the droq descriptor data to memory to be sure
|
if (desc_refilled) {
|
||||||
* that when we update the credits the data in memory
|
/* Flush the droq descriptor data to memory to
|
||||||
* is accurate.
|
* be sure that when we update the credits the
|
||||||
|
* data in memory is accurate.
|
||||||
*/
|
*/
|
||||||
wmb();
|
wmb();
|
||||||
writel((desc_refilled), droq->pkts_credit_reg);
|
writel(desc_refilled, droq->pkts_credit_reg);
|
||||||
/* make sure mmio write completes */
|
/* make sure mmio write completes */
|
||||||
mmiowb();
|
mmiowb();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} /* for (each packet)... */
|
} /* for (each packet)... */
|
||||||
|
|
||||||
/* Increment refill_count by the number of buffers processed. */
|
/* Increment refill_count by the number of buffers processed. */
|
||||||
droq->stats.pkts_received += pkt;
|
droq->stats.pkts_received += pkt;
|
||||||
droq->stats.bytes_received += total_len;
|
droq->stats.bytes_received += total_len;
|
||||||
|
|
||||||
|
retval = pkt;
|
||||||
if ((droq->ops.drop_on_max) && (pkts_to_process - pkt)) {
|
if ((droq->ops.drop_on_max) && (pkts_to_process - pkt)) {
|
||||||
octeon_droq_drop_packets(oct, droq, (pkts_to_process - pkt));
|
octeon_droq_drop_packets(oct, droq, (pkts_to_process - pkt));
|
||||||
|
|
||||||
droq->stats.dropped_toomany += (pkts_to_process - pkt);
|
droq->stats.dropped_toomany += (pkts_to_process - pkt);
|
||||||
return pkts_to_process;
|
retval = pkts_to_process;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pkt;
|
atomic_sub(retval, &droq->pkts_pending);
|
||||||
|
|
||||||
|
if (droq->refill_count >= droq->refill_threshold &&
|
||||||
|
readl(droq->pkts_credit_reg) < CN23XX_SLI_DEF_BP) {
|
||||||
|
octeon_droq_check_hw_for_pkts(droq);
|
||||||
|
|
||||||
|
/* Make sure there are no pkts_pending */
|
||||||
|
if (!atomic_read(&droq->pkts_pending))
|
||||||
|
octeon_schedule_rxq_oom_work(oct, droq);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -740,7 +753,7 @@ octeon_droq_process_packets(struct octeon_device *oct,
|
||||||
struct octeon_droq *droq,
|
struct octeon_droq *droq,
|
||||||
u32 budget)
|
u32 budget)
|
||||||
{
|
{
|
||||||
u32 pkt_count = 0, pkts_processed = 0;
|
u32 pkt_count = 0;
|
||||||
struct list_head *tmp, *tmp2;
|
struct list_head *tmp, *tmp2;
|
||||||
|
|
||||||
/* Grab the droq lock */
|
/* Grab the droq lock */
|
||||||
|
@ -757,9 +770,7 @@ octeon_droq_process_packets(struct octeon_device *oct,
|
||||||
if (pkt_count > budget)
|
if (pkt_count > budget)
|
||||||
pkt_count = budget;
|
pkt_count = budget;
|
||||||
|
|
||||||
pkts_processed = octeon_droq_fast_process_packets(oct, droq, pkt_count);
|
octeon_droq_fast_process_packets(oct, droq, pkt_count);
|
||||||
|
|
||||||
atomic_sub(pkts_processed, &droq->pkts_pending);
|
|
||||||
|
|
||||||
/* Release the spin lock */
|
/* Release the spin lock */
|
||||||
spin_unlock(&droq->lock);
|
spin_unlock(&droq->lock);
|
||||||
|
@ -813,8 +824,6 @@ octeon_droq_process_poll_pkts(struct octeon_device *oct,
|
||||||
octeon_droq_fast_process_packets(oct, droq,
|
octeon_droq_fast_process_packets(oct, droq,
|
||||||
pkts_available);
|
pkts_available);
|
||||||
|
|
||||||
atomic_sub(pkts_processed, &droq->pkts_pending);
|
|
||||||
|
|
||||||
total_pkts_processed += pkts_processed;
|
total_pkts_processed += pkts_processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -414,6 +414,6 @@ int octeon_droq_process_poll_pkts(struct octeon_device *oct,
|
||||||
|
|
||||||
int octeon_enable_irq(struct octeon_device *oct, u32 q_no);
|
int octeon_enable_irq(struct octeon_device *oct, u32 q_no);
|
||||||
|
|
||||||
void octeon_droq_check_oom(struct octeon_droq *droq);
|
int octeon_retry_droq_refill(struct octeon_droq *droq);
|
||||||
|
|
||||||
#endif /*__OCTEON_DROQ_H__ */
|
#endif /*__OCTEON_DROQ_H__ */
|
||||||
|
|
|
@ -70,6 +70,10 @@ void octeon_update_tx_completion_counters(void *buf, int reqtype,
|
||||||
void octeon_report_tx_completion_to_bql(void *txq, unsigned int pkts_compl,
|
void octeon_report_tx_completion_to_bql(void *txq, unsigned int pkts_compl,
|
||||||
unsigned int bytes_compl);
|
unsigned int bytes_compl);
|
||||||
void octeon_pf_changed_vf_macaddr(struct octeon_device *oct, u8 *mac);
|
void octeon_pf_changed_vf_macaddr(struct octeon_device *oct, u8 *mac);
|
||||||
|
|
||||||
|
void octeon_schedule_rxq_oom_work(struct octeon_device *oct,
|
||||||
|
struct octeon_droq *droq);
|
||||||
|
|
||||||
/** Swap 8B blocks */
|
/** Swap 8B blocks */
|
||||||
static inline void octeon_swap_8B_data(u64 *data, u32 blocks)
|
static inline void octeon_swap_8B_data(u64 *data, u32 blocks)
|
||||||
{
|
{
|
||||||
|
|
|
@ -173,7 +173,7 @@ struct lio {
|
||||||
struct cavium_wq txq_status_wq;
|
struct cavium_wq txq_status_wq;
|
||||||
|
|
||||||
/* work queue for rxq oom status */
|
/* work queue for rxq oom status */
|
||||||
struct cavium_wq rxq_status_wq;
|
struct cavium_wq rxq_status_wq[MAX_POSSIBLE_OCTEON_OUTPUT_QUEUES];
|
||||||
|
|
||||||
/* work queue for link status */
|
/* work queue for link status */
|
||||||
struct cavium_wq link_status_wq;
|
struct cavium_wq link_status_wq;
|
||||||
|
|
Loading…
Reference in New Issue