IB/rdmavt: Initialize and teardown of qpn table

Add table init as well as teardown for handling qpn maps. Drivers can still
provide this functionality by setting the QP_INIT_DRIVER bit.

Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Reviewed-by: Mike Marciniszyn <mike.marciniszyn@intel.com>
Signed-off-by: Dennis Dalessandro <dennis.dalessandro@intel.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
This commit is contained in:
Dennis Dalessandro 2016-01-06 10:04:46 -08:00 committed by Doug Ledford
parent b4e64397da
commit 0acb0cc7ec
5 changed files with 263 additions and 13 deletions

View File

@ -45,8 +45,205 @@
*
*/
#include <linux/bitops.h>
#include <linux/lockdep.h>
#include "vt.h"
#include "qp.h"
static void get_map_page(struct rvt_qpn_table *qpt, struct rvt_qpn_map *map)
{
unsigned long page = get_zeroed_page(GFP_KERNEL);
/*
* Free the page if someone raced with us installing it.
*/
spin_lock(&qpt->lock);
if (map->page)
free_page(page);
else
map->page = (void *)page;
spin_unlock(&qpt->lock);
}
/**
* init_qpn_table - initialize the QP number table for a device
* @qpt: the QPN table
*/
static int init_qpn_table(struct rvt_dev_info *rdi, struct rvt_qpn_table *qpt)
{
u32 offset, i;
struct rvt_qpn_map *map;
int ret = 0;
if (!(rdi->dparms.qpn_res_end > rdi->dparms.qpn_res_start))
return -EINVAL;
spin_lock_init(&qpt->lock);
qpt->last = rdi->dparms.qpn_start;
qpt->incr = rdi->dparms.qpn_inc << rdi->dparms.qos_shift;
/*
* Drivers may want some QPs beyond what we need for verbs let them use
* our qpn table. No need for two. Lets go ahead and mark the bitmaps
* for those. The reserved range must be *after* the range which verbs
* will pick from.
*/
/* Figure out number of bit maps needed before reserved range */
qpt->nmaps = rdi->dparms.qpn_res_start / RVT_BITS_PER_PAGE;
/* This should always be zero */
offset = rdi->dparms.qpn_res_start & RVT_BITS_PER_PAGE_MASK;
/* Starting with the first reserved bit map */
map = &qpt->map[qpt->nmaps];
rvt_pr_info(rdi, "Reserving QPNs from 0x%x to 0x%x for non-verbs use\n",
rdi->dparms.qpn_res_start, rdi->dparms.qpn_res_end);
for (i = rdi->dparms.qpn_res_start; i < rdi->dparms.qpn_res_end; i++) {
if (!map->page) {
get_map_page(qpt, map);
if (!map->page) {
ret = -ENOMEM;
break;
}
}
set_bit(offset, map->page);
offset++;
if (offset == RVT_BITS_PER_PAGE) {
/* next page */
qpt->nmaps++;
map++;
offset = 0;
}
}
return ret;
}
/**
* free_qpn_table - free the QP number table for a device
* @qpt: the QPN table
*/
static void free_qpn_table(struct rvt_qpn_table *qpt)
{
int i;
for (i = 0; i < ARRAY_SIZE(qpt->map); i++)
free_page((unsigned long)qpt->map[i].page);
}
int rvt_driver_qp_init(struct rvt_dev_info *rdi)
{
int i;
int ret = -ENOMEM;
if (rdi->flags & RVT_FLAG_QP_INIT_DRIVER) {
rvt_pr_info(rdi, "Driver is doing QP init.\n");
return 0;
}
if (!rdi->dparms.qp_table_size)
return -EINVAL;
/*
* If driver is not doing any QP allocation then make sure it is
* providing the necessary QP functions.
*/
if (!rdi->driver_f.free_all_qps)
return -EINVAL;
/* allocate parent object */
rdi->qp_dev = kzalloc(sizeof(*rdi->qp_dev), GFP_KERNEL);
if (!rdi->qp_dev)
return -ENOMEM;
/* allocate hash table */
rdi->qp_dev->qp_table_size = rdi->dparms.qp_table_size;
rdi->qp_dev->qp_table_bits = ilog2(rdi->dparms.qp_table_size);
rdi->qp_dev->qp_table =
kmalloc(rdi->qp_dev->qp_table_size *
sizeof(*rdi->qp_dev->qp_table),
GFP_KERNEL);
if (!rdi->qp_dev->qp_table)
goto no_qp_table;
for (i = 0; i < rdi->qp_dev->qp_table_size; i++)
RCU_INIT_POINTER(rdi->qp_dev->qp_table[i], NULL);
spin_lock_init(&rdi->qp_dev->qpt_lock);
/* initialize qpn map */
if (init_qpn_table(rdi, &rdi->qp_dev->qpn_table))
goto fail_table;
return ret;
fail_table:
kfree(rdi->qp_dev->qp_table);
free_qpn_table(&rdi->qp_dev->qpn_table);
no_qp_table:
kfree(rdi->qp_dev);
return ret;
}
/**
* free_all_qps - check for QPs still in use
* @qpt: the QP table to empty
*
* There should not be any QPs still in use.
* Free memory for table.
*/
static unsigned free_all_qps(struct rvt_dev_info *rdi)
{
unsigned long flags;
struct rvt_qp *qp;
unsigned n, qp_inuse = 0;
spinlock_t *ql; /* work around too long line below */
rdi->driver_f.free_all_qps(rdi);
if (!rdi->qp_dev)
return 0;
ql = &rdi->qp_dev->qpt_lock;
spin_lock_irqsave(&rdi->qp_dev->qpt_lock, flags);
for (n = 0; n < rdi->qp_dev->qp_table_size; n++) {
qp = rcu_dereference_protected(rdi->qp_dev->qp_table[n],
lockdep_is_held(ql));
RCU_INIT_POINTER(rdi->qp_dev->qp_table[n], NULL);
qp = rcu_dereference_protected(qp->next,
lockdep_is_held(ql));
while (qp) {
qp_inuse++;
qp = rcu_dereference_protected(qp->next,
lockdep_is_held(ql));
}
}
spin_unlock_irqrestore(ql, flags);
synchronize_rcu();
return qp_inuse;
}
void rvt_qp_exit(struct rvt_dev_info *rdi)
{
u32 qps_inuse = free_all_qps(rdi);
qps_inuse = free_all_qps(rdi);
if (qps_inuse)
rvt_pr_err(rdi, "QP memory leak! %u still in use\n",
qps_inuse);
if (!rdi->qp_dev)
return;
kfree(rdi->qp_dev->qp_table);
free_qpn_table(&rdi->qp_dev->qpn_table);
kfree(rdi->qp_dev);
}
/**
* rvt_create_qp - create a queue pair for a device
* @ibpd: the protection domain who's device we create the queue pair for

View File

@ -50,6 +50,8 @@
#include <rdma/rdma_vt.h>
int rvt_driver_qp_init(struct rvt_dev_info *rdi);
void rvt_qp_exit(struct rvt_dev_info *rdi);
struct ib_qp *rvt_create_qp(struct ib_pd *ibpd,
struct ib_qp_init_attr *init_attr,
struct ib_udata *udata);

View File

@ -223,9 +223,23 @@ int rvt_register_device(struct rvt_dev_info *rdi)
(!rdi->driver_f.get_card_name) ||
(!rdi->driver_f.get_pci_dev) ||
(!rdi->driver_f.check_ah)) {
pr_err("Driver not supporting req func\n");
return -EINVAL;
}
if (!rdi->dparms.nports) {
rvt_pr_err(rdi, "Driver says it has no ports.\n");
return -EINVAL;
}
rdi->ports = kcalloc(rdi->dparms.nports,
sizeof(struct rvt_ibport **),
GFP_KERNEL);
if (!rdi->ports) {
rvt_pr_err(rdi, "Could not allocate port mem.\n");
return -ENOMEM;
}
/* Once we get past here we can use the rvt_pr macros */
/* Dev Ops */
@ -240,6 +254,12 @@ int rvt_register_device(struct rvt_dev_info *rdi)
CHECK_DRIVER_OVERRIDE(rdi, get_port_immutable);
/* Queue Pairs */
ret = rvt_driver_qp_init(rdi);
if (ret) {
pr_err("Error in driver QP init.\n");
return -EINVAL;
}
CHECK_DRIVER_OVERRIDE(rdi, create_qp);
CHECK_DRIVER_OVERRIDE(rdi, modify_qp);
CHECK_DRIVER_OVERRIDE(rdi, destroy_qp);
@ -300,19 +320,6 @@ int rvt_register_device(struct rvt_dev_info *rdi)
spin_lock_init(&rdi->n_pds_lock);
rdi->n_pds_allocated = 0;
if (rdi->dparms.nports) {
rdi->ports = kcalloc(rdi->dparms.nports,
sizeof(struct rvt_ibport **),
GFP_KERNEL);
if (!rdi->ports) {
rvt_pr_err(rdi, "Could not allocate port mem.\n");
ret = -ENOMEM;
goto bail_mr;
}
} else {
rvt_pr_warn(rdi, "Driver says it has no ports.\n");
}
/* We are now good to announce we exist */
ret = ib_register_device(&rdi->ibdev, rdi->driver_f.port_callback);
if (ret) {
@ -327,6 +334,8 @@ bail_mr:
rvt_mr_exit(rdi);
bail_no_mr:
rvt_qp_exit(rdi);
return ret;
}
EXPORT_SYMBOL(rvt_register_device);

