Commit 99fa331d authored by Mark Zhang's avatar Mark Zhang Committed by Jason Gunthorpe

RDMA/counter: Add "auto" configuration mode support

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: default avatarMark Zhang <markz@mellanox.com>
Reviewed-by: default avatarMajd Dibbiny <majd@mellanox.com>
Signed-off-by: default avatarLeon Romanovsky <leonro@mellanox.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
parent 413d3347
......@@ -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;
......
......@@ -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);
......
......@@ -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;
......@@ -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) {
......
......@@ -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 {
......@@ -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);
......
......@@ -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;
};
......@@ -31,6 +34,9 @@ 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;
};
......@@ -38,5 +44,7 @@ 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_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment