diff options
Diffstat (limited to 'drivers/infiniband/core/uverbs_cmd.c')
| -rw-r--r-- | drivers/infiniband/core/uverbs_cmd.c | 1297 | 
1 files changed, 1137 insertions, 160 deletions
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index b342248aec0..ea6203ee7bc 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -40,21 +40,22 @@  #include <asm/uaccess.h>  #include "uverbs.h" - -static struct lock_class_key pd_lock_key; -static struct lock_class_key mr_lock_key; -static struct lock_class_key cq_lock_key; -static struct lock_class_key qp_lock_key; -static struct lock_class_key ah_lock_key; -static struct lock_class_key srq_lock_key; - -#define INIT_UDATA(udata, ibuf, obuf, ilen, olen)			\ -	do {								\ -		(udata)->inbuf  = (void __user *) (ibuf);		\ -		(udata)->outbuf = (void __user *) (obuf);		\ -		(udata)->inlen  = (ilen);				\ -		(udata)->outlen = (olen);				\ -	} while (0) +#include "core_priv.h" + +struct uverbs_lock_class { +	struct lock_class_key	key; +	char			name[16]; +}; + +static struct uverbs_lock_class pd_lock_class	= { .name = "PD-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" }; +static struct uverbs_lock_class qp_lock_class	= { .name = "QP-uobj" }; +static struct uverbs_lock_class ah_lock_class	= { .name = "AH-uobj" }; +static struct uverbs_lock_class srq_lock_class	= { .name = "SRQ-uobj" }; +static struct uverbs_lock_class xrcd_lock_class = { .name = "XRCD-uobj" }; +static struct uverbs_lock_class rule_lock_class = { .name = "RULE-uobj" };  /*   * The ib_uobject locking scheme is as follows: @@ -82,13 +83,13 @@ static struct lock_class_key srq_lock_key;   */  static void init_uobj(struct ib_uobject *uobj, u64 user_handle, -		      struct ib_ucontext *context, struct lock_class_key *key) +		      struct ib_ucontext *context, struct uverbs_lock_class *c)  {  	uobj->user_handle = user_handle;  	uobj->context     = context;  	kref_init(&uobj->ref);  	init_rwsem(&uobj->mutex); -	lockdep_set_class(&uobj->mutex, key); +	lockdep_set_class_and_name(&uobj->mutex, &c->key, c->name);  	uobj->live        = 0;  } @@ -118,18 +119,17 @@ static int idr_add_uobj(struct idr *idr, struct ib_uobject *uobj)  {  	int ret; -retry: -	if (!idr_pre_get(idr, GFP_KERNEL)) -		return -ENOMEM; - +	idr_preload(GFP_KERNEL);  	spin_lock(&ib_uverbs_idr_lock); -	ret = idr_get_new(idr, uobj, &uobj->id); -	spin_unlock(&ib_uverbs_idr_lock); -	if (ret == -EAGAIN) -		goto retry; +	ret = idr_alloc(idr, uobj, 0, 0, GFP_NOWAIT); +	if (ret >= 0) +		uobj->id = ret; -	return ret; +	spin_unlock(&ib_uverbs_idr_lock); +	idr_preload_end(); + +	return ret < 0 ? ret : 0;  }  void idr_remove_uobj(struct idr *idr, struct ib_uobject *uobj) @@ -240,11 +240,24 @@ static struct ib_qp *idr_read_qp(int qp_handle, struct ib_ucontext *context)  	return idr_read_obj(&ib_uverbs_qp_idr, qp_handle, context, 0);  } +static struct ib_qp *idr_write_qp(int qp_handle, struct ib_ucontext *context) +{ +	struct ib_uobject *uobj; + +	uobj = idr_write_uobj(&ib_uverbs_qp_idr, qp_handle, context); +	return uobj ? uobj->object : NULL; +} +  static void put_qp_read(struct ib_qp *qp)  {  	put_uobj_read(qp->uobject);  } +static void put_qp_write(struct ib_qp *qp) +{ +	put_uobj_write(qp->uobject); +} +  static struct ib_srq *idr_read_srq(int srq_handle, struct ib_ucontext *context)  {  	return idr_read_obj(&ib_uverbs_srq_idr, srq_handle, context, 0); @@ -255,6 +268,18 @@ static void put_srq_read(struct ib_srq *srq)  	put_uobj_read(srq->uobject);  } +static struct ib_xrcd *idr_read_xrcd(int xrcd_handle, struct ib_ucontext *context, +				     struct ib_uobject **uobj) +{ +	*uobj = idr_read_uobj(&ib_uverbs_xrcd_idr, xrcd_handle, context, 0); +	return *uobj ? (*uobj)->object : NULL; +} + +static void put_xrcd_read(struct ib_uobject *uobj) +{ +	put_uobj_read(uobj); +} +  ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,  			      const char __user *buf,  			      int in_len, int out_len) @@ -298,11 +323,13 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file,  	INIT_LIST_HEAD(&ucontext->qp_list);  	INIT_LIST_HEAD(&ucontext->srq_list);  	INIT_LIST_HEAD(&ucontext->ah_list); +	INIT_LIST_HEAD(&ucontext->xrcd_list); +	INIT_LIST_HEAD(&ucontext->rule_list);  	ucontext->closing = 0;  	resp.num_comp_vectors = file->device->num_comp_vectors; -	ret = get_unused_fd(); +	ret = get_unused_fd_flags(O_CLOEXEC);  	if (ret < 0)  		goto err_free;  	resp.async_fd = ret; @@ -495,7 +522,7 @@ ssize_t ib_uverbs_alloc_pd(struct ib_uverbs_file *file,  	if (!uobj)  		return -ENOMEM; -	init_uobj(uobj, 0, file->ucontext, &pd_lock_key); +	init_uobj(uobj, 0, file->ucontext, &pd_lock_class);  	down_write(&uobj->mutex);  	pd = file->device->ib_dev->alloc_pd(file->device->ib_dev, @@ -579,6 +606,305 @@ ssize_t ib_uverbs_dealloc_pd(struct ib_uverbs_file *file,  	return in_len;  } +struct xrcd_table_entry { +	struct rb_node  node; +	struct ib_xrcd *xrcd; +	struct inode   *inode; +}; + +static int xrcd_table_insert(struct ib_uverbs_device *dev, +			    struct inode *inode, +			    struct ib_xrcd *xrcd) +{ +	struct xrcd_table_entry *entry, *scan; +	struct rb_node **p = &dev->xrcd_tree.rb_node; +	struct rb_node *parent = NULL; + +	entry = kmalloc(sizeof *entry, GFP_KERNEL); +	if (!entry) +		return -ENOMEM; + +	entry->xrcd  = xrcd; +	entry->inode = inode; + +	while (*p) { +		parent = *p; +		scan = rb_entry(parent, struct xrcd_table_entry, node); + +		if (inode < scan->inode) { +			p = &(*p)->rb_left; +		} else if (inode > scan->inode) { +			p = &(*p)->rb_right; +		} else { +			kfree(entry); +			return -EEXIST; +		} +	} + +	rb_link_node(&entry->node, parent, p); +	rb_insert_color(&entry->node, &dev->xrcd_tree); +	igrab(inode); +	return 0; +} + +static struct xrcd_table_entry *xrcd_table_search(struct ib_uverbs_device *dev, +						  struct inode *inode) +{ +	struct xrcd_table_entry *entry; +	struct rb_node *p = dev->xrcd_tree.rb_node; + +	while (p) { +		entry = rb_entry(p, struct xrcd_table_entry, node); + +		if (inode < entry->inode) +			p = p->rb_left; +		else if (inode > entry->inode) +			p = p->rb_right; +		else +			return entry; +	} + +	return NULL; +} + +static struct ib_xrcd *find_xrcd(struct ib_uverbs_device *dev, struct inode *inode) +{ +	struct xrcd_table_entry *entry; + +	entry = xrcd_table_search(dev, inode); +	if (!entry) +		return NULL; + +	return entry->xrcd; +} + +static void xrcd_table_delete(struct ib_uverbs_device *dev, +			      struct inode *inode) +{ +	struct xrcd_table_entry *entry; + +	entry = xrcd_table_search(dev, inode); +	if (entry) { +		iput(inode); +		rb_erase(&entry->node, &dev->xrcd_tree); +		kfree(entry); +	} +} + +ssize_t ib_uverbs_open_xrcd(struct ib_uverbs_file *file, +			    const char __user *buf, int in_len, +			    int out_len) +{ +	struct ib_uverbs_open_xrcd	cmd; +	struct ib_uverbs_open_xrcd_resp	resp; +	struct ib_udata			udata; +	struct ib_uxrcd_object         *obj; +	struct ib_xrcd                 *xrcd = NULL; +	struct fd			f = {NULL, 0}; +	struct inode                   *inode = NULL; +	int				ret = 0; +	int				new_xrcd = 0; + +	if (out_len < sizeof resp) +		return -ENOSPC; + +	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); + +	mutex_lock(&file->device->xrcd_tree_mutex); + +	if (cmd.fd != -1) { +		/* search for file descriptor */ +		f = fdget(cmd.fd); +		if (!f.file) { +			ret = -EBADF; +			goto err_tree_mutex_unlock; +		} + +		inode = file_inode(f.file); +		xrcd = find_xrcd(file->device, inode); +		if (!xrcd && !(cmd.oflags & O_CREAT)) { +			/* no file descriptor. Need CREATE flag */ +			ret = -EAGAIN; +			goto err_tree_mutex_unlock; +		} + +		if (xrcd && cmd.oflags & O_EXCL) { +			ret = -EINVAL; +			goto err_tree_mutex_unlock; +		} +	} + +	obj = kmalloc(sizeof *obj, GFP_KERNEL); +	if (!obj) { +		ret = -ENOMEM; +		goto err_tree_mutex_unlock; +	} + +	init_uobj(&obj->uobject, 0, file->ucontext, &xrcd_lock_class); + +	down_write(&obj->uobject.mutex); + +	if (!xrcd) { +		xrcd = file->device->ib_dev->alloc_xrcd(file->device->ib_dev, +							file->ucontext, &udata); +		if (IS_ERR(xrcd)) { +			ret = PTR_ERR(xrcd); +			goto err; +		} + +		xrcd->inode   = inode; +		xrcd->device  = file->device->ib_dev; +		atomic_set(&xrcd->usecnt, 0); +		mutex_init(&xrcd->tgt_qp_mutex); +		INIT_LIST_HEAD(&xrcd->tgt_qp_list); +		new_xrcd = 1; +	} + +	atomic_set(&obj->refcnt, 0); +	obj->uobject.object = xrcd; +	ret = idr_add_uobj(&ib_uverbs_xrcd_idr, &obj->uobject); +	if (ret) +		goto err_idr; + +	memset(&resp, 0, sizeof resp); +	resp.xrcd_handle = obj->uobject.id; + +	if (inode) { +		if (new_xrcd) { +			/* create new inode/xrcd table entry */ +			ret = xrcd_table_insert(file->device, inode, xrcd); +			if (ret) +				goto err_insert_xrcd; +		} +		atomic_inc(&xrcd->usecnt); +	} + +	if (copy_to_user((void __user *) (unsigned long) cmd.response, +			 &resp, sizeof resp)) { +		ret = -EFAULT; +		goto err_copy; +	} + +	if (f.file) +		fdput(f); + +	mutex_lock(&file->mutex); +	list_add_tail(&obj->uobject.list, &file->ucontext->xrcd_list); +	mutex_unlock(&file->mutex); + +	obj->uobject.live = 1; +	up_write(&obj->uobject.mutex); + +	mutex_unlock(&file->device->xrcd_tree_mutex); +	return in_len; + +err_copy: +	if (inode) { +		if (new_xrcd) +			xrcd_table_delete(file->device, inode); +		atomic_dec(&xrcd->usecnt); +	} + +err_insert_xrcd: +	idr_remove_uobj(&ib_uverbs_xrcd_idr, &obj->uobject); + +err_idr: +	ib_dealloc_xrcd(xrcd); + +err: +	put_uobj_write(&obj->uobject); + +err_tree_mutex_unlock: +	if (f.file) +		fdput(f); + +	mutex_unlock(&file->device->xrcd_tree_mutex); + +	return ret; +} + +ssize_t ib_uverbs_close_xrcd(struct ib_uverbs_file *file, +			     const char __user *buf, int in_len, +			     int out_len) +{ +	struct ib_uverbs_close_xrcd cmd; +	struct ib_uobject           *uobj; +	struct ib_xrcd              *xrcd = NULL; +	struct inode                *inode = NULL; +	struct ib_uxrcd_object      *obj; +	int                         live; +	int                         ret = 0; + +	if (copy_from_user(&cmd, buf, sizeof cmd)) +		return -EFAULT; + +	mutex_lock(&file->device->xrcd_tree_mutex); +	uobj = idr_write_uobj(&ib_uverbs_xrcd_idr, cmd.xrcd_handle, file->ucontext); +	if (!uobj) { +		ret = -EINVAL; +		goto out; +	} + +	xrcd  = uobj->object; +	inode = xrcd->inode; +	obj   = container_of(uobj, struct ib_uxrcd_object, uobject); +	if (atomic_read(&obj->refcnt)) { +		put_uobj_write(uobj); +		ret = -EBUSY; +		goto out; +	} + +	if (!inode || atomic_dec_and_test(&xrcd->usecnt)) { +		ret = ib_dealloc_xrcd(uobj->object); +		if (!ret) +			uobj->live = 0; +	} + +	live = uobj->live; +	if (inode && ret) +		atomic_inc(&xrcd->usecnt); + +	put_uobj_write(uobj); + +	if (ret) +		goto out; + +	if (inode && !live) +		xrcd_table_delete(file->device, inode); + +	idr_remove_uobj(&ib_uverbs_xrcd_idr, uobj); +	mutex_lock(&file->mutex); +	list_del(&uobj->list); +	mutex_unlock(&file->mutex); + +	put_uobj(uobj); +	ret = in_len; + +out: +	mutex_unlock(&file->device->xrcd_tree_mutex); +	return ret; +} + +void ib_uverbs_dealloc_xrcd(struct ib_uverbs_device *dev, +			    struct ib_xrcd *xrcd) +{ +	struct inode *inode; + +	inode = xrcd->inode; +	if (inode && !atomic_dec_and_test(&xrcd->usecnt)) +		return; + +	ib_dealloc_xrcd(xrcd); + +	if (inode) +		xrcd_table_delete(dev, inode); +} +  ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file,  			 const char __user *buf, int in_len,  			 int out_len) @@ -604,19 +930,15 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file,  	if ((cmd.start & ~PAGE_MASK) != (cmd.hca_va & ~PAGE_MASK))  		return -EINVAL; -	/* -	 * Local write permission is required if remote write or -	 * remote atomic permission is also requested. -	 */ -	if (cmd.access_flags & (IB_ACCESS_REMOTE_ATOMIC | IB_ACCESS_REMOTE_WRITE) && -	    !(cmd.access_flags & IB_ACCESS_LOCAL_WRITE)) -		return -EINVAL; +	ret = ib_check_mr_access(cmd.access_flags); +	if (ret) +		return ret;  	uobj = kmalloc(sizeof *uobj, GFP_KERNEL);  	if (!uobj)  		return -ENOMEM; -	init_uobj(uobj, 0, file->ucontext, &mr_lock_key); +	init_uobj(uobj, 0, file->ucontext, &mr_lock_class);  	down_write(&uobj->mutex);  	pd = idr_read_pd(cmd.pd_handle, file->ucontext); @@ -718,6 +1040,126 @@ ssize_t ib_uverbs_dereg_mr(struct ib_uverbs_file *file,  	return in_len;  } +ssize_t ib_uverbs_alloc_mw(struct ib_uverbs_file *file, +			 const char __user *buf, int in_len, +			 int out_len) +{ +	struct ib_uverbs_alloc_mw      cmd; +	struct ib_uverbs_alloc_mw_resp resp; +	struct ib_uobject             *uobj; +	struct ib_pd                  *pd; +	struct ib_mw                  *mw; +	int                            ret; + +	if (out_len < sizeof(resp)) +		return -ENOSPC; + +	if (copy_from_user(&cmd, buf, sizeof(cmd))) +		return -EFAULT; + +	uobj = kmalloc(sizeof(*uobj), GFP_KERNEL); +	if (!uobj) +		return -ENOMEM; + +	init_uobj(uobj, 0, file->ucontext, &mw_lock_class); +	down_write(&uobj->mutex); + +	pd = idr_read_pd(cmd.pd_handle, file->ucontext); +	if (!pd) { +		ret = -EINVAL; +		goto err_free; +	} + +	mw = pd->device->alloc_mw(pd, cmd.mw_type); +	if (IS_ERR(mw)) { +		ret = PTR_ERR(mw); +		goto err_put; +	} + +	mw->device  = pd->device; +	mw->pd      = pd; +	mw->uobject = uobj; +	atomic_inc(&pd->usecnt); + +	uobj->object = mw; +	ret = idr_add_uobj(&ib_uverbs_mw_idr, uobj); +	if (ret) +		goto err_unalloc; + +	memset(&resp, 0, sizeof(resp)); +	resp.rkey      = mw->rkey; +	resp.mw_handle = uobj->id; + +	if (copy_to_user((void __user *)(unsigned long)cmd.response, +			 &resp, sizeof(resp))) { +		ret = -EFAULT; +		goto err_copy; +	} + +	put_pd_read(pd); + +	mutex_lock(&file->mutex); +	list_add_tail(&uobj->list, &file->ucontext->mw_list); +	mutex_unlock(&file->mutex); + +	uobj->live = 1; + +	up_write(&uobj->mutex); + +	return in_len; + +err_copy: +	idr_remove_uobj(&ib_uverbs_mw_idr, uobj); + +err_unalloc: +	ib_dealloc_mw(mw); + +err_put: +	put_pd_read(pd); + +err_free: +	put_uobj_write(uobj); +	return ret; +} + +ssize_t ib_uverbs_dealloc_mw(struct ib_uverbs_file *file, +			   const char __user *buf, int in_len, +			   int out_len) +{ +	struct ib_uverbs_dealloc_mw cmd; +	struct ib_mw               *mw; +	struct ib_uobject	   *uobj; +	int                         ret = -EINVAL; + +	if (copy_from_user(&cmd, buf, sizeof(cmd))) +		return -EFAULT; + +	uobj = idr_write_uobj(&ib_uverbs_mw_idr, cmd.mw_handle, file->ucontext); +	if (!uobj) +		return -EINVAL; + +	mw = uobj->object; + +	ret = ib_dealloc_mw(mw); +	if (!ret) +		uobj->live = 0; + +	put_uobj_write(uobj); + +	if (ret) +		return ret; + +	idr_remove_uobj(&ib_uverbs_mw_idr, uobj); + +	mutex_lock(&file->mutex); +	list_del(&uobj->list); +	mutex_unlock(&file->mutex); + +	put_uobj(uobj); + +	return in_len; +} +  ssize_t ib_uverbs_create_comp_channel(struct ib_uverbs_file *file,  				      const char __user *buf, int in_len,  				      int out_len) @@ -733,7 +1175,7 @@ ssize_t ib_uverbs_create_comp_channel(struct ib_uverbs_file *file,  	if (copy_from_user(&cmd, buf, sizeof cmd))  		return -EFAULT; -	ret = get_unused_fd(); +	ret = get_unused_fd_flags(O_CLOEXEC);  	if (ret < 0)  		return ret;  	resp.fd = ret; @@ -784,7 +1226,7 @@ ssize_t ib_uverbs_create_cq(struct ib_uverbs_file *file,  	if (!obj)  		return -ENOMEM; -	init_uobj(&obj->uobject, cmd.user_handle, file->ucontext, &cq_lock_key); +	init_uobj(&obj->uobject, cmd.user_handle, file->ucontext, &cq_lock_class);  	down_write(&obj->uobject.mutex);  	if (cmd.comp_channel >= 0) { @@ -893,68 +1335,81 @@ out:  	return ret ? ret : in_len;  } +static int copy_wc_to_user(void __user *dest, struct ib_wc *wc) +{ +	struct ib_uverbs_wc tmp; + +	tmp.wr_id		= wc->wr_id; +	tmp.status		= wc->status; +	tmp.opcode		= wc->opcode; +	tmp.vendor_err		= wc->vendor_err; +	tmp.byte_len		= wc->byte_len; +	tmp.ex.imm_data		= (__u32 __force) wc->ex.imm_data; +	tmp.qp_num		= wc->qp->qp_num; +	tmp.src_qp		= wc->src_qp; +	tmp.wc_flags		= wc->wc_flags; +	tmp.pkey_index		= wc->pkey_index; +	tmp.slid		= wc->slid; +	tmp.sl			= wc->sl; +	tmp.dlid_path_bits	= wc->dlid_path_bits; +	tmp.port_num		= wc->port_num; +	tmp.reserved		= 0; + +	if (copy_to_user(dest, &tmp, sizeof tmp)) +		return -EFAULT; + +	return 0; +} +  ssize_t ib_uverbs_poll_cq(struct ib_uverbs_file *file,  			  const char __user *buf, int in_len,  			  int out_len)  {  	struct ib_uverbs_poll_cq       cmd; -	struct ib_uverbs_poll_cq_resp *resp; +	struct ib_uverbs_poll_cq_resp  resp; +	u8 __user                     *header_ptr; +	u8 __user                     *data_ptr;  	struct ib_cq                  *cq; -	struct ib_wc                  *wc; -	int                            ret = 0; -	int                            i; -	int                            rsize; +	struct ib_wc                   wc; +	int                            ret;  	if (copy_from_user(&cmd, buf, sizeof cmd))  		return -EFAULT; -	wc = kmalloc(cmd.ne * sizeof *wc, GFP_KERNEL); -	if (!wc) -		return -ENOMEM; +	cq = idr_read_cq(cmd.cq_handle, file->ucontext, 0); +	if (!cq) +		return -EINVAL; -	rsize = sizeof *resp + cmd.ne * sizeof(struct ib_uverbs_wc); -	resp = kmalloc(rsize, GFP_KERNEL); -	if (!resp) { -		ret = -ENOMEM; -		goto out_wc; -	} +	/* we copy a struct ib_uverbs_poll_cq_resp to user space */ +	header_ptr = (void __user *)(unsigned long) cmd.response; +	data_ptr = header_ptr + sizeof resp; -	cq = idr_read_cq(cmd.cq_handle, file->ucontext, 0); -	if (!cq) { -		ret = -EINVAL; -		goto out; -	} +	memset(&resp, 0, sizeof resp); +	while (resp.count < cmd.ne) { +		ret = ib_poll_cq(cq, 1, &wc); +		if (ret < 0) +			goto out_put; +		if (!ret) +			break; -	resp->count = ib_poll_cq(cq, cmd.ne, wc); +		ret = copy_wc_to_user(data_ptr, &wc); +		if (ret) +			goto out_put; -	put_cq_read(cq); +		data_ptr += sizeof(struct ib_uverbs_wc); +		++resp.count; +	} -	for (i = 0; i < resp->count; i++) { -		resp->wc[i].wr_id 	   = wc[i].wr_id; -		resp->wc[i].status 	   = wc[i].status; -		resp->wc[i].opcode 	   = wc[i].opcode; -		resp->wc[i].vendor_err 	   = wc[i].vendor_err; -		resp->wc[i].byte_len 	   = wc[i].byte_len; -		resp->wc[i].ex.imm_data    = (__u32 __force) wc[i].ex.imm_data; -		resp->wc[i].qp_num 	   = wc[i].qp->qp_num; -		resp->wc[i].src_qp 	   = wc[i].src_qp; -		resp->wc[i].wc_flags 	   = wc[i].wc_flags; -		resp->wc[i].pkey_index 	   = wc[i].pkey_index; -		resp->wc[i].slid 	   = wc[i].slid; -		resp->wc[i].sl 		   = wc[i].sl; -		resp->wc[i].dlid_path_bits = wc[i].dlid_path_bits; -		resp->wc[i].port_num 	   = wc[i].port_num; -	} - -	if (copy_to_user((void __user *) (unsigned long) cmd.response, resp, rsize)) +	if (copy_to_user(header_ptr, &resp, sizeof resp)) {  		ret = -EFAULT; +		goto out_put; +	} -out: -	kfree(resp); +	ret = in_len; -out_wc: -	kfree(wc); -	return ret ? ret : in_len; +out_put: +	put_cq_read(cq); +	return ret;  }  ssize_t ib_uverbs_req_notify_cq(struct ib_uverbs_file *file, @@ -1039,9 +1494,12 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,  	struct ib_uverbs_create_qp_resp resp;  	struct ib_udata                 udata;  	struct ib_uqp_object           *obj; -	struct ib_pd                   *pd; -	struct ib_cq                   *scq, *rcq; -	struct ib_srq                  *srq; +	struct ib_device	       *device; +	struct ib_pd                   *pd = NULL; +	struct ib_xrcd		       *xrcd = NULL; +	struct ib_uobject	       *uninitialized_var(xrcd_uobj); +	struct ib_cq                   *scq = NULL, *rcq = NULL; +	struct ib_srq                  *srq = NULL;  	struct ib_qp                   *qp;  	struct ib_qp_init_attr          attr;  	int ret; @@ -1052,26 +1510,57 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,  	if (copy_from_user(&cmd, buf, sizeof cmd))  		return -EFAULT; +	if (cmd.qp_type == IB_QPT_RAW_PACKET && !capable(CAP_NET_RAW)) +		return -EPERM; +  	INIT_UDATA(&udata, buf + sizeof cmd,  		   (unsigned long) cmd.response + sizeof resp,  		   in_len - sizeof cmd, out_len - sizeof resp); -	obj = kmalloc(sizeof *obj, GFP_KERNEL); +	obj = kzalloc(sizeof *obj, GFP_KERNEL);  	if (!obj)  		return -ENOMEM; -	init_uobj(&obj->uevent.uobject, cmd.user_handle, file->ucontext, &qp_lock_key); +	init_uobj(&obj->uevent.uobject, cmd.user_handle, file->ucontext, &qp_lock_class);  	down_write(&obj->uevent.uobject.mutex); -	srq = cmd.is_srq ? idr_read_srq(cmd.srq_handle, file->ucontext) : NULL; -	pd  = idr_read_pd(cmd.pd_handle, file->ucontext); -	scq = idr_read_cq(cmd.send_cq_handle, file->ucontext, 0); -	rcq = cmd.recv_cq_handle == cmd.send_cq_handle ? -		scq : idr_read_cq(cmd.recv_cq_handle, file->ucontext, 1); +	if (cmd.qp_type == IB_QPT_XRC_TGT) { +		xrcd = idr_read_xrcd(cmd.pd_handle, file->ucontext, &xrcd_uobj); +		if (!xrcd) { +			ret = -EINVAL; +			goto err_put; +		} +		device = xrcd->device; +	} else { +		if (cmd.qp_type == IB_QPT_XRC_INI) { +			cmd.max_recv_wr = cmd.max_recv_sge = 0; +		} else { +			if (cmd.is_srq) { +				srq = idr_read_srq(cmd.srq_handle, file->ucontext); +				if (!srq || srq->srq_type != IB_SRQT_BASIC) { +					ret = -EINVAL; +					goto err_put; +				} +			} + +			if (cmd.recv_cq_handle != cmd.send_cq_handle) { +				rcq = idr_read_cq(cmd.recv_cq_handle, file->ucontext, 0); +				if (!rcq) { +					ret = -EINVAL; +					goto err_put; +				} +			} +		} -	if (!pd || !scq || !rcq || (cmd.is_srq && !srq)) { -		ret = -EINVAL; -		goto err_put; +		scq = idr_read_cq(cmd.send_cq_handle, file->ucontext, !!rcq); +		rcq = rcq ?: scq; +		pd  = idr_read_pd(cmd.pd_handle, file->ucontext); +		if (!pd || !scq) { +			ret = -EINVAL; +			goto err_put; +		} + +		device = pd->device;  	}  	attr.event_handler = ib_uverbs_qp_event_handler; @@ -1079,6 +1568,7 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,  	attr.send_cq       = scq;  	attr.recv_cq       = rcq;  	attr.srq           = srq; +	attr.xrcd	   = xrcd;  	attr.sq_sig_type   = cmd.sq_sig_all ? IB_SIGNAL_ALL_WR : IB_SIGNAL_REQ_WR;  	attr.qp_type       = cmd.qp_type;  	attr.create_flags  = 0; @@ -1093,26 +1583,35 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,  	INIT_LIST_HEAD(&obj->uevent.event_list);  	INIT_LIST_HEAD(&obj->mcast_list); -	qp = pd->device->create_qp(pd, &attr, &udata); +	if (cmd.qp_type == IB_QPT_XRC_TGT) +		qp = ib_create_qp(pd, &attr); +	else +		qp = device->create_qp(pd, &attr, &udata); +  	if (IS_ERR(qp)) {  		ret = PTR_ERR(qp);  		goto err_put;  	} -	qp->device     	  = pd->device; -	qp->pd         	  = pd; -	qp->send_cq    	  = attr.send_cq; -	qp->recv_cq    	  = attr.recv_cq; -	qp->srq	       	  = attr.srq; -	qp->uobject       = &obj->uevent.uobject; -	qp->event_handler = attr.event_handler; -	qp->qp_context    = attr.qp_context; -	qp->qp_type	  = attr.qp_type; -	atomic_inc(&pd->usecnt); -	atomic_inc(&attr.send_cq->usecnt); -	atomic_inc(&attr.recv_cq->usecnt); -	if (attr.srq) -		atomic_inc(&attr.srq->usecnt); +	if (cmd.qp_type != IB_QPT_XRC_TGT) { +		qp->real_qp	  = qp; +		qp->device	  = device; +		qp->pd		  = pd; +		qp->send_cq	  = attr.send_cq; +		qp->recv_cq	  = attr.recv_cq; +		qp->srq		  = attr.srq; +		qp->event_handler = attr.event_handler; +		qp->qp_context	  = attr.qp_context; +		qp->qp_type	  = attr.qp_type; +		atomic_set(&qp->usecnt, 0); +		atomic_inc(&pd->usecnt); +		atomic_inc(&attr.send_cq->usecnt); +		if (attr.recv_cq) +			atomic_inc(&attr.recv_cq->usecnt); +		if (attr.srq) +			atomic_inc(&attr.srq->usecnt); +	} +	qp->uobject = &obj->uevent.uobject;  	obj->uevent.uobject.object = qp;  	ret = idr_add_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject); @@ -1134,9 +1633,18 @@ ssize_t ib_uverbs_create_qp(struct ib_uverbs_file *file,  		goto err_copy;  	} -	put_pd_read(pd); -	put_cq_read(scq); -	if (rcq != scq) +	if (xrcd) { +		obj->uxrcd = container_of(xrcd_uobj, struct ib_uxrcd_object, +					  uobject); +		atomic_inc(&obj->uxrcd->refcnt); +		put_xrcd_read(xrcd_uobj); +	} + +	if (pd) +		put_pd_read(pd); +	if (scq) +		put_cq_read(scq); +	if (rcq && rcq != scq)  		put_cq_read(rcq);  	if (srq)  		put_srq_read(srq); @@ -1158,6 +1666,8 @@ err_destroy:  	ib_destroy_qp(qp);  err_put: +	if (xrcd) +		put_xrcd_read(xrcd_uobj);  	if (pd)  		put_pd_read(pd);  	if (scq) @@ -1171,6 +1681,100 @@ err_put:  	return ret;  } +ssize_t ib_uverbs_open_qp(struct ib_uverbs_file *file, +			  const char __user *buf, int in_len, int out_len) +{ +	struct ib_uverbs_open_qp        cmd; +	struct ib_uverbs_create_qp_resp resp; +	struct ib_udata                 udata; +	struct ib_uqp_object           *obj; +	struct ib_xrcd		       *xrcd; +	struct ib_uobject	       *uninitialized_var(xrcd_uobj); +	struct ib_qp                   *qp; +	struct ib_qp_open_attr          attr; +	int ret; + +	if (out_len < sizeof resp) +		return -ENOSPC; + +	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); + +	obj = kmalloc(sizeof *obj, GFP_KERNEL); +	if (!obj) +		return -ENOMEM; + +	init_uobj(&obj->uevent.uobject, cmd.user_handle, file->ucontext, &qp_lock_class); +	down_write(&obj->uevent.uobject.mutex); + +	xrcd = idr_read_xrcd(cmd.pd_handle, file->ucontext, &xrcd_uobj); +	if (!xrcd) { +		ret = -EINVAL; +		goto err_put; +	} + +	attr.event_handler = ib_uverbs_qp_event_handler; +	attr.qp_context    = file; +	attr.qp_num        = cmd.qpn; +	attr.qp_type       = cmd.qp_type; + +	obj->uevent.events_reported = 0; +	INIT_LIST_HEAD(&obj->uevent.event_list); +	INIT_LIST_HEAD(&obj->mcast_list); + +	qp = ib_open_qp(xrcd, &attr); +	if (IS_ERR(qp)) { +		ret = PTR_ERR(qp); +		goto err_put; +	} + +	qp->uobject = &obj->uevent.uobject; + +	obj->uevent.uobject.object = qp; +	ret = idr_add_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject); +	if (ret) +		goto err_destroy; + +	memset(&resp, 0, sizeof resp); +	resp.qpn       = qp->qp_num; +	resp.qp_handle = obj->uevent.uobject.id; + +	if (copy_to_user((void __user *) (unsigned long) cmd.response, +			 &resp, sizeof resp)) { +		ret = -EFAULT; +		goto err_remove; +	} + +	obj->uxrcd = container_of(xrcd_uobj, struct ib_uxrcd_object, uobject); +	atomic_inc(&obj->uxrcd->refcnt); +	put_xrcd_read(xrcd_uobj); + +	mutex_lock(&file->mutex); +	list_add_tail(&obj->uevent.uobject.list, &file->ucontext->qp_list); +	mutex_unlock(&file->mutex); + +	obj->uevent.uobject.live = 1; + +	up_write(&obj->uevent.uobject.mutex); + +	return in_len; + +err_remove: +	idr_remove_uobj(&ib_uverbs_qp_idr, &obj->uevent.uobject); + +err_destroy: +	ib_destroy_qp(qp); + +err_put: +	put_xrcd_read(xrcd_uobj); +	put_uobj_write(&obj->uevent.uobject); +	return ret; +} +  ssize_t ib_uverbs_query_qp(struct ib_uverbs_file *file,  			   const char __user *buf, int in_len,  			   int out_len) @@ -1271,6 +1875,20 @@ out:  	return ret ? ret : in_len;  } +/* Remove ignored fields set in the attribute mask */ +static int modify_qp_mask(enum ib_qp_type qp_type, int mask) +{ +	switch (qp_type) { +	case IB_QPT_XRC_INI: +		return mask & ~(IB_QP_MAX_DEST_RD_ATOMIC | IB_QP_MIN_RNR_TIMER); +	case IB_QPT_XRC_TGT: +		return mask & ~(IB_QP_MAX_QP_RD_ATOMIC | IB_QP_RETRY_CNT | +				IB_QP_RNR_RETRY); +	default: +		return mask; +	} +} +  ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file,  			    const char __user *buf, int in_len,  			    int out_len) @@ -1343,7 +1961,15 @@ ssize_t ib_uverbs_modify_qp(struct ib_uverbs_file *file,  	attr->alt_ah_attr.ah_flags 	    = cmd.alt_dest.is_global ? IB_AH_GRH : 0;  	attr->alt_ah_attr.port_num 	    = cmd.alt_dest.port_num; -	ret = qp->device->modify_qp(qp, attr, cmd.attr_mask, &udata); +	if (qp->real_qp == qp) { +		ret = ib_resolve_eth_l2_attrs(qp, attr, &cmd.attr_mask); +		if (ret) +			goto out; +		ret = qp->device->modify_qp(qp, attr, +			modify_qp_mask(qp->qp_type, cmd.attr_mask), &udata); +	} else { +		ret = ib_modify_qp(qp, attr, modify_qp_mask(qp->qp_type, cmd.attr_mask)); +	}  	put_qp_read(qp); @@ -1394,6 +2020,9 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file,  	if (ret)  		return ret; +	if (obj->uxrcd) +		atomic_dec(&obj->uxrcd->refcnt); +  	idr_remove_uobj(&ib_uverbs_qp_idr, uobj);  	mutex_lock(&file->mutex); @@ -1489,6 +2118,9 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,  			}  			next->wr.ud.remote_qpn  = user_wr->wr.ud.remote_qpn;  			next->wr.ud.remote_qkey = user_wr->wr.ud.remote_qkey; +			if (next->opcode == IB_WR_SEND_WITH_IMM) +				next->ex.imm_data = +					(__be32 __force) user_wr->ex.imm_data;  		} else {  			switch (next->opcode) {  			case IB_WR_RDMA_WRITE_WITH_IMM: @@ -1540,7 +2172,7 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,  	}  	resp.bad_wr = 0; -	ret = qp->device->post_send(qp, wr, &bad_wr); +	ret = qp->device->post_send(qp->real_qp, wr, &bad_wr);  	if (ret)  		for (next = wr; next; next = next->next) {  			++resp.bad_wr; @@ -1678,7 +2310,7 @@ ssize_t ib_uverbs_post_recv(struct ib_uverbs_file *file,  		goto out;  	resp.bad_wr = 0; -	ret = qp->device->post_recv(qp, wr, &bad_wr); +	ret = qp->device->post_recv(qp->real_qp, wr, &bad_wr);  	put_qp_read(qp); @@ -1774,7 +2406,7 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file,  	if (!uobj)  		return -ENOMEM; -	init_uobj(uobj, cmd.user_handle, file->ucontext, &ah_lock_key); +	init_uobj(uobj, cmd.user_handle, file->ucontext, &ah_lock_class);  	down_write(&uobj->mutex);  	pd = idr_read_pd(cmd.pd_handle, file->ucontext); @@ -1891,7 +2523,7 @@ ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file *file,  	if (copy_from_user(&cmd, buf, sizeof cmd))  		return -EFAULT; -	qp = idr_read_qp(cmd.qp_handle, file->ucontext); +	qp = idr_write_qp(cmd.qp_handle, file->ucontext);  	if (!qp)  		return -EINVAL; @@ -1920,7 +2552,7 @@ ssize_t ib_uverbs_attach_mcast(struct ib_uverbs_file *file,  		kfree(mcast);  out_put: -	put_qp_read(qp); +	put_qp_write(qp);  	return ret ? ret : in_len;  } @@ -1938,7 +2570,7 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file,  	if (copy_from_user(&cmd, buf, sizeof cmd))  		return -EFAULT; -	qp = idr_read_qp(cmd.qp_handle, file->ucontext); +	qp = idr_write_qp(cmd.qp_handle, file->ucontext);  	if (!qp)  		return -EINVAL; @@ -1957,100 +2589,367 @@ ssize_t ib_uverbs_detach_mcast(struct ib_uverbs_file *file,  		}  out_put: -	put_qp_read(qp); +	put_qp_write(qp);  	return ret ? ret : in_len;  } -ssize_t ib_uverbs_create_srq(struct ib_uverbs_file *file, -			     const char __user *buf, int in_len, -			     int out_len) +static int kern_spec_to_ib_spec(struct ib_uverbs_flow_spec *kern_spec, +				union ib_flow_spec *ib_spec) +{ +	if (kern_spec->reserved) +		return -EINVAL; + +	ib_spec->type = kern_spec->type; + +	switch (ib_spec->type) { +	case IB_FLOW_SPEC_ETH: +		ib_spec->eth.size = sizeof(struct ib_flow_spec_eth); +		if (ib_spec->eth.size != kern_spec->eth.size) +			return -EINVAL; +		memcpy(&ib_spec->eth.val, &kern_spec->eth.val, +		       sizeof(struct ib_flow_eth_filter)); +		memcpy(&ib_spec->eth.mask, &kern_spec->eth.mask, +		       sizeof(struct ib_flow_eth_filter)); +		break; +	case IB_FLOW_SPEC_IPV4: +		ib_spec->ipv4.size = sizeof(struct ib_flow_spec_ipv4); +		if (ib_spec->ipv4.size != kern_spec->ipv4.size) +			return -EINVAL; +		memcpy(&ib_spec->ipv4.val, &kern_spec->ipv4.val, +		       sizeof(struct ib_flow_ipv4_filter)); +		memcpy(&ib_spec->ipv4.mask, &kern_spec->ipv4.mask, +		       sizeof(struct ib_flow_ipv4_filter)); +		break; +	case IB_FLOW_SPEC_TCP: +	case IB_FLOW_SPEC_UDP: +		ib_spec->tcp_udp.size = sizeof(struct ib_flow_spec_tcp_udp); +		if (ib_spec->tcp_udp.size != kern_spec->tcp_udp.size) +			return -EINVAL; +		memcpy(&ib_spec->tcp_udp.val, &kern_spec->tcp_udp.val, +		       sizeof(struct ib_flow_tcp_udp_filter)); +		memcpy(&ib_spec->tcp_udp.mask, &kern_spec->tcp_udp.mask, +		       sizeof(struct ib_flow_tcp_udp_filter)); +		break; +	default: +		return -EINVAL; +	} +	return 0; +} + +int ib_uverbs_ex_create_flow(struct ib_uverbs_file *file, +			     struct ib_udata *ucore, +			     struct ib_udata *uhw) +{ +	struct ib_uverbs_create_flow	  cmd; +	struct ib_uverbs_create_flow_resp resp; +	struct ib_uobject		  *uobj; +	struct ib_flow			  *flow_id; +	struct ib_uverbs_flow_attr	  *kern_flow_attr; +	struct ib_flow_attr		  *flow_attr; +	struct ib_qp			  *qp; +	int err = 0; +	void *kern_spec; +	void *ib_spec; +	int i; + +	if (ucore->inlen < sizeof(cmd)) +		return -EINVAL; + +	if (ucore->outlen < sizeof(resp)) +		return -ENOSPC; + +	err = ib_copy_from_udata(&cmd, ucore, sizeof(cmd)); +	if (err) +		return err; + +	ucore->inbuf += sizeof(cmd); +	ucore->inlen -= sizeof(cmd); + +	if (cmd.comp_mask) +		return -EINVAL; + +	if ((cmd.flow_attr.type == IB_FLOW_ATTR_SNIFFER && +	     !capable(CAP_NET_ADMIN)) || !capable(CAP_NET_RAW)) +		return -EPERM; + +	if (cmd.flow_attr.num_of_specs > IB_FLOW_SPEC_SUPPORT_LAYERS) +		return -EINVAL; + +	if (cmd.flow_attr.size > ucore->inlen || +	    cmd.flow_attr.size > +	    (cmd.flow_attr.num_of_specs * sizeof(struct ib_uverbs_flow_spec))) +		return -EINVAL; + +	if (cmd.flow_attr.reserved[0] || +	    cmd.flow_attr.reserved[1]) +		return -EINVAL; + +	if (cmd.flow_attr.num_of_specs) { +		kern_flow_attr = kmalloc(sizeof(*kern_flow_attr) + cmd.flow_attr.size, +					 GFP_KERNEL); +		if (!kern_flow_attr) +			return -ENOMEM; + +		memcpy(kern_flow_attr, &cmd.flow_attr, sizeof(*kern_flow_attr)); +		err = ib_copy_from_udata(kern_flow_attr + 1, ucore, +					 cmd.flow_attr.size); +		if (err) +			goto err_free_attr; +	} else { +		kern_flow_attr = &cmd.flow_attr; +	} + +	uobj = kmalloc(sizeof(*uobj), GFP_KERNEL); +	if (!uobj) { +		err = -ENOMEM; +		goto err_free_attr; +	} +	init_uobj(uobj, 0, file->ucontext, &rule_lock_class); +	down_write(&uobj->mutex); + +	qp = idr_read_qp(cmd.qp_handle, file->ucontext); +	if (!qp) { +		err = -EINVAL; +		goto err_uobj; +	} + +	flow_attr = kmalloc(sizeof(*flow_attr) + cmd.flow_attr.size, GFP_KERNEL); +	if (!flow_attr) { +		err = -ENOMEM; +		goto err_put; +	} + +	flow_attr->type = kern_flow_attr->type; +	flow_attr->priority = kern_flow_attr->priority; +	flow_attr->num_of_specs = kern_flow_attr->num_of_specs; +	flow_attr->port = kern_flow_attr->port; +	flow_attr->flags = kern_flow_attr->flags; +	flow_attr->size = sizeof(*flow_attr); + +	kern_spec = kern_flow_attr + 1; +	ib_spec = flow_attr + 1; +	for (i = 0; i < flow_attr->num_of_specs && +	     cmd.flow_attr.size > offsetof(struct ib_uverbs_flow_spec, reserved) && +	     cmd.flow_attr.size >= +	     ((struct ib_uverbs_flow_spec *)kern_spec)->size; i++) { +		err = kern_spec_to_ib_spec(kern_spec, ib_spec); +		if (err) +			goto err_free; +		flow_attr->size += +			((union ib_flow_spec *) ib_spec)->size; +		cmd.flow_attr.size -= ((struct ib_uverbs_flow_spec *)kern_spec)->size; +		kern_spec += ((struct ib_uverbs_flow_spec *) kern_spec)->size; +		ib_spec += ((union ib_flow_spec *) ib_spec)->size; +	} +	if (cmd.flow_attr.size || (i != flow_attr->num_of_specs)) { +		pr_warn("create flow failed, flow %d: %d bytes left from uverb cmd\n", +			i, cmd.flow_attr.size); +		err = -EINVAL; +		goto err_free; +	} +	flow_id = ib_create_flow(qp, flow_attr, IB_FLOW_DOMAIN_USER); +	if (IS_ERR(flow_id)) { +		err = PTR_ERR(flow_id); +		goto err_free; +	} +	flow_id->qp = qp; +	flow_id->uobject = uobj; +	uobj->object = flow_id; + +	err = idr_add_uobj(&ib_uverbs_rule_idr, uobj); +	if (err) +		goto destroy_flow; + +	memset(&resp, 0, sizeof(resp)); +	resp.flow_handle = uobj->id; + +	err = ib_copy_to_udata(ucore, +			       &resp, sizeof(resp)); +	if (err) +		goto err_copy; + +	put_qp_read(qp); +	mutex_lock(&file->mutex); +	list_add_tail(&uobj->list, &file->ucontext->rule_list); +	mutex_unlock(&file->mutex); + +	uobj->live = 1; + +	up_write(&uobj->mutex); +	kfree(flow_attr); +	if (cmd.flow_attr.num_of_specs) +		kfree(kern_flow_attr); +	return 0; +err_copy: +	idr_remove_uobj(&ib_uverbs_rule_idr, uobj); +destroy_flow: +	ib_destroy_flow(flow_id); +err_free: +	kfree(flow_attr); +err_put: +	put_qp_read(qp); +err_uobj: +	put_uobj_write(uobj); +err_free_attr: +	if (cmd.flow_attr.num_of_specs) +		kfree(kern_flow_attr); +	return err; +} + +int ib_uverbs_ex_destroy_flow(struct ib_uverbs_file *file, +			      struct ib_udata *ucore, +			      struct ib_udata *uhw) +{ +	struct ib_uverbs_destroy_flow	cmd; +	struct ib_flow			*flow_id; +	struct ib_uobject		*uobj; +	int				ret; + +	if (ucore->inlen < sizeof(cmd)) +		return -EINVAL; + +	ret = ib_copy_from_udata(&cmd, ucore, sizeof(cmd)); +	if (ret) +		return ret; + +	if (cmd.comp_mask) +		return -EINVAL; + +	uobj = idr_write_uobj(&ib_uverbs_rule_idr, cmd.flow_handle, +			      file->ucontext); +	if (!uobj) +		return -EINVAL; +	flow_id = uobj->object; + +	ret = ib_destroy_flow(flow_id); +	if (!ret) +		uobj->live = 0; + +	put_uobj_write(uobj); + +	idr_remove_uobj(&ib_uverbs_rule_idr, uobj); + +	mutex_lock(&file->mutex); +	list_del(&uobj->list); +	mutex_unlock(&file->mutex); + +	put_uobj(uobj); + +	return ret; +} + +static int __uverbs_create_xsrq(struct ib_uverbs_file *file, +				struct ib_uverbs_create_xsrq *cmd, +				struct ib_udata *udata)  { -	struct ib_uverbs_create_srq      cmd;  	struct ib_uverbs_create_srq_resp resp; -	struct ib_udata                  udata; -	struct ib_uevent_object         *obj; +	struct ib_usrq_object           *obj;  	struct ib_pd                    *pd;  	struct ib_srq                   *srq; +	struct ib_uobject               *uninitialized_var(xrcd_uobj);  	struct ib_srq_init_attr          attr;  	int ret; -	if (out_len < sizeof resp) -		return -ENOSPC; - -	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); -  	obj = kmalloc(sizeof *obj, GFP_KERNEL);  	if (!obj)  		return -ENOMEM; -	init_uobj(&obj->uobject, cmd.user_handle, file->ucontext, &srq_lock_key); -	down_write(&obj->uobject.mutex); +	init_uobj(&obj->uevent.uobject, cmd->user_handle, file->ucontext, &srq_lock_class); +	down_write(&obj->uevent.uobject.mutex); + +	if (cmd->srq_type == IB_SRQT_XRC) { +		attr.ext.xrc.xrcd  = idr_read_xrcd(cmd->xrcd_handle, file->ucontext, &xrcd_uobj); +		if (!attr.ext.xrc.xrcd) { +			ret = -EINVAL; +			goto err; +		} -	pd  = idr_read_pd(cmd.pd_handle, file->ucontext); +		obj->uxrcd = container_of(xrcd_uobj, struct ib_uxrcd_object, uobject); +		atomic_inc(&obj->uxrcd->refcnt); + +		attr.ext.xrc.cq  = idr_read_cq(cmd->cq_handle, file->ucontext, 0); +		if (!attr.ext.xrc.cq) { +			ret = -EINVAL; +			goto err_put_xrcd; +		} +	} + +	pd  = idr_read_pd(cmd->pd_handle, file->ucontext);  	if (!pd) {  		ret = -EINVAL; -		goto err; +		goto err_put_cq;  	}  	attr.event_handler  = ib_uverbs_srq_event_handler;  	attr.srq_context    = file; -	attr.attr.max_wr    = cmd.max_wr; -	attr.attr.max_sge   = cmd.max_sge; -	attr.attr.srq_limit = cmd.srq_limit; +	attr.srq_type       = cmd->srq_type; +	attr.attr.max_wr    = cmd->max_wr; +	attr.attr.max_sge   = cmd->max_sge; +	attr.attr.srq_limit = cmd->srq_limit; -	obj->events_reported     = 0; -	INIT_LIST_HEAD(&obj->event_list); +	obj->uevent.events_reported = 0; +	INIT_LIST_HEAD(&obj->uevent.event_list); -	srq = pd->device->create_srq(pd, &attr, &udata); +	srq = pd->device->create_srq(pd, &attr, udata);  	if (IS_ERR(srq)) {  		ret = PTR_ERR(srq);  		goto err_put;  	} -	srq->device    	   = pd->device; -	srq->pd        	   = pd; -	srq->uobject       = &obj->uobject; +	srq->device        = pd->device; +	srq->pd            = pd; +	srq->srq_type	   = cmd->srq_type; +	srq->uobject       = &obj->uevent.uobject;  	srq->event_handler = attr.event_handler;  	srq->srq_context   = attr.srq_context; + +	if (cmd->srq_type == IB_SRQT_XRC) { +		srq->ext.xrc.cq   = attr.ext.xrc.cq; +		srq->ext.xrc.xrcd = attr.ext.xrc.xrcd; +		atomic_inc(&attr.ext.xrc.cq->usecnt); +		atomic_inc(&attr.ext.xrc.xrcd->usecnt); +	} +  	atomic_inc(&pd->usecnt);  	atomic_set(&srq->usecnt, 0); -	obj->uobject.object = srq; -	ret = idr_add_uobj(&ib_uverbs_srq_idr, &obj->uobject); +	obj->uevent.uobject.object = srq; +	ret = idr_add_uobj(&ib_uverbs_srq_idr, &obj->uevent.uobject);  	if (ret)  		goto err_destroy;  	memset(&resp, 0, sizeof resp); -	resp.srq_handle = obj->uobject.id; +	resp.srq_handle = obj->uevent.uobject.id;  	resp.max_wr     = attr.attr.max_wr;  	resp.max_sge    = attr.attr.max_sge; +	if (cmd->srq_type == IB_SRQT_XRC) +		resp.srqn = srq->ext.xrc.srq_num; -	if (copy_to_user((void __user *) (unsigned long) cmd.response, +	if (copy_to_user((void __user *) (unsigned long) cmd->response,  			 &resp, sizeof resp)) {  		ret = -EFAULT;  		goto err_copy;  	} +	if (cmd->srq_type == IB_SRQT_XRC) { +		put_uobj_read(xrcd_uobj); +		put_cq_read(attr.ext.xrc.cq); +	}  	put_pd_read(pd);  	mutex_lock(&file->mutex); -	list_add_tail(&obj->uobject.list, &file->ucontext->srq_list); +	list_add_tail(&obj->uevent.uobject.list, &file->ucontext->srq_list);  	mutex_unlock(&file->mutex); -	obj->uobject.live = 1; +	obj->uevent.uobject.live = 1; -	up_write(&obj->uobject.mutex); +	up_write(&obj->uevent.uobject.mutex); -	return in_len; +	return 0;  err_copy: -	idr_remove_uobj(&ib_uverbs_srq_idr, &obj->uobject); +	idr_remove_uobj(&ib_uverbs_srq_idr, &obj->uevent.uobject);  err_destroy:  	ib_destroy_srq(srq); @@ -2058,11 +2957,81 @@ err_destroy:  err_put:  	put_pd_read(pd); +err_put_cq: +	if (cmd->srq_type == IB_SRQT_XRC) +		put_cq_read(attr.ext.xrc.cq); + +err_put_xrcd: +	if (cmd->srq_type == IB_SRQT_XRC) { +		atomic_dec(&obj->uxrcd->refcnt); +		put_uobj_read(xrcd_uobj); +	} +  err: -	put_uobj_write(&obj->uobject); +	put_uobj_write(&obj->uevent.uobject);  	return ret;  } +ssize_t ib_uverbs_create_srq(struct ib_uverbs_file *file, +			     const char __user *buf, int in_len, +			     int out_len) +{ +	struct ib_uverbs_create_srq      cmd; +	struct ib_uverbs_create_xsrq     xcmd; +	struct ib_uverbs_create_srq_resp resp; +	struct ib_udata                  udata; +	int ret; + +	if (out_len < sizeof resp) +		return -ENOSPC; + +	if (copy_from_user(&cmd, buf, sizeof cmd)) +		return -EFAULT; + +	xcmd.response	 = cmd.response; +	xcmd.user_handle = cmd.user_handle; +	xcmd.srq_type	 = IB_SRQT_BASIC; +	xcmd.pd_handle	 = cmd.pd_handle; +	xcmd.max_wr	 = cmd.max_wr; +	xcmd.max_sge	 = cmd.max_sge; +	xcmd.srq_limit	 = cmd.srq_limit; + +	INIT_UDATA(&udata, buf + sizeof cmd, +		   (unsigned long) cmd.response + sizeof resp, +		   in_len - sizeof cmd, out_len - sizeof resp); + +	ret = __uverbs_create_xsrq(file, &xcmd, &udata); +	if (ret) +		return ret; + +	return in_len; +} + +ssize_t ib_uverbs_create_xsrq(struct ib_uverbs_file *file, +			      const char __user *buf, int in_len, int out_len) +{ +	struct ib_uverbs_create_xsrq     cmd; +	struct ib_uverbs_create_srq_resp resp; +	struct ib_udata                  udata; +	int ret; + +	if (out_len < sizeof resp) +		return -ENOSPC; + +	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); + +	ret = __uverbs_create_xsrq(file, &cmd, &udata); +	if (ret) +		return ret; + +	return in_len; +} +  ssize_t ib_uverbs_modify_srq(struct ib_uverbs_file *file,  			     const char __user *buf, int in_len,  			     int out_len) @@ -2143,6 +3112,8 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file,  	struct ib_srq               	 *srq;  	struct ib_uevent_object        	 *obj;  	int                         	  ret = -EINVAL; +	struct ib_usrq_object		 *us; +	enum ib_srq_type		  srq_type;  	if (copy_from_user(&cmd, buf, sizeof cmd))  		return -EFAULT; @@ -2152,6 +3123,7 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file,  		return -EINVAL;  	srq = uobj->object;  	obj = container_of(uobj, struct ib_uevent_object, uobject); +	srq_type = srq->srq_type;  	ret = ib_destroy_srq(srq);  	if (!ret) @@ -2162,6 +3134,11 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file,  	if (ret)  		return ret; +	if (srq_type == IB_SRQT_XRC) { +		us = container_of(obj, struct ib_usrq_object, uevent); +		atomic_dec(&us->uxrcd->refcnt); +	} +  	idr_remove_uobj(&ib_uverbs_srq_idr, uobj);  	mutex_lock(&file->mutex);  | 