View File

@ -172,7 +172,13 @@ struct rvt_driver_params {
* For instance special module parameters. Goes here.
*/
unsigned int lkey_table_size;
unsigned int qp_table_size;
int qpn_start;
int qpn_inc;
int qpn_res_start;
int qpn_res_end;
int nports;
u8 qos_shift;
};
/* Protection domain */
@ -205,6 +211,7 @@ struct rvt_driver_provided {
int (*port_callback)(struct ib_device *, u8, struct kobject *);
const char * (*get_card_name)(struct rvt_dev_info *rdi);
struct pci_dev * (*get_pci_dev)(struct rvt_dev_info *rdi);
void (*free_all_qps)(struct rvt_dev_info *rdi);
/*--------------------*/
/* Optional functions */
@ -245,6 +252,8 @@ struct rvt_dev_info {
int flags;
struct rvt_ibport **ports;
struct rvt_qp_ibdev *qp_dev;
};
static inline struct rvt_pd *ibpd_to_rvtpd(struct ib_pd *ibpd)

View File

@ -259,4 +259,37 @@ struct rvt_srq {
u32 limit;
};
#define RVT_QPN_MAX BIT(24)
#define RVT_QPNMAP_ENTRIES (RVT_QPN_MAX / PAGE_SIZE / BITS_PER_BYTE)
#define RVT_BITS_PER_PAGE (PAGE_SIZE * BITS_PER_BYTE)
#define RVT_BITS_PER_PAGE_MASK (RVT_BITS_PER_PAGE - 1)
/*
* QPN-map pages start out as NULL, they get allocated upon
* first use and are never deallocated. This way,
* large bitmaps are not allocated unless large numbers of QPs are used.
*/
struct rvt_qpn_map {
void *page;
};
struct rvt_qpn_table {
spinlock_t lock; /* protect changes to the qp table */
unsigned flags; /* flags for QP0/1 allocated for each port */
u32 last; /* last QP number allocated */
u32 nmaps; /* size of the map table */
u16 limit;
u8 incr;
/* bit map of free QP numbers other than 0/1 */
struct rvt_qpn_map map[RVT_QPNMAP_ENTRIES];
};
struct rvt_qp_ibdev {
u32 qp_table_size;
u32 qp_table_bits;
struct rvt_qp __rcu **qp_table;
spinlock_t qpt_lock; /* qptable lock */
struct rvt_qpn_table qpn_table;
};
#endif /* DEF_RDMAVT_INCQP_H */