extern spinlock_t ib_uverbs_idr_lock;
extern struct idr ib_uverbs_pd_idr;
+extern struct idr ib_uverbs_shpd_idr;
extern struct idr ib_uverbs_mr_idr;
extern struct idr ib_uverbs_mw_idr;
extern struct idr ib_uverbs_ah_idr;
IB_UVERBS_DECLARE_CMD(create_xsrq);
IB_UVERBS_DECLARE_CMD(open_xrcd);
IB_UVERBS_DECLARE_CMD(close_xrcd);
+IB_UVERBS_DECLARE_CMD(alloc_shpd);
+IB_UVERBS_DECLARE_CMD(share_pd);
#define IB_UVERBS_DECLARE_EX_CMD(name) \
int ib_uverbs_ex_##name(struct ib_uverbs_file *file, \
};
static struct uverbs_lock_class pd_lock_class = { .name = "PD-uobj" };
+static struct uverbs_lock_class shpd_lock_class = { .name = "SHPD-uobj" };
static struct uverbs_lock_class mr_lock_class = { .name = "MR-uobj" };
static struct uverbs_lock_class mw_lock_class = { .name = "MW-uobj" };
static struct uverbs_lock_class cq_lock_class = { .name = "CQ-uobj" };
put_uobj_read(pd->uobject);
}
+static void put_pd_write(struct ib_pd *pd)
+{
+ put_uobj_write(pd->uobject);
+}
+
static struct ib_cq *idr_read_cq(int cq_handle, struct ib_ucontext *context, int nested)
{
return idr_read_obj(&ib_uverbs_cq_idr, cq_handle, context, nested);
pd->device = file->device->ib_dev;
pd->uobject = uobj;
+ pd->shpd = NULL; /* will be filled in when pd is shared */
atomic_set(&pd->usecnt, 0);
uobj->object = pd;
return ret;
}
+ssize_t ib_uverbs_alloc_shpd(struct ib_uverbs_file *file,
+ const char __user *buf,
+ int in_len, int out_len)
+{
+ struct ib_uverbs_alloc_shpd cmd;
+ struct ib_uverbs_alloc_shpd_resp resp;
+ struct ib_udata udata;
+ struct ib_uobject *uobj;
+ struct ib_uobject *shuobj = NULL;
+ struct ib_pd *pd;
+ struct ib_shpd *shpd = NULL;
+ int ret;
+
+ if (!file->device->ib_dev->alloc_shpd ||
+ !file->device->ib_dev->share_pd ||
+ !file->device->ib_dev->remove_shpd)
+ return -ENOSYS;
+
+ if (copy_from_user(&cmd, buf, sizeof(cmd)))
+ return -EFAULT;
+
+ INIT_UDATA(&udata, buf + sizeof(cmd),
+ (unsigned long) cmd.response + sizeof(resp),
+ in_len - sizeof(cmd), out_len - sizeof(resp));
+
+ uobj = idr_write_uobj(&ib_uverbs_pd_idr, cmd.pd_handle, file->ucontext);
+ if (!uobj)
+ return -EINVAL;
+
+ pd = uobj->object;
+
+ /* pd can be shared only once */
+ if (pd->shpd) {
+ ret = -EINVAL;
+ goto err_pd;
+ }
+
+
+ /* create a new uobj */
+ shuobj = kmalloc(sizeof(*shuobj), GFP_KERNEL);
+ if (!shuobj) {
+ ret = -ENOMEM;
+ goto err_pd;
+ }
+
+ init_uobj(shuobj, 0, 0/* global */, &shpd_lock_class);
+ down_write(&shuobj->mutex);
+
+ /* alloc shared pd from device driver */
+ shpd = file->device->ib_dev->alloc_shpd(file->device->ib_dev, pd);
+ if (IS_ERR(shpd)) {
+ ret = PTR_ERR(shpd);
+ goto err_shobj;
+ }
+
+ shpd->device = file->device->ib_dev;
+ shpd->uobject = shuobj;
+ shpd->share_key = cmd.share_key;
+
+ shuobj->object = shpd;
+
+ /* link new uobj to device level list */
+ ret = idr_add_uobj(&ib_uverbs_shpd_idr, shuobj);
+ if (ret)
+ goto err_idr;
+
+ /* return pd_handle */
+ memset(&resp, 0, sizeof(resp));
+ resp.shpd_handle = shuobj->id;
+
+ if (copy_to_user((void __user *) (unsigned long) cmd.response,
+ &resp, sizeof(resp))) {
+ ret = -EFAULT;
+ goto err_copy;
+ }
+
+ shuobj->live = 1;
+
+ /* mark pd as shared */
+ pd->shpd = shpd;
+
+ up_write(&shuobj->mutex);
+ put_pd_write(pd);
+
+ return in_len;
+
+err_copy:
+ idr_remove_uobj(&ib_uverbs_shpd_idr, shuobj);
+
+err_idr:
+ file->device->ib_dev->remove_shpd(file->device->ib_dev, shpd, 1);
+
+err_shobj:
+ put_uobj_write(shuobj);
+
+err_pd:
+ put_pd_write(pd);
+
+ return ret;
+}
+
+ssize_t ib_uverbs_share_pd(struct ib_uverbs_file *file,
+ const char __user *buf,
+ int in_len, int out_len)
+{
+ struct ib_uverbs_share_pd cmd;
+ struct ib_uverbs_share_pd_resp resp;
+ struct ib_udata udata;
+ struct ib_uobject *uobj = NULL;
+ struct ib_uobject *shuobj;
+ struct ib_pd *pd;
+ struct ib_shpd *shpd;
+ int ret;
+
+ if (copy_from_user(&cmd, buf, sizeof(cmd)))
+ return -EFAULT;
+
+ INIT_UDATA(&udata, buf + sizeof(cmd),
+ (unsigned long) cmd.response + sizeof(resp),
+ in_len - sizeof(cmd), out_len - sizeof(resp));
+
+ /* get global uobject for the shared pd */
+ shuobj = idr_read_uobj(&ib_uverbs_shpd_idr, cmd.shpd_handle,
+ 0/* global */, 0);
+ if (!shuobj)
+ return -EINVAL;
+
+ shpd = shuobj->object;
+
+ /* check if the key matches */
+ if (shpd->share_key != cmd.share_key) {
+ pr_warn("WARNING : invalid shared pd key\n");
+ ret = -EINVAL;
+ goto err_putshpd;
+ }
+
+ /* check if the devices match */
+ if (strncmp(file->device->ib_dev->name, shpd->device->name,
+ IB_DEVICE_NAME_MAX)) {
+ ret = -EINVAL;
+ goto err_putshpd;
+ }
+
+ /* allocate a new user object */
+ uobj = kmalloc(sizeof(*uobj), GFP_KERNEL);
+ if (!uobj) {
+ ret = -ENOMEM;
+ goto err_putshpd;
+ }
+
+
+ init_uobj(uobj, 0, file->ucontext, &pd_lock_class);
+ down_write(&uobj->mutex);
+
+ /* share the pd at device driver level */
+ pd = file->device->ib_dev->share_pd(file->device->ib_dev,
+ file->ucontext, &udata, shpd);
+ if (IS_ERR(pd)) {
+ ret = PTR_ERR(pd);
+ goto err_putuobj;
+ }
+
+ pd->device = file->device->ib_dev;
+ pd->uobject = uobj;
+ pd->shpd = shpd;
+ atomic_set(&pd->usecnt, 0);
+
+ /* initialize uobj and return pd_handle */
+ uobj->object = pd;
+ ret = idr_add_uobj(&ib_uverbs_pd_idr, uobj);
+ if (ret)
+ goto err_idr;
+
+ memset(&resp, 0, sizeof(resp));
+ resp.pd_handle = uobj->id;
+
+ if (copy_to_user((void __user *) (unsigned long) cmd.response,
+ &resp, sizeof(resp))) {
+ ret = -EFAULT;
+ goto err_copy;
+ }
+
+ mutex_lock(&file->mutex);
+ list_add_tail(&uobj->list, &file->ucontext->pd_list);
+ mutex_unlock(&file->mutex);
+
+ uobj->live = 1;
+ atomic_inc(&shpd->shared);
+
+ up_write(&uobj->mutex);
+
+ put_uobj_read(shuobj);
+
+ return in_len;
+
+err_copy:
+ idr_remove_uobj(&ib_uverbs_pd_idr, uobj);
+
+err_idr:
+ ib_dealloc_pd(pd);
+
+err_putuobj:
+
+ put_uobj_write(uobj);
+
+err_putshpd:
+ put_uobj_read(shuobj);
+
+ return ret;
+}
+
ssize_t ib_uverbs_dealloc_pd(struct ib_uverbs_file *file,
const char __user *buf,
int in_len, int out_len)
{
struct ib_uverbs_dealloc_pd cmd;
struct ib_uobject *uobj;
- int ret;
+ int ret = 0;
+ struct ib_uobject *shuobj = 0;
+ struct ib_pd *pd = NULL;
+ struct ib_shpd *shpd = NULL;
- if (copy_from_user(&cmd, buf, sizeof cmd))
+ if (copy_from_user(&cmd, buf, sizeof(cmd)))
return -EFAULT;
uobj = idr_write_uobj(&ib_uverbs_pd_idr, cmd.pd_handle, file->ucontext);
if (!uobj)
return -EINVAL;
+ pd = uobj->object;
+
+ /* is pd shared ?*/
+ if (pd->shpd) {
+ shpd = pd->shpd;
+ shuobj = shpd->uobject;
+ }
+
ret = ib_dealloc_pd(uobj->object);
if (!ret)
uobj->live = 0;
+ if (!ret && shpd) {
+ down_write(&shuobj->mutex);
+ atomic_dec(&shpd->shared);
+
+ /* if this shpd is no longer shared */
+ if (!atomic_read(&shpd->shared)) {
+ /* free the shpd info from device driver */
+ file->device->ib_dev->remove_shpd(file->device->ib_dev,
+ shpd, 0);
+ shuobj->live = 0;
+ up_write(&shuobj->mutex);
+ idr_remove_uobj(&ib_uverbs_shpd_idr, shuobj);
+ put_uobj(shuobj);
+ } else
+ up_write(&shuobj->mutex);
+ }
+
put_uobj_write(uobj);
if (ret)
DEFINE_SPINLOCK(ib_uverbs_idr_lock);
DEFINE_IDR(ib_uverbs_pd_idr);
+DEFINE_IDR(ib_uverbs_shpd_idr);
DEFINE_IDR(ib_uverbs_mr_idr);
DEFINE_IDR(ib_uverbs_mw_idr);
DEFINE_IDR(ib_uverbs_ah_idr);
[IB_USER_VERBS_CMD_CLOSE_XRCD] = ib_uverbs_close_xrcd,
[IB_USER_VERBS_CMD_CREATE_XSRQ] = ib_uverbs_create_xsrq,
[IB_USER_VERBS_CMD_OPEN_QP] = ib_uverbs_open_qp,
+ /*
+ * Upstream verbs index 0-40 above.
+ * Oracle additions to verbs start here with some
+ * space (index 46)
+ */
+ [IB_USER_VERBS_CMD_ALLOC_SHPD] = ib_uverbs_alloc_shpd,
+ [IB_USER_VERBS_CMD_SHARE_PD] = ib_uverbs_share_pd,
};
static int (*uverbs_ex_cmd_table[])(struct ib_uverbs_file *file,
if (overflow_maj)
unregister_chrdev_region(overflow_maj, IB_UVERBS_MAX_DEVICES);
idr_destroy(&ib_uverbs_pd_idr);
+ idr_destroy(&ib_uverbs_shpd_idr);
idr_destroy(&ib_uverbs_mr_idr);
idr_destroy(&ib_uverbs_mw_idr);
idr_destroy(&ib_uverbs_ah_idr);
dev->ibdev.post_send = c2_post_send;
dev->ibdev.post_recv = c2_post_receive;
+ dev->ibdev.alloc_shpd = NULL;
+ dev->ibdev.share_pd = NULL;
+ dev->ibdev.remove_shpd = NULL;
+
dev->ibdev.iwcm = kmalloc(sizeof(*dev->ibdev.iwcm), GFP_KERNEL);
if (dev->ibdev.iwcm == NULL) {
ret = -ENOMEM;
shca->ib_device.process_mad = ehca_process_mad;
shca->ib_device.mmap = ehca_mmap;
shca->ib_device.dma_ops = &ehca_dma_mapping_ops;
+ shca->ib_device.alloc_shpd = NULL;
+ shca->ib_device.share_pd = NULL;
+ shca->ib_device.remove_shpd = NULL;
if (EHCA_BMASK_GET(HCA_CAP_SRQ, shca->hca_cap)) {
shca->ib_device.uverbs_cmd_mask |=
dev->process_mad = ipath_process_mad;
dev->mmap = ipath_mmap;
dev->dma_ops = &ipath_dma_mapping_ops;
+ dev->alloc_shpd = NULL;
+ dev->share_pd = NULL;
+ dev->remove_shpd = NULL;
snprintf(dev->node_desc, sizeof(dev->node_desc),
IPATH_IDSTR " %s", init_utsname()->nodename);
return &pd->ibpd;
}
+static struct ib_shpd *mlx4_ib_alloc_shpd(struct ib_device *ibdev,
+ struct ib_pd *pd)
+{
+ struct mlx4_ib_shpd *shpd;
+
+ shpd = kzalloc(sizeof(*shpd), GFP_KERNEL);
+ if (!shpd)
+ return ERR_PTR(-ENOMEM);
+
+ shpd->pdn = to_mpd(pd)->pdn;
+
+ return &shpd->ibshpd;
+}
+
+static struct ib_pd *mlx4_ib_share_pd(struct ib_device *ibdev,
+ struct ib_ucontext *context,
+ struct ib_udata *udata,
+ struct ib_shpd *shpd)
+{
+ struct mlx4_ib_pd *pd;
+
+ pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return ERR_PTR(-ENOMEM);
+
+ pd->pdn = to_mshpd(shpd)->pdn;
+
+ if (context)
+ if (ib_copy_to_udata(udata, &pd->pdn, sizeof(__u32))) {
+ kfree(pd);
+ return ERR_PTR(-EFAULT);
+ }
+
+ return &pd->ibpd;
+}
+
+static int mlx4_ib_remove_shpd(struct ib_device *ibdev,
+ struct ib_shpd *shpd, int atinit)
+{
+
+ /*
+ * if remove shpd is called during shpd creation time itself, then
+ * pd should not be freed from device. it will be freed when deall_pd
+ * is called
+ */
+ if (!atinit)
+ mlx4_pd_free(to_mdev(ibdev)->dev, to_mshpd(shpd)->pdn);
+ kfree(shpd);
+
+ return 0;
+}
+
static int mlx4_ib_dealloc_pd(struct ib_pd *pd)
{
- mlx4_pd_free(to_mdev(pd->device)->dev, to_mpd(pd)->pdn);
- kfree(pd);
+ struct ib_shpd *shpd = pd->shpd;
+
+ if (shpd) {
+ /*
+ * if pd is shared, pd number will be freed by remove_shpd call
+ */
+ kfree(pd);
+ } else {
+ mlx4_pd_free(to_mdev(pd->device)->dev, to_mpd(pd)->pdn);
+ kfree(pd);
+ }
return 0;
}
(1ull << IB_USER_VERBS_CMD_QUERY_SRQ) |
(1ull << IB_USER_VERBS_CMD_DESTROY_SRQ) |
(1ull << IB_USER_VERBS_CMD_CREATE_XSRQ) |
- (1ull << IB_USER_VERBS_CMD_OPEN_QP);
+ (1ull << IB_USER_VERBS_CMD_OPEN_QP) |
+ (1ull << IB_USER_VERBS_CMD_ALLOC_SHPD) |
+ (1ull << IB_USER_VERBS_CMD_SHARE_PD);
ibdev->ib_dev.query_device = mlx4_ib_query_device;
ibdev->ib_dev.query_port = mlx4_ib_query_port;
ibdev->ib_dev.attach_mcast = mlx4_ib_mcg_attach;
ibdev->ib_dev.detach_mcast = mlx4_ib_mcg_detach;
ibdev->ib_dev.process_mad = mlx4_ib_process_mad;
+ ibdev->ib_dev.alloc_shpd = mlx4_ib_alloc_shpd;
+ ibdev->ib_dev.share_pd = mlx4_ib_share_pd;
+ ibdev->ib_dev.remove_shpd = mlx4_ib_remove_shpd;
if (!mlx4_is_slave(ibdev->dev)) {
ibdev->ib_dev.alloc_fmr = mlx4_ib_fmr_alloc;
u32 pdn;
};
+struct mlx4_ib_shpd {
+ struct ib_shpd ibshpd;
+ u32 pdn;
+};
+
struct mlx4_ib_xrcd {
struct ib_xrcd ibxrcd;
u32 xrcdn;
return container_of(ibpd, struct mlx4_ib_pd, ibpd);
}
+static inline struct mlx4_ib_shpd *to_mshpd(struct ib_shpd *ibshpd)
+{
+ return container_of(ibshpd, struct mlx4_ib_shpd, ibshpd);
+}
+
static inline struct mlx4_ib_xrcd *to_mxrcd(struct ib_xrcd *ibxrcd)
{
return container_of(ibxrcd, struct mlx4_ib_xrcd, ibxrcd);
dev->ib_dev.post_recv = mthca_tavor_post_receive;
}
+ dev->ib_dev.alloc_shpd = NULL;
+ dev->ib_dev.share_pd = NULL;
+ dev->ib_dev.remove_shpd = NULL;
+
mutex_init(&dev->cap_mask_mutex);
ret = ib_register_device(&dev->ib_dev, NULL);
struct ib_pd {
struct ib_device *device;
struct ib_uobject *uobject;
+ struct ib_shpd *shpd; /* global uobj id if this
+ pd is shared */
atomic_t usecnt; /* count all resources */
};
+struct ib_shpd {
+ struct ib_device *device;
+ struct ib_uobject *uobject;
+ atomic_t shared; /* count procs sharing the pd*/
+ u64 share_key;
+};
+
struct ib_xrcd {
struct ib_device *device;
atomic_t usecnt; /* count all exposed resources */
int (*destroy_flow)(struct ib_flow *flow_id);
int (*check_mr_status)(struct ib_mr *mr, u32 check_mask,
struct ib_mr_status *mr_status);
+ struct ib_shpd *(*alloc_shpd)(struct ib_device *ibdev,
+ struct ib_pd *pd);
+ struct ib_pd *(*share_pd)(struct ib_device *ibdev,
+ struct ib_ucontext *context,
+ struct ib_udata *udata,
+ struct ib_shpd *shpd);
+ int (*remove_shpd)(struct ib_device *ibdev,
+ struct ib_shpd *shpd,
+ int atinit);
struct ib_dma_mapping_ops *dma_ops;
IB_USER_VERBS_CMD_OPEN_XRCD,
IB_USER_VERBS_CMD_CLOSE_XRCD,
IB_USER_VERBS_CMD_CREATE_XSRQ,
- IB_USER_VERBS_CMD_OPEN_QP,
+ IB_USER_VERBS_CMD_OPEN_QP, /* =40 */
+ /*
+ * Note: 0-40 verbs defined above
+ * Start oracle verb additions leaving a gap
+ * for upstream verbs growth.
+ *
+ * We start at 46 which is the starting value used
+ * for these verbs in UEK2 and add them in same
+ * order.
+ *
+ * (Even if we dont care about aligning with UEK2 values,
+ * cannot go beyond 63 because of "struct ib_device"
+ * has uverbs_cmd_mask which is 64 bits wide!)
+ */
+#define IB_USER_VERBS_CMD_ORACLE_ADDS_START 46
+ IB_USER_VERBS_CMD_ALLOC_SHPD = IB_USER_VERBS_CMD_ORACLE_ADDS_START,
+ /* =46 */
+ IB_USER_VERBS_CMD_SHARE_PD, /* =47 */
};
enum {
__u32 pd_handle;
};
+struct ib_uverbs_alloc_shpd {
+ __u64 response;
+ __u32 pd_handle;
+ __u64 share_key;
+};
+
+struct ib_uverbs_alloc_shpd_resp {
+ __u32 shpd_handle;
+};
+
+struct ib_uverbs_share_pd {
+ __u64 response;
+ __u32 shpd_handle;
+ __u64 share_key;
+};
+
+struct ib_uverbs_share_pd_resp {
+ __u32 pd_handle;
+};
+
struct ib_uverbs_dealloc_pd {
__u32 pd_handle;
};