📄 mthca_cq.c
字号:
/* * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * Copyright (c) 2005, 2006 Cisco Systems, Inc. All rights reserved. * Copyright (c) 2005 Mellanox Technologies. All rights reserved. * Copyright (c) 2004 Voltaire, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * $Id: mthca_cq.c 1369 2004-12-20 16:17:07Z roland $ */#include <linux/hardirq.h>#include <linux/sched.h>#include <asm/io.h>#include <rdma/ib_pack.h>#include "mthca_dev.h"#include "mthca_cmd.h"#include "mthca_memfree.h"enum { MTHCA_MAX_DIRECT_CQ_SIZE = 4 * PAGE_SIZE};enum { MTHCA_CQ_ENTRY_SIZE = 0x20};enum { MTHCA_ATOMIC_BYTE_LEN = 8};/* * Must be packed because start is 64 bits but only aligned to 32 bits. */struct mthca_cq_context { __be32 flags; __be64 start; __be32 logsize_usrpage; __be32 error_eqn; /* Tavor only */ __be32 comp_eqn; __be32 pd; __be32 lkey; __be32 last_notified_index; __be32 solicit_producer_index; __be32 consumer_index; __be32 producer_index; __be32 cqn; __be32 ci_db; /* Arbel only */ __be32 state_db; /* Arbel only */ u32 reserved;} __attribute__((packed));#define MTHCA_CQ_STATUS_OK ( 0 << 28)#define MTHCA_CQ_STATUS_OVERFLOW ( 9 << 28)#define MTHCA_CQ_STATUS_WRITE_FAIL (10 << 28)#define MTHCA_CQ_FLAG_TR ( 1 << 18)#define MTHCA_CQ_FLAG_OI ( 1 << 17)#define MTHCA_CQ_STATE_DISARMED ( 0 << 8)#define MTHCA_CQ_STATE_ARMED ( 1 << 8)#define MTHCA_CQ_STATE_ARMED_SOL ( 4 << 8)#define MTHCA_EQ_STATE_FIRED (10 << 8)enum { MTHCA_ERROR_CQE_OPCODE_MASK = 0xfe};enum { SYNDROME_LOCAL_LENGTH_ERR = 0x01, SYNDROME_LOCAL_QP_OP_ERR = 0x02, SYNDROME_LOCAL_EEC_OP_ERR = 0x03, SYNDROME_LOCAL_PROT_ERR = 0x04, SYNDROME_WR_FLUSH_ERR = 0x05, SYNDROME_MW_BIND_ERR = 0x06, SYNDROME_BAD_RESP_ERR = 0x10, SYNDROME_LOCAL_ACCESS_ERR = 0x11, SYNDROME_REMOTE_INVAL_REQ_ERR = 0x12, SYNDROME_REMOTE_ACCESS_ERR = 0x13, SYNDROME_REMOTE_OP_ERR = 0x14, SYNDROME_RETRY_EXC_ERR = 0x15, SYNDROME_RNR_RETRY_EXC_ERR = 0x16, SYNDROME_LOCAL_RDD_VIOL_ERR = 0x20, SYNDROME_REMOTE_INVAL_RD_REQ_ERR = 0x21, SYNDROME_REMOTE_ABORTED_ERR = 0x22, SYNDROME_INVAL_EECN_ERR = 0x23, SYNDROME_INVAL_EEC_STATE_ERR = 0x24};struct mthca_cqe { __be32 my_qpn; __be32 my_ee; __be32 rqpn; __be16 sl_g_mlpath; __be16 rlid; __be32 imm_etype_pkey_eec; __be32 byte_cnt; __be32 wqe; u8 opcode; u8 is_send; u8 reserved; u8 owner;};struct mthca_err_cqe { __be32 my_qpn; u32 reserved1[3]; u8 syndrome; u8 vendor_err; __be16 db_cnt; u32 reserved2; __be32 wqe; u8 opcode; u8 reserved3[2]; u8 owner;};#define MTHCA_CQ_ENTRY_OWNER_SW (0 << 7)#define MTHCA_CQ_ENTRY_OWNER_HW (1 << 7)#define MTHCA_TAVOR_CQ_DB_INC_CI (1 << 24)#define MTHCA_TAVOR_CQ_DB_REQ_NOT (2 << 24)#define MTHCA_TAVOR_CQ_DB_REQ_NOT_SOL (3 << 24)#define MTHCA_TAVOR_CQ_DB_SET_CI (4 << 24)#define MTHCA_TAVOR_CQ_DB_REQ_NOT_MULT (5 << 24)#define MTHCA_ARBEL_CQ_DB_REQ_NOT_SOL (1 << 24)#define MTHCA_ARBEL_CQ_DB_REQ_NOT (2 << 24)#define MTHCA_ARBEL_CQ_DB_REQ_NOT_MULT (3 << 24)static inline struct mthca_cqe *get_cqe_from_buf(struct mthca_cq_buf *buf, int entry){ if (buf->is_direct) return buf->queue.direct.buf + (entry * MTHCA_CQ_ENTRY_SIZE); else return buf->queue.page_list[entry * MTHCA_CQ_ENTRY_SIZE / PAGE_SIZE].buf + (entry * MTHCA_CQ_ENTRY_SIZE) % PAGE_SIZE;}static inline struct mthca_cqe *get_cqe(struct mthca_cq *cq, int entry){ return get_cqe_from_buf(&cq->buf, entry);}static inline struct mthca_cqe *cqe_sw(struct mthca_cqe *cqe){ return MTHCA_CQ_ENTRY_OWNER_HW & cqe->owner ? NULL : cqe;}static inline struct mthca_cqe *next_cqe_sw(struct mthca_cq *cq){ return cqe_sw(get_cqe(cq, cq->cons_index & cq->ibcq.cqe));}static inline void set_cqe_hw(struct mthca_cqe *cqe){ cqe->owner = MTHCA_CQ_ENTRY_OWNER_HW;}static void dump_cqe(struct mthca_dev *dev, void *cqe_ptr){ __be32 *cqe = cqe_ptr; (void) cqe; /* avoid warning if mthca_dbg compiled away... */ mthca_dbg(dev, "CQE contents %08x %08x %08x %08x %08x %08x %08x %08x\n", be32_to_cpu(cqe[0]), be32_to_cpu(cqe[1]), be32_to_cpu(cqe[2]), be32_to_cpu(cqe[3]), be32_to_cpu(cqe[4]), be32_to_cpu(cqe[5]), be32_to_cpu(cqe[6]), be32_to_cpu(cqe[7]));}/* * incr is ignored in native Arbel (mem-free) mode, so cq->cons_index * should be correct before calling update_cons_index(). */static inline void update_cons_index(struct mthca_dev *dev, struct mthca_cq *cq, int incr){ if (mthca_is_memfree(dev)) { *cq->set_ci_db = cpu_to_be32(cq->cons_index); wmb(); } else { mthca_write64(MTHCA_TAVOR_CQ_DB_INC_CI | cq->cqn, incr - 1, dev->kar + MTHCA_CQ_DOORBELL, MTHCA_GET_DOORBELL_LOCK(&dev->doorbell_lock)); /* * Make sure doorbells don't leak out of CQ spinlock * and reach the HCA out of order: */ mmiowb(); }}void mthca_cq_completion(struct mthca_dev *dev, u32 cqn){ struct mthca_cq *cq; cq = mthca_array_get(&dev->cq_table.cq, cqn & (dev->limits.num_cqs - 1)); if (!cq) { mthca_warn(dev, "Completion event for bogus CQ %08x\n", cqn); return; } ++cq->arm_sn; cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context);}void mthca_cq_event(struct mthca_dev *dev, u32 cqn, enum ib_event_type event_type){ struct mthca_cq *cq; struct ib_event event; spin_lock(&dev->cq_table.lock); cq = mthca_array_get(&dev->cq_table.cq, cqn & (dev->limits.num_cqs - 1)); if (cq) ++cq->refcount; spin_unlock(&dev->cq_table.lock); if (!cq) { mthca_warn(dev, "Async event for bogus CQ %08x\n", cqn); return; } event.device = &dev->ib_dev; event.event = event_type; event.element.cq = &cq->ibcq; if (cq->ibcq.event_handler) cq->ibcq.event_handler(&event, cq->ibcq.cq_context); spin_lock(&dev->cq_table.lock); if (!--cq->refcount) wake_up(&cq->wait); spin_unlock(&dev->cq_table.lock);}static inline int is_recv_cqe(struct mthca_cqe *cqe){ if ((cqe->opcode & MTHCA_ERROR_CQE_OPCODE_MASK) == MTHCA_ERROR_CQE_OPCODE_MASK) return !(cqe->opcode & 0x01); else return !(cqe->is_send & 0x80);}void mthca_cq_clean(struct mthca_dev *dev, struct mthca_cq *cq, u32 qpn, struct mthca_srq *srq){ struct mthca_cqe *cqe; u32 prod_index; int i, nfreed = 0; spin_lock_irq(&cq->lock); /* * First we need to find the current producer index, so we * know where to start cleaning from. It doesn't matter if HW * adds new entries after this loop -- the QP we're worried * about is already in RESET, so the new entries won't come * from our QP and therefore don't need to be checked. */ for (prod_index = cq->cons_index; cqe_sw(get_cqe(cq, prod_index & cq->ibcq.cqe)); ++prod_index) if (prod_index == cq->cons_index + cq->ibcq.cqe) break; if (0) mthca_dbg(dev, "Cleaning QPN %06x from CQN %06x; ci %d, pi %d\n", qpn, cq->cqn, cq->cons_index, prod_index); /* * Now sweep backwards through the CQ, removing CQ entries * that match our QP by copying older entries on top of them. */ while ((int) --prod_index - (int) cq->cons_index >= 0) { cqe = get_cqe(cq, prod_index & cq->ibcq.cqe); if (cqe->my_qpn == cpu_to_be32(qpn)) { if (srq && is_recv_cqe(cqe)) mthca_free_srq_wqe(srq, be32_to_cpu(cqe->wqe)); ++nfreed; } else if (nfreed) memcpy(get_cqe(cq, (prod_index + nfreed) & cq->ibcq.cqe), cqe, MTHCA_CQ_ENTRY_SIZE); } if (nfreed) { for (i = 0; i < nfreed; ++i) set_cqe_hw(get_cqe(cq, (cq->cons_index + i) & cq->ibcq.cqe)); wmb(); cq->cons_index += nfreed; update_cons_index(dev, cq, nfreed); } spin_unlock_irq(&cq->lock);}void mthca_cq_resize_copy_cqes(struct mthca_cq *cq){ int i; /* * In Tavor mode, the hardware keeps the consumer and producer * indices mod the CQ size. Since we might be making the CQ * bigger, we need to deal with the case where the producer * index wrapped around before the CQ was resized. */ if (!mthca_is_memfree(to_mdev(cq->ibcq.device)) && cq->ibcq.cqe < cq->resize_buf->cqe) { cq->cons_index &= cq->ibcq.cqe; if (cqe_sw(get_cqe(cq, cq->ibcq.cqe))) cq->cons_index -= cq->ibcq.cqe + 1; } for (i = cq->cons_index; cqe_sw(get_cqe(cq, i & cq->ibcq.cqe)); ++i) memcpy(get_cqe_from_buf(&cq->resize_buf->buf, i & cq->resize_buf->cqe), get_cqe(cq, i & cq->ibcq.cqe), MTHCA_CQ_ENTRY_SIZE);}int mthca_alloc_cq_buf(struct mthca_dev *dev, struct mthca_cq_buf *buf, int nent){ int ret; int i; ret = mthca_buf_alloc(dev, nent * MTHCA_CQ_ENTRY_SIZE, MTHCA_MAX_DIRECT_CQ_SIZE, &buf->queue, &buf->is_direct, &dev->driver_pd, 1, &buf->mr); if (ret) return ret; for (i = 0; i < nent; ++i) set_cqe_hw(get_cqe_from_buf(buf, i)); return 0;}void mthca_free_cq_buf(struct mthca_dev *dev, struct mthca_cq_buf *buf, int cqe){ mthca_buf_free(dev, (cqe + 1) * MTHCA_CQ_ENTRY_SIZE, &buf->queue, buf->is_direct, &buf->mr);}static void handle_error_cqe(struct mthca_dev *dev, struct mthca_cq *cq, struct mthca_qp *qp, int wqe_index, int is_send, struct mthca_err_cqe *cqe, struct ib_wc *entry, int *free_cqe){ int dbd; __be32 new_wqe; if (cqe->syndrome == SYNDROME_LOCAL_QP_OP_ERR) { mthca_dbg(dev, "local QP operation err " "(QPN %06x, WQE @ %08x, CQN %06x, index %d)\n", be32_to_cpu(cqe->my_qpn), be32_to_cpu(cqe->wqe), cq->cqn, cq->cons_index); dump_cqe(dev, cqe); } /* * For completions in error, only work request ID, status, vendor error * (and freed resource count for RD) have to be set. */ switch (cqe->syndrome) { case SYNDROME_LOCAL_LENGTH_ERR: entry->status = IB_WC_LOC_LEN_ERR; break; case SYNDROME_LOCAL_QP_OP_ERR: entry->status = IB_WC_LOC_QP_OP_ERR; break; case SYNDROME_LOCAL_EEC_OP_ERR: entry->status = IB_WC_LOC_EEC_OP_ERR; break; case SYNDROME_LOCAL_PROT_ERR: entry->status = IB_WC_LOC_PROT_ERR; break; case SYNDROME_WR_FLUSH_ERR: entry->status = IB_WC_WR_FLUSH_ERR; break; case SYNDROME_MW_BIND_ERR: entry->status = IB_WC_MW_BIND_ERR; break; case SYNDROME_BAD_RESP_ERR: entry->status = IB_WC_BAD_RESP_ERR; break; case SYNDROME_LOCAL_ACCESS_ERR: entry->status = IB_WC_LOC_ACCESS_ERR; break; case SYNDROME_REMOTE_INVAL_REQ_ERR: entry->status = IB_WC_REM_INV_REQ_ERR; break; case SYNDROME_REMOTE_ACCESS_ERR: entry->status = IB_WC_REM_ACCESS_ERR; break; case SYNDROME_REMOTE_OP_ERR: entry->status = IB_WC_REM_OP_ERR; break; case SYNDROME_RETRY_EXC_ERR: entry->status = IB_WC_RETRY_EXC_ERR; break; case SYNDROME_RNR_RETRY_EXC_ERR: entry->status = IB_WC_RNR_RETRY_EXC_ERR; break; case SYNDROME_LOCAL_RDD_VIOL_ERR: entry->status = IB_WC_LOC_RDD_VIOL_ERR; break; case SYNDROME_REMOTE_INVAL_RD_REQ_ERR: entry->status = IB_WC_REM_INV_RD_REQ_ERR; break; case SYNDROME_REMOTE_ABORTED_ERR: entry->status = IB_WC_REM_ABORT_ERR; break; case SYNDROME_INVAL_EECN_ERR: entry->status = IB_WC_INV_EECN_ERR; break; case SYNDROME_INVAL_EEC_STATE_ERR: entry->status = IB_WC_INV_EEC_STATE_ERR; break; default: entry->status = IB_WC_GENERAL_ERR; break; } entry->vendor_err = cqe->vendor_err; /* * Mem-free HCAs always generate one CQE per WQE, even in the * error case, so we don't have to check the doorbell count, etc. */ if (mthca_is_memfree(dev)) return; mthca_free_err_wqe(dev, qp, is_send, wqe_index, &dbd, &new_wqe); /* * If we're at the end of the WQE chain, or we've used up our * doorbell count, free the CQE. Otherwise just update it for * the next poll operation. */ if (!(new_wqe & cpu_to_be32(0x3f)) || (!cqe->db_cnt && dbd)) return; cqe->db_cnt = cpu_to_be16(be16_to_cpu(cqe->db_cnt) - dbd); cqe->wqe = new_wqe; cqe->syndrome = SYNDROME_WR_FLUSH_ERR; *free_cqe = 0;}static inline int mthca_poll_one(struct mthca_dev *dev, struct mthca_cq *cq, struct mthca_qp **cur_qp, int *freed, struct ib_wc *entry){ struct mthca_wq *wq; struct mthca_cqe *cqe; int wqe_index; int is_error; int is_send; int free_cqe = 1; int err = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -