123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 |
- /*
- * Copyright (c) 2016, Mellanox Technologies. 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.
- */
- #include "mlx5_ib.h"
- struct mlx5_ib_gsi_wr {
- struct ib_cqe cqe;
- struct ib_wc wc;
- int send_flags;
- bool completed:1;
- };
- struct mlx5_ib_gsi_qp {
- struct ib_qp ibqp;
- struct ib_qp *rx_qp;
- u8 port_num;
- struct ib_qp_cap cap;
- enum ib_sig_type sq_sig_type;
- /* Serialize qp state modifications */
- struct mutex mutex;
- struct ib_cq *cq;
- struct mlx5_ib_gsi_wr *outstanding_wrs;
- u32 outstanding_pi, outstanding_ci;
- int num_qps;
- /* Protects access to the tx_qps. Post send operations synchronize
- * with tx_qp creation in setup_qp(). Also protects the
- * outstanding_wrs array and indices.
- */
- spinlock_t lock;
- struct ib_qp **tx_qps;
- };
- static struct mlx5_ib_gsi_qp *gsi_qp(struct ib_qp *qp)
- {
- return container_of(qp, struct mlx5_ib_gsi_qp, ibqp);
- }
- static bool mlx5_ib_deth_sqpn_cap(struct mlx5_ib_dev *dev)
- {
- return MLX5_CAP_GEN(dev->mdev, set_deth_sqpn);
- }
- /* Call with gsi->lock locked */
- static void generate_completions(struct mlx5_ib_gsi_qp *gsi)
- {
- struct ib_cq *gsi_cq = gsi->ibqp.send_cq;
- struct mlx5_ib_gsi_wr *wr;
- u32 index;
- for (index = gsi->outstanding_ci; index != gsi->outstanding_pi;
- index++) {
- wr = &gsi->outstanding_wrs[index % gsi->cap.max_send_wr];
- if (!wr->completed)
- break;
- if (gsi->sq_sig_type == IB_SIGNAL_ALL_WR ||
- wr->send_flags & IB_SEND_SIGNALED)
- WARN_ON_ONCE(mlx5_ib_generate_wc(gsi_cq, &wr->wc));
- wr->completed = false;
- }
- gsi->outstanding_ci = index;
- }
- static void handle_single_completion(struct ib_cq *cq, struct ib_wc *wc)
- {
- struct mlx5_ib_gsi_qp *gsi = cq->cq_context;
- struct mlx5_ib_gsi_wr *wr =
- container_of(wc->wr_cqe, struct mlx5_ib_gsi_wr, cqe);
- u64 wr_id;
- unsigned long flags;
- spin_lock_irqsave(&gsi->lock, flags);
- wr->completed = true;
- wr_id = wr->wc.wr_id;
- wr->wc = *wc;
- wr->wc.wr_id = wr_id;
- wr->wc.qp = &gsi->ibqp;
- generate_completions(gsi);
- spin_unlock_irqrestore(&gsi->lock, flags);
- }
- struct ib_qp *mlx5_ib_gsi_create_qp(struct ib_pd *pd,
- struct ib_qp_init_attr *init_attr)
- {
- struct mlx5_ib_dev *dev = to_mdev(pd->device);
- struct mlx5_ib_gsi_qp *gsi;
- struct ib_qp_init_attr hw_init_attr = *init_attr;
- const u8 port_num = init_attr->port_num;
- const int num_pkeys = pd->device->attrs.max_pkeys;
- const int num_qps = mlx5_ib_deth_sqpn_cap(dev) ? num_pkeys : 0;
- int ret;
- mlx5_ib_dbg(dev, "creating GSI QP\n");
- if (port_num > ARRAY_SIZE(dev->devr.ports) || port_num < 1) {
- mlx5_ib_warn(dev,
- "invalid port number %d during GSI QP creation\n",
- port_num);
- return ERR_PTR(-EINVAL);
- }
- gsi = kzalloc(sizeof(*gsi), GFP_KERNEL);
- if (!gsi)
- return ERR_PTR(-ENOMEM);
- gsi->tx_qps = kcalloc(num_qps, sizeof(*gsi->tx_qps), GFP_KERNEL);
- if (!gsi->tx_qps) {
- ret = -ENOMEM;
- goto err_free;
- }
- gsi->outstanding_wrs = kcalloc(init_attr->cap.max_send_wr,
- sizeof(*gsi->outstanding_wrs),
- GFP_KERNEL);
- if (!gsi->outstanding_wrs) {
- ret = -ENOMEM;
- goto err_free_tx;
- }
- mutex_init(&gsi->mutex);
- mutex_lock(&dev->devr.mutex);
- if (dev->devr.ports[port_num - 1].gsi) {
- mlx5_ib_warn(dev, "GSI QP already exists on port %d\n",
- port_num);
- ret = -EBUSY;
- goto err_free_wrs;
- }
- gsi->num_qps = num_qps;
- spin_lock_init(&gsi->lock);
- gsi->cap = init_attr->cap;
- gsi->sq_sig_type = init_attr->sq_sig_type;
- gsi->ibqp.qp_num = 1;
- gsi->port_num = port_num;
- gsi->cq = ib_alloc_cq(pd->device, gsi, init_attr->cap.max_send_wr, 0,
- IB_POLL_SOFTIRQ);
- if (IS_ERR(gsi->cq)) {
- mlx5_ib_warn(dev, "unable to create send CQ for GSI QP. error %ld\n",
- PTR_ERR(gsi->cq));
- ret = PTR_ERR(gsi->cq);
- goto err_free_wrs;
- }
- hw_init_attr.qp_type = MLX5_IB_QPT_HW_GSI;
- hw_init_attr.send_cq = gsi->cq;
- if (num_qps) {
- hw_init_attr.cap.max_send_wr = 0;
- hw_init_attr.cap.max_send_sge = 0;
- hw_init_attr.cap.max_inline_data = 0;
- }
- gsi->rx_qp = ib_create_qp(pd, &hw_init_attr);
- if (IS_ERR(gsi->rx_qp)) {
- mlx5_ib_warn(dev, "unable to create hardware GSI QP. error %ld\n",
- PTR_ERR(gsi->rx_qp));
- ret = PTR_ERR(gsi->rx_qp);
- goto err_destroy_cq;
- }
- dev->devr.ports[init_attr->port_num - 1].gsi = gsi;
- mutex_unlock(&dev->devr.mutex);
- return &gsi->ibqp;
- err_destroy_cq:
- ib_free_cq(gsi->cq);
- err_free_wrs:
- mutex_unlock(&dev->devr.mutex);
- kfree(gsi->outstanding_wrs);
- err_free_tx:
- kfree(gsi->tx_qps);
- err_free:
- kfree(gsi);
- return ERR_PTR(ret);
- }
- int mlx5_ib_gsi_destroy_qp(struct ib_qp *qp)
- {
- struct mlx5_ib_dev *dev = to_mdev(qp->device);
- struct mlx5_ib_gsi_qp *gsi = gsi_qp(qp);
- const int port_num = gsi->port_num;
- int qp_index;
- int ret;
- mlx5_ib_dbg(dev, "destroying GSI QP\n");
- mutex_lock(&dev->devr.mutex);
- ret = ib_destroy_qp(gsi->rx_qp);
- if (ret) {
- mlx5_ib_warn(dev, "unable to destroy hardware GSI QP. error %d\n",
- ret);
- mutex_unlock(&dev->devr.mutex);
- return ret;
- }
- dev->devr.ports[port_num - 1].gsi = NULL;
- mutex_unlock(&dev->devr.mutex);
- gsi->rx_qp = NULL;
- for (qp_index = 0; qp_index < gsi->num_qps; ++qp_index) {
- if (!gsi->tx_qps[qp_index])
- continue;
- WARN_ON_ONCE(ib_destroy_qp(gsi->tx_qps[qp_index]));
- gsi->tx_qps[qp_index] = NULL;
- }
- ib_free_cq(gsi->cq);
- kfree(gsi->outstanding_wrs);
- kfree(gsi->tx_qps);
- kfree(gsi);
- return 0;
- }
- static struct ib_qp *create_gsi_ud_qp(struct mlx5_ib_gsi_qp *gsi)
- {
- struct ib_pd *pd = gsi->rx_qp->pd;
- struct ib_qp_init_attr init_attr = {
- .event_handler = gsi->rx_qp->event_handler,
- .qp_context = gsi->rx_qp->qp_context,
- .send_cq = gsi->cq,
- .recv_cq = gsi->rx_qp->recv_cq,
- .cap = {
- .max_send_wr = gsi->cap.max_send_wr,
- .max_send_sge = gsi->cap.max_send_sge,
- .max_inline_data = gsi->cap.max_inline_data,
- },
- .sq_sig_type = gsi->sq_sig_type,
- .qp_type = IB_QPT_UD,
- .create_flags = mlx5_ib_create_qp_sqpn_qp1(),
- };
- return ib_create_qp(pd, &init_attr);
- }
- static int modify_to_rts(struct mlx5_ib_gsi_qp *gsi, struct ib_qp *qp,
- u16 qp_index)
- {
- struct mlx5_ib_dev *dev = to_mdev(qp->device);
- struct ib_qp_attr attr;
- int mask;
- int ret;
- mask = IB_QP_STATE | IB_QP_PKEY_INDEX | IB_QP_QKEY | IB_QP_PORT;
- attr.qp_state = IB_QPS_INIT;
- attr.pkey_index = qp_index;
- attr.qkey = IB_QP1_QKEY;
- attr.port_num = gsi->port_num;
- ret = ib_modify_qp(qp, &attr, mask);
- if (ret) {
- mlx5_ib_err(dev, "could not change QP%d state to INIT: %d\n",
- qp->qp_num, ret);
- return ret;
- }
- attr.qp_state = IB_QPS_RTR;
- ret = ib_modify_qp(qp, &attr, IB_QP_STATE);
- if (ret) {
- mlx5_ib_err(dev, "could not change QP%d state to RTR: %d\n",
- qp->qp_num, ret);
- return ret;
- }
- attr.qp_state = IB_QPS_RTS;
- attr.sq_psn = 0;
- ret = ib_modify_qp(qp, &attr, IB_QP_STATE | IB_QP_SQ_PSN);
- if (ret) {
- mlx5_ib_err(dev, "could not change QP%d state to RTS: %d\n",
- qp->qp_num, ret);
- return ret;
- }
- return 0;
- }
- static void setup_qp(struct mlx5_ib_gsi_qp *gsi, u16 qp_index)
- {
- struct ib_device *device = gsi->rx_qp->device;
- struct mlx5_ib_dev *dev = to_mdev(device);
- struct ib_qp *qp;
- unsigned long flags;
- u16 pkey;
- int ret;
- ret = ib_query_pkey(device, gsi->port_num, qp_index, &pkey);
- if (ret) {
- mlx5_ib_warn(dev, "unable to read P_Key at port %d, index %d\n",
- gsi->port_num, qp_index);
- return;
- }
- if (!pkey) {
- mlx5_ib_dbg(dev, "invalid P_Key at port %d, index %d. Skipping.\n",
- gsi->port_num, qp_index);
- return;
- }
- spin_lock_irqsave(&gsi->lock, flags);
- qp = gsi->tx_qps[qp_index];
- spin_unlock_irqrestore(&gsi->lock, flags);
- if (qp) {
- mlx5_ib_dbg(dev, "already existing GSI TX QP at port %d, index %d. Skipping\n",
- gsi->port_num, qp_index);
- return;
- }
- qp = create_gsi_ud_qp(gsi);
- if (IS_ERR(qp)) {
- mlx5_ib_warn(dev, "unable to create hardware UD QP for GSI: %ld\n",
- PTR_ERR(qp));
- return;
- }
- ret = modify_to_rts(gsi, qp, qp_index);
- if (ret)
- goto err_destroy_qp;
- spin_lock_irqsave(&gsi->lock, flags);
- WARN_ON_ONCE(gsi->tx_qps[qp_index]);
- gsi->tx_qps[qp_index] = qp;
- spin_unlock_irqrestore(&gsi->lock, flags);
- return;
- err_destroy_qp:
- WARN_ON_ONCE(qp);
- }
- static void setup_qps(struct mlx5_ib_gsi_qp *gsi)
- {
- u16 qp_index;
- for (qp_index = 0; qp_index < gsi->num_qps; ++qp_index)
- setup_qp(gsi, qp_index);
- }
- int mlx5_ib_gsi_modify_qp(struct ib_qp *qp, struct ib_qp_attr *attr,
- int attr_mask)
- {
- struct mlx5_ib_dev *dev = to_mdev(qp->device);
- struct mlx5_ib_gsi_qp *gsi = gsi_qp(qp);
- int ret;
- mlx5_ib_dbg(dev, "modifying GSI QP to state %d\n", attr->qp_state);
- mutex_lock(&gsi->mutex);
- ret = ib_modify_qp(gsi->rx_qp, attr, attr_mask);
- if (ret) {
- mlx5_ib_warn(dev, "unable to modify GSI rx QP: %d\n", ret);
- goto unlock;
- }
- if (to_mqp(gsi->rx_qp)->state == IB_QPS_RTS)
- setup_qps(gsi);
- unlock:
- mutex_unlock(&gsi->mutex);
- return ret;
- }
- int mlx5_ib_gsi_query_qp(struct ib_qp *qp, struct ib_qp_attr *qp_attr,
- int qp_attr_mask,
- struct ib_qp_init_attr *qp_init_attr)
- {
- struct mlx5_ib_gsi_qp *gsi = gsi_qp(qp);
- int ret;
- mutex_lock(&gsi->mutex);
- ret = ib_query_qp(gsi->rx_qp, qp_attr, qp_attr_mask, qp_init_attr);
- qp_init_attr->cap = gsi->cap;
- mutex_unlock(&gsi->mutex);
- return ret;
- }
- /* Call with gsi->lock locked */
- static int mlx5_ib_add_outstanding_wr(struct mlx5_ib_gsi_qp *gsi,
- struct ib_ud_wr *wr, struct ib_wc *wc)
- {
- struct mlx5_ib_dev *dev = to_mdev(gsi->rx_qp->device);
- struct mlx5_ib_gsi_wr *gsi_wr;
- if (gsi->outstanding_pi == gsi->outstanding_ci + gsi->cap.max_send_wr) {
- mlx5_ib_warn(dev, "no available GSI work request.\n");
- return -ENOMEM;
- }
- gsi_wr = &gsi->outstanding_wrs[gsi->outstanding_pi %
- gsi->cap.max_send_wr];
- gsi->outstanding_pi++;
- if (!wc) {
- memset(&gsi_wr->wc, 0, sizeof(gsi_wr->wc));
- gsi_wr->wc.pkey_index = wr->pkey_index;
- gsi_wr->wc.wr_id = wr->wr.wr_id;
- } else {
- gsi_wr->wc = *wc;
- gsi_wr->completed = true;
- }
- gsi_wr->cqe.done = &handle_single_completion;
- wr->wr.wr_cqe = &gsi_wr->cqe;
- return 0;
- }
- /* Call with gsi->lock locked */
- static int mlx5_ib_gsi_silent_drop(struct mlx5_ib_gsi_qp *gsi,
- struct ib_ud_wr *wr)
- {
- struct ib_wc wc = {
- { .wr_id = wr->wr.wr_id },
- .status = IB_WC_SUCCESS,
- .opcode = IB_WC_SEND,
- .qp = &gsi->ibqp,
- };
- int ret;
- ret = mlx5_ib_add_outstanding_wr(gsi, wr, &wc);
- if (ret)
- return ret;
- generate_completions(gsi);
- return 0;
- }
- /* Call with gsi->lock locked */
- static struct ib_qp *get_tx_qp(struct mlx5_ib_gsi_qp *gsi, struct ib_ud_wr *wr)
- {
- struct mlx5_ib_dev *dev = to_mdev(gsi->rx_qp->device);
- int qp_index = wr->pkey_index;
- if (!mlx5_ib_deth_sqpn_cap(dev))
- return gsi->rx_qp;
- if (qp_index >= gsi->num_qps)
- return NULL;
- return gsi->tx_qps[qp_index];
- }
- int mlx5_ib_gsi_post_send(struct ib_qp *qp, const struct ib_send_wr *wr,
- const struct ib_send_wr **bad_wr)
- {
- struct mlx5_ib_gsi_qp *gsi = gsi_qp(qp);
- struct ib_qp *tx_qp;
- unsigned long flags;
- int ret;
- for (; wr; wr = wr->next) {
- struct ib_ud_wr cur_wr = *ud_wr(wr);
- cur_wr.wr.next = NULL;
- spin_lock_irqsave(&gsi->lock, flags);
- tx_qp = get_tx_qp(gsi, &cur_wr);
- if (!tx_qp) {
- ret = mlx5_ib_gsi_silent_drop(gsi, &cur_wr);
- if (ret)
- goto err;
- spin_unlock_irqrestore(&gsi->lock, flags);
- continue;
- }
- ret = mlx5_ib_add_outstanding_wr(gsi, &cur_wr, NULL);
- if (ret)
- goto err;
- ret = ib_post_send(tx_qp, &cur_wr.wr, bad_wr);
- if (ret) {
- /* Undo the effect of adding the outstanding wr */
- gsi->outstanding_pi--;
- goto err;
- }
- spin_unlock_irqrestore(&gsi->lock, flags);
- }
- return 0;
- err:
- spin_unlock_irqrestore(&gsi->lock, flags);
- *bad_wr = wr;
- return ret;
- }
- int mlx5_ib_gsi_post_recv(struct ib_qp *qp, const struct ib_recv_wr *wr,
- const struct ib_recv_wr **bad_wr)
- {
- struct mlx5_ib_gsi_qp *gsi = gsi_qp(qp);
- return ib_post_recv(gsi->rx_qp, wr, bad_wr);
- }
- void mlx5_ib_gsi_pkey_change(struct mlx5_ib_gsi_qp *gsi)
- {
- if (!gsi)
- return;
- mutex_lock(&gsi->mutex);
- setup_qps(gsi);
- mutex_unlock(&gsi->mutex);
- }
|