Skip to content

Commit

Permalink
RDMA/counter: Add "auto" configuration mode support
Browse files Browse the repository at this point in the history
In auto mode all QPs belong to one category are bind automatically to a
single counter set. Currently only "qp type" is supported.

In this mode the qp counter is set in RST2INIT modification, and when a qp
is destroyed the counter is unbound.

Signed-off-by: Mark Zhang <markz@mellanox.com>
Reviewed-by: Majd Dibbiny <majd@mellanox.com>
Signed-off-by: Leon Romanovsky <leonro@mellanox.com>
Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
  • Loading branch information
Mark Zhang authored and jgunthorpe committed Jul 5, 2019
1 parent 413d334 commit 99fa331
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 0 deletions.
221 changes: 221 additions & 0 deletions drivers/infiniband/core/counters.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,227 @@ int rdma_counter_set_auto_mode(struct ib_device *dev, u8 port,
return ret;
}

static struct rdma_counter *rdma_counter_alloc(struct ib_device *dev, u8 port,
enum rdma_nl_counter_mode mode)
{
struct rdma_counter *counter;

if (!dev->ops.counter_dealloc)
return NULL;

counter = kzalloc(sizeof(*counter), GFP_KERNEL);
if (!counter)
return NULL;

counter->device = dev;
counter->port = port;
counter->res.type = RDMA_RESTRACK_COUNTER;
counter->mode.mode = mode;
kref_init(&counter->kref);
mutex_init(&counter->lock);

return counter;
}

static void rdma_counter_free(struct rdma_counter *counter)
{
rdma_restrack_del(&counter->res);
kfree(counter);
}

static void auto_mode_init_counter(struct rdma_counter *counter,
const struct ib_qp *qp,
enum rdma_nl_counter_mask new_mask)
{
struct auto_mode_param *param = &counter->mode.param;

counter->mode.mode = RDMA_COUNTER_MODE_AUTO;
counter->mode.mask = new_mask;

if (new_mask & RDMA_COUNTER_MASK_QP_TYPE)
param->qp_type = qp->qp_type;
}

static bool auto_mode_match(struct ib_qp *qp, struct rdma_counter *counter,
enum rdma_nl_counter_mask auto_mask)
{
struct auto_mode_param *param = &counter->mode.param;
bool match = true;

if (rdma_is_kernel_res(&counter->res) != rdma_is_kernel_res(&qp->res))
return false;

/* Ensure that counter belong to right PID */
if (!rdma_is_kernel_res(&counter->res) &&
!rdma_is_kernel_res(&qp->res) &&
(task_pid_vnr(counter->res.task) != current->pid))
return false;

if (auto_mask & RDMA_COUNTER_MASK_QP_TYPE)
match &= (param->qp_type == qp->qp_type);

return match;
}

static int __rdma_counter_bind_qp(struct rdma_counter *counter,
struct ib_qp *qp)
{
int ret;

if (qp->counter)
return -EINVAL;

if (!qp->device->ops.counter_bind_qp)
return -EOPNOTSUPP;

mutex_lock(&counter->lock);
ret = qp->device->ops.counter_bind_qp(counter, qp);
mutex_unlock(&counter->lock);

return ret;
}

static int __rdma_counter_unbind_qp(struct ib_qp *qp)
{
struct rdma_counter *counter = qp->counter;
int ret;

if (!qp->device->ops.counter_unbind_qp)
return -EOPNOTSUPP;

mutex_lock(&counter->lock);
ret = qp->device->ops.counter_unbind_qp(qp);
mutex_unlock(&counter->lock);

return ret;
}

/**
* rdma_get_counter_auto_mode - Find the counter that @qp should be bound
* with in auto mode
*
* Return: The counter (with ref-count increased) if found
*/
static struct rdma_counter *rdma_get_counter_auto_mode(struct ib_qp *qp,
u8 port)
{
struct rdma_port_counter *port_counter;
struct rdma_counter *counter = NULL;
struct ib_device *dev = qp->device;
struct rdma_restrack_entry *res;
struct rdma_restrack_root *rt;
unsigned long id = 0;

port_counter = &dev->port_data[port].port_counter;
rt = &dev->res[RDMA_RESTRACK_COUNTER];
xa_lock(&rt->xa);
xa_for_each(&rt->xa, id, res) {
if (!rdma_is_visible_in_pid_ns(res))
continue;

counter = container_of(res, struct rdma_counter, res);
if ((counter->device != qp->device) || (counter->port != port))
goto next;

if (auto_mode_match(qp, counter, port_counter->mode.mask))
break;
next:
counter = NULL;
}

if (counter && !kref_get_unless_zero(&counter->kref))
counter = NULL;

xa_unlock(&rt->xa);
return counter;
}

static void rdma_counter_res_add(struct rdma_counter *counter,
struct ib_qp *qp)
{
if (rdma_is_kernel_res(&qp->res)) {
rdma_restrack_set_task(&counter->res, qp->res.kern_name);
rdma_restrack_kadd(&counter->res);
} else {
rdma_restrack_attach_task(&counter->res, qp->res.task);
rdma_restrack_uadd(&counter->res);
}
}

static void counter_release(struct kref *kref)
{
struct rdma_counter *counter;

counter = container_of(kref, struct rdma_counter, kref);
counter->device->ops.counter_dealloc(counter);
rdma_counter_free(counter);
}

/**
* rdma_counter_bind_qp_auto - Check and bind the QP to a counter base on
* the auto-mode rule
*/
int rdma_counter_bind_qp_auto(struct ib_qp *qp, u8 port)
{
struct rdma_port_counter *port_counter;
struct ib_device *dev = qp->device;
struct rdma_counter *counter;
int ret;

if (!rdma_is_port_valid(dev, port))
return -EINVAL;

port_counter = &dev->port_data[port].port_counter;
if (port_counter->mode.mode != RDMA_COUNTER_MODE_AUTO)
return 0;

counter = rdma_get_counter_auto_mode(qp, port);
if (counter) {
ret = __rdma_counter_bind_qp(counter, qp);
if (ret) {
kref_put(&counter->kref, counter_release);
return ret;
}
} else {
counter = rdma_counter_alloc(dev, port, RDMA_COUNTER_MODE_AUTO);
if (!counter)
return -ENOMEM;

auto_mode_init_counter(counter, qp, port_counter->mode.mask);

ret = __rdma_counter_bind_qp(counter, qp);
if (ret) {
rdma_counter_free(counter);
return ret;
}

rdma_counter_res_add(counter, qp);
}

return 0;
}

/**
* rdma_counter_unbind_qp - Unbind a qp from a counter
* @force:
* true - Decrease the counter ref-count anyway (e.g., qp destroy)
*/
int rdma_counter_unbind_qp(struct ib_qp *qp, bool force)
{
struct rdma_counter *counter = qp->counter;
int ret;

if (!counter)
return -EINVAL;

ret = __rdma_counter_unbind_qp(qp);
if (ret && !force)
return ret;

kref_put(&counter->kref, counter_release);
return 0;
}

void rdma_counter_init(struct ib_device *dev)
{
struct rdma_port_counter *port_counter;
Expand Down
3 changes: 3 additions & 0 deletions drivers/infiniband/core/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,9 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops)
SET_DEVICE_OP(dev_ops, alloc_xrcd);
SET_DEVICE_OP(dev_ops, attach_mcast);
SET_DEVICE_OP(dev_ops, check_mr_status);
SET_DEVICE_OP(dev_ops, counter_bind_qp);
SET_DEVICE_OP(dev_ops, counter_dealloc);
SET_DEVICE_OP(dev_ops, counter_unbind_qp);
SET_DEVICE_OP(dev_ops, create_ah);
SET_DEVICE_OP(dev_ops, create_counters);
SET_DEVICE_OP(dev_ops, create_cq);
Expand Down
9 changes: 9 additions & 0 deletions drivers/infiniband/core/verbs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1690,6 +1690,14 @@ static int _ib_modify_qp(struct ib_qp *qp, struct ib_qp_attr *attr,
}
}

/*
* Bind this qp to a counter automatically based on the rdma counter
* rules. This only set in RST2INIT with port specified
*/
if (!qp->counter && (attr_mask & IB_QP_PORT) &&
((attr_mask & IB_QP_STATE) && attr->qp_state == IB_QPS_INIT))
rdma_counter_bind_qp_auto(qp, attr->port_num);

ret = ib_security_modify_qp(qp, attr, attr_mask, udata);
if (ret)
goto out;
Expand Down Expand Up @@ -1885,6 +1893,7 @@ int ib_destroy_qp_user(struct ib_qp *qp, struct ib_udata *udata)
if (!qp->uobject)
rdma_rw_cleanup_mrs(qp);

rdma_counter_unbind_qp(qp, true);
rdma_restrack_del(&qp->res);
ret = qp->device->ops.destroy_qp(qp, udata);
if (!ret) {
Expand Down
18 changes: 18 additions & 0 deletions include/rdma/ib_verbs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1698,6 +1698,9 @@ struct ib_qp {
* Implementation details of the RDMA core, don't use in drivers:
*/
struct rdma_restrack_entry res;

/* The counter the qp is bind to */
struct rdma_counter *counter;
};

struct ib_dm {
Expand Down Expand Up @@ -2485,6 +2488,21 @@ struct ib_device_ops {
u8 pdata_len);
int (*iw_create_listen)(struct iw_cm_id *cm_id, int backlog);
int (*iw_destroy_listen)(struct iw_cm_id *cm_id);
/**
* counter_bind_qp - Bind a QP to a counter.
* @counter - The counter to be bound. If counter->id is zero then
* the driver needs to allocate a new counter and set counter->id
*/
int (*counter_bind_qp)(struct rdma_counter *counter, struct ib_qp *qp);
/**
* counter_unbind_qp - Unbind the qp from the dynamically-allocated
* counter and bind it onto the default one
*/
int (*counter_unbind_qp)(struct ib_qp *qp);
/**
* counter_dealloc -De-allocate the hw counter
*/
int (*counter_dealloc)(struct rdma_counter *counter);

DECLARE_RDMA_OBJ_SIZE(ib_ah);
DECLARE_RDMA_OBJ_SIZE(ib_cq);
Expand Down
8 changes: 8 additions & 0 deletions include/rdma/rdma_counter.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
#define _RDMA_COUNTER_H_

#include <linux/mutex.h>
#include <linux/pid_namespace.h>

#include <rdma/ib_verbs.h>
#include <rdma/restrack.h>
#include <rdma/rdma_netlink.h>

struct ib_qp;

struct auto_mode_param {
int qp_type;
};
Expand All @@ -31,12 +34,17 @@ struct rdma_counter {
struct rdma_restrack_entry res;
struct ib_device *device;
uint32_t id;
struct kref kref;
struct rdma_counter_mode mode;
struct mutex lock;
u8 port;
};

void rdma_counter_init(struct ib_device *dev);
void rdma_counter_release(struct ib_device *dev);
int rdma_counter_set_auto_mode(struct ib_device *dev, u8 port,
bool on, enum rdma_nl_counter_mask mask);
int rdma_counter_bind_qp_auto(struct ib_qp *qp, u8 port);
int rdma_counter_unbind_qp(struct ib_qp *qp, bool force);

#endif /* _RDMA_COUNTER_H_ */

0 comments on commit 99fa331

Please sign in to comment.