diff options
Diffstat (limited to 'drivers/infiniband/core')
| -rw-r--r-- | drivers/infiniband/core/addr.c | 15 | ||||
| -rw-r--r-- | drivers/infiniband/core/cma.c | 15 | ||||
| -rw-r--r-- | drivers/infiniband/core/core_priv.h | 7 | ||||
| -rw-r--r-- | drivers/infiniband/core/cq.c | 21 | ||||
| -rw-r--r-- | drivers/infiniband/core/device.c | 6 | ||||
| -rw-r--r-- | drivers/infiniband/core/rdma_core.c | 38 | ||||
| -rw-r--r-- | drivers/infiniband/core/restrack.c | 23 | ||||
| -rw-r--r-- | drivers/infiniband/core/sa_query.c | 7 | ||||
| -rw-r--r-- | drivers/infiniband/core/ucma.c | 42 | ||||
| -rw-r--r-- | drivers/infiniband/core/uverbs_cmd.c | 50 | ||||
| -rw-r--r-- | drivers/infiniband/core/uverbs_ioctl.c | 3 | ||||
| -rw-r--r-- | drivers/infiniband/core/uverbs_ioctl_merge.c | 18 | ||||
| -rw-r--r-- | drivers/infiniband/core/uverbs_main.c | 29 | ||||
| -rw-r--r-- | drivers/infiniband/core/uverbs_std_types.c | 12 | ||||
| -rw-r--r-- | drivers/infiniband/core/verbs.c | 3 | 
15 files changed, 198 insertions, 91 deletions
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index a5b4cf030c11..9183d148d644 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -550,18 +550,13 @@ static int addr_resolve(struct sockaddr *src_in,  		dst_release(dst);  	} -	if (ndev->flags & IFF_LOOPBACK) { -		ret = rdma_translate_ip(dst_in, addr); -		/* -		 * Put the loopback device and get the translated -		 * device instead. -		 */ +	if (ndev) { +		if (ndev->flags & IFF_LOOPBACK) +			ret = rdma_translate_ip(dst_in, addr); +		else +			addr->bound_dev_if = ndev->ifindex;  		dev_put(ndev); -		ndev = dev_get_by_index(addr->net, addr->bound_dev_if); -	} else { -		addr->bound_dev_if = ndev->ifindex;  	} -	dev_put(ndev);  	return ret;  } diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index e66963ca58bd..a5367c5efbe7 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -3069,7 +3069,8 @@ static int cma_port_is_unique(struct rdma_bind_list *bind_list,  			continue;  		/* different dest port -> unique */ -		if (!cma_any_port(cur_daddr) && +		if (!cma_any_port(daddr) && +		    !cma_any_port(cur_daddr) &&  		    (dport != cur_dport))  			continue; @@ -3080,7 +3081,8 @@ static int cma_port_is_unique(struct rdma_bind_list *bind_list,  			continue;  		/* different dst address -> unique */ -		if (!cma_any_addr(cur_daddr) && +		if (!cma_any_addr(daddr) && +		    !cma_any_addr(cur_daddr) &&  		    cma_addr_cmp(daddr, cur_daddr))  			continue; @@ -3378,13 +3380,13 @@ int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr)  		}  #endif  	} +	daddr = cma_dst_addr(id_priv); +	daddr->sa_family = addr->sa_family; +  	ret = cma_get_port(id_priv);  	if (ret)  		goto err2; -	daddr = cma_dst_addr(id_priv); -	daddr->sa_family = addr->sa_family; -  	return 0;  err2:  	if (id_priv->cma_dev) @@ -4173,6 +4175,9 @@ int rdma_join_multicast(struct rdma_cm_id *id, struct sockaddr *addr,  	struct cma_multicast *mc;  	int ret; +	if (!id->device) +		return -EINVAL; +  	id_priv = container_of(id, struct rdma_id_private, id);  	if (!cma_comp(id_priv, RDMA_CM_ADDR_BOUND) &&  	    !cma_comp(id_priv, RDMA_CM_ADDR_RESOLVED)) diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h index c4560d84dfae..25bb178f6074 100644 --- a/drivers/infiniband/core/core_priv.h +++ b/drivers/infiniband/core/core_priv.h @@ -305,16 +305,21 @@ void nldev_exit(void);  static inline struct ib_qp *_ib_create_qp(struct ib_device *dev,  					  struct ib_pd *pd,  					  struct ib_qp_init_attr *attr, -					  struct ib_udata *udata) +					  struct ib_udata *udata, +					  struct ib_uobject *uobj)  {  	struct ib_qp *qp; +	if (!dev->create_qp) +		return ERR_PTR(-EOPNOTSUPP); +  	qp = dev->create_qp(pd, attr, udata);  	if (IS_ERR(qp))  		return qp;  	qp->device = dev;  	qp->pd = pd; +	qp->uobject = uobj;  	/*  	 * We don't track XRC QPs for now, because they don't have PD  	 * and more importantly they are created internaly by driver, diff --git a/drivers/infiniband/core/cq.c b/drivers/infiniband/core/cq.c index bc79ca8215d7..af5ad6a56ae4 100644 --- a/drivers/infiniband/core/cq.c +++ b/drivers/infiniband/core/cq.c @@ -17,6 +17,7 @@  /* # of WCs to poll for with a single call to ib_poll_cq */  #define IB_POLL_BATCH			16 +#define IB_POLL_BATCH_DIRECT		8  /* # of WCs to iterate over before yielding */  #define IB_POLL_BUDGET_IRQ		256 @@ -25,18 +26,18 @@  #define IB_POLL_FLAGS \  	(IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS) -static int __ib_process_cq(struct ib_cq *cq, int budget, struct ib_wc *poll_wc) +static int __ib_process_cq(struct ib_cq *cq, int budget, struct ib_wc *wcs, +			   int batch)  {  	int i, n, completed = 0; -	struct ib_wc *wcs = poll_wc ? : cq->wc;  	/*  	 * budget might be (-1) if the caller does not  	 * want to bound this call, thus we need unsigned  	 * minimum here.  	 */ -	while ((n = ib_poll_cq(cq, min_t(u32, IB_POLL_BATCH, -			budget - completed), wcs)) > 0) { +	while ((n = ib_poll_cq(cq, min_t(u32, batch, +					 budget - completed), wcs)) > 0) {  		for (i = 0; i < n; i++) {  			struct ib_wc *wc = &wcs[i]; @@ -48,8 +49,7 @@ static int __ib_process_cq(struct ib_cq *cq, int budget, struct ib_wc *poll_wc)  		completed += n; -		if (n != IB_POLL_BATCH || -		    (budget != -1 && completed >= budget)) +		if (n != batch || (budget != -1 && completed >= budget))  			break;  	} @@ -72,9 +72,9 @@ static int __ib_process_cq(struct ib_cq *cq, int budget, struct ib_wc *poll_wc)   */  int ib_process_cq_direct(struct ib_cq *cq, int budget)  { -	struct ib_wc wcs[IB_POLL_BATCH]; +	struct ib_wc wcs[IB_POLL_BATCH_DIRECT]; -	return __ib_process_cq(cq, budget, wcs); +	return __ib_process_cq(cq, budget, wcs, IB_POLL_BATCH_DIRECT);  }  EXPORT_SYMBOL(ib_process_cq_direct); @@ -88,7 +88,7 @@ static int ib_poll_handler(struct irq_poll *iop, int budget)  	struct ib_cq *cq = container_of(iop, struct ib_cq, iop);  	int completed; -	completed = __ib_process_cq(cq, budget, NULL); +	completed = __ib_process_cq(cq, budget, cq->wc, IB_POLL_BATCH);  	if (completed < budget) {  		irq_poll_complete(&cq->iop);  		if (ib_req_notify_cq(cq, IB_POLL_FLAGS) > 0) @@ -108,7 +108,8 @@ static void ib_cq_poll_work(struct work_struct *work)  	struct ib_cq *cq = container_of(work, struct ib_cq, work);  	int completed; -	completed = __ib_process_cq(cq, IB_POLL_BUDGET_WORKQUEUE, NULL); +	completed = __ib_process_cq(cq, IB_POLL_BUDGET_WORKQUEUE, cq->wc, +				    IB_POLL_BATCH);  	if (completed >= IB_POLL_BUDGET_WORKQUEUE ||  	    ib_req_notify_cq(cq, IB_POLL_FLAGS) > 0)  		queue_work(ib_comp_wq, &cq->work); diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index e8010e73a1cf..bb065c9449be 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -536,14 +536,14 @@ int ib_register_device(struct ib_device *device,  	ret = device->query_device(device, &device->attrs, &uhw);  	if (ret) {  		pr_warn("Couldn't query the device attributes\n"); -		goto cache_cleanup; +		goto cg_cleanup;  	}  	ret = ib_device_register_sysfs(device, port_callback);  	if (ret) {  		pr_warn("Couldn't register device %s with driver model\n",  			device->name); -		goto cache_cleanup; +		goto cg_cleanup;  	}  	device->reg_state = IB_DEV_REGISTERED; @@ -559,6 +559,8 @@ int ib_register_device(struct ib_device *device,  	mutex_unlock(&device_mutex);  	return 0; +cg_cleanup: +	ib_device_unregister_rdmacg(device);  cache_cleanup:  	ib_cache_cleanup_one(device);  	ib_cache_release_one(device); diff --git a/drivers/infiniband/core/rdma_core.c b/drivers/infiniband/core/rdma_core.c index 85b5ee4defa4..d8eead5d106d 100644 --- a/drivers/infiniband/core/rdma_core.c +++ b/drivers/infiniband/core/rdma_core.c @@ -141,7 +141,12 @@ static struct ib_uobject *alloc_uobj(struct ib_ucontext *context,  	 */  	uobj->context = context;  	uobj->type = type; -	atomic_set(&uobj->usecnt, 0); +	/* +	 * Allocated objects start out as write locked to deny any other +	 * syscalls from accessing them until they are committed. See +	 * rdma_alloc_commit_uobject +	 */ +	atomic_set(&uobj->usecnt, -1);  	kref_init(&uobj->ref);  	return uobj; @@ -196,7 +201,15 @@ static struct ib_uobject *lookup_get_idr_uobject(const struct uverbs_obj_type *t  		goto free;  	} -	uverbs_uobject_get(uobj); +	/* +	 * The idr_find is guaranteed to return a pointer to something that +	 * isn't freed yet, or NULL, as the free after idr_remove goes through +	 * kfree_rcu(). However the object may still have been released and +	 * kfree() could be called at any time. +	 */ +	if (!kref_get_unless_zero(&uobj->ref)) +		uobj = ERR_PTR(-ENOENT); +  free:  	rcu_read_unlock();  	return uobj; @@ -399,13 +412,13 @@ static int __must_check remove_commit_fd_uobject(struct ib_uobject *uobj,  	return ret;  } -static void lockdep_check(struct ib_uobject *uobj, bool exclusive) +static void assert_uverbs_usecnt(struct ib_uobject *uobj, bool exclusive)  {  #ifdef CONFIG_LOCKDEP  	if (exclusive) -		WARN_ON(atomic_read(&uobj->usecnt) > 0); +		WARN_ON(atomic_read(&uobj->usecnt) != -1);  	else -		WARN_ON(atomic_read(&uobj->usecnt) == -1); +		WARN_ON(atomic_read(&uobj->usecnt) <= 0);  #endif  } @@ -444,7 +457,7 @@ int __must_check rdma_remove_commit_uobject(struct ib_uobject *uobj)  		WARN(true, "ib_uverbs: Cleanup is running while removing an uobject\n");  		return 0;  	} -	lockdep_check(uobj, true); +	assert_uverbs_usecnt(uobj, true);  	ret = _rdma_remove_commit_uobject(uobj, RDMA_REMOVE_DESTROY);  	up_read(&ucontext->cleanup_rwsem); @@ -474,16 +487,17 @@ int rdma_explicit_destroy(struct ib_uobject *uobject)  		WARN(true, "ib_uverbs: Cleanup is running while removing an uobject\n");  		return 0;  	} -	lockdep_check(uobject, true); +	assert_uverbs_usecnt(uobject, true);  	ret = uobject->type->type_class->remove_commit(uobject,  						       RDMA_REMOVE_DESTROY);  	if (ret) -		return ret; +		goto out;  	uobject->type = &null_obj_type; +out:  	up_read(&ucontext->cleanup_rwsem); -	return 0; +	return ret;  }  static void alloc_commit_idr_uobject(struct ib_uobject *uobj) @@ -527,6 +541,10 @@ int rdma_alloc_commit_uobject(struct ib_uobject *uobj)  		return ret;  	} +	/* matches atomic_set(-1) in alloc_uobj */ +	assert_uverbs_usecnt(uobj, true); +	atomic_set(&uobj->usecnt, 0); +  	uobj->type->type_class->alloc_commit(uobj);  	up_read(&uobj->context->cleanup_rwsem); @@ -561,7 +579,7 @@ static void lookup_put_fd_uobject(struct ib_uobject *uobj, bool exclusive)  void rdma_lookup_put_uobject(struct ib_uobject *uobj, bool exclusive)  { -	lockdep_check(uobj, exclusive); +	assert_uverbs_usecnt(uobj, exclusive);  	uobj->type->type_class->lookup_put(uobj, exclusive);  	/*  	 * In order to unlock an object, either decrease its usecnt for diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c index 857637bf46da..3dbc4e4cca41 100644 --- a/drivers/infiniband/core/restrack.c +++ b/drivers/infiniband/core/restrack.c @@ -7,7 +7,6 @@  #include <rdma/restrack.h>  #include <linux/mutex.h>  #include <linux/sched/task.h> -#include <linux/uaccess.h>  #include <linux/pid_namespace.h>  void rdma_restrack_init(struct rdma_restrack_root *res) @@ -63,7 +62,6 @@ static struct ib_device *res_to_dev(struct rdma_restrack_entry *res)  {  	enum rdma_restrack_type type = res->type;  	struct ib_device *dev; -	struct ib_xrcd *xrcd;  	struct ib_pd *pd;  	struct ib_cq *cq;  	struct ib_qp *qp; @@ -81,10 +79,6 @@ static struct ib_device *res_to_dev(struct rdma_restrack_entry *res)  		qp = container_of(res, struct ib_qp, res);  		dev = qp->device;  		break; -	case RDMA_RESTRACK_XRCD: -		xrcd = container_of(res, struct ib_xrcd, res); -		dev = xrcd->device; -		break;  	default:  		WARN_ONCE(true, "Wrong resource tracking type %u\n", type);  		return NULL; @@ -93,6 +87,21 @@ static struct ib_device *res_to_dev(struct rdma_restrack_entry *res)  	return dev;  } +static bool res_is_user(struct rdma_restrack_entry *res) +{ +	switch (res->type) { +	case RDMA_RESTRACK_PD: +		return container_of(res, struct ib_pd, res)->uobject; +	case RDMA_RESTRACK_CQ: +		return container_of(res, struct ib_cq, res)->uobject; +	case RDMA_RESTRACK_QP: +		return container_of(res, struct ib_qp, res)->uobject; +	default: +		WARN_ONCE(true, "Wrong resource tracking type %u\n", res->type); +		return false; +	} +} +  void rdma_restrack_add(struct rdma_restrack_entry *res)  {  	struct ib_device *dev = res_to_dev(res); @@ -100,7 +109,7 @@ void rdma_restrack_add(struct rdma_restrack_entry *res)  	if (!dev)  		return; -	if (!uaccess_kernel()) { +	if (res_is_user(res)) {  		get_task_struct(current);  		res->task = current;  		res->kern_name = NULL; diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index 8cf15d4a8ac4..9f029a1ca5ea 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -1291,10 +1291,9 @@ int ib_init_ah_attr_from_path(struct ib_device *device, u8 port_num,  		resolved_dev = dev_get_by_index(dev_addr.net,  						dev_addr.bound_dev_if); -		if (resolved_dev->flags & IFF_LOOPBACK) { -			dev_put(resolved_dev); -			resolved_dev = idev; -			dev_hold(resolved_dev); +		if (!resolved_dev) { +			dev_put(idev); +			return -ENODEV;  		}  		ndev = ib_get_ndev_from_path(rec);  		rcu_read_lock(); diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index f015f1bf88c9..e5a1e7d81326 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -132,7 +132,7 @@ static inline struct ucma_context *_ucma_find_context(int id,  	ctx = idr_find(&ctx_idr, id);  	if (!ctx)  		ctx = ERR_PTR(-ENOENT); -	else if (ctx->file != file) +	else if (ctx->file != file || !ctx->cm_id)  		ctx = ERR_PTR(-EINVAL);  	return ctx;  } @@ -456,6 +456,7 @@ static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf,  	struct rdma_ucm_create_id cmd;  	struct rdma_ucm_create_id_resp resp;  	struct ucma_context *ctx; +	struct rdma_cm_id *cm_id;  	enum ib_qp_type qp_type;  	int ret; @@ -476,10 +477,10 @@ static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf,  		return -ENOMEM;  	ctx->uid = cmd.uid; -	ctx->cm_id = rdma_create_id(current->nsproxy->net_ns, -				    ucma_event_handler, ctx, cmd.ps, qp_type); -	if (IS_ERR(ctx->cm_id)) { -		ret = PTR_ERR(ctx->cm_id); +	cm_id = rdma_create_id(current->nsproxy->net_ns, +			       ucma_event_handler, ctx, cmd.ps, qp_type); +	if (IS_ERR(cm_id)) { +		ret = PTR_ERR(cm_id);  		goto err1;  	} @@ -489,14 +490,19 @@ static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf,  		ret = -EFAULT;  		goto err2;  	} + +	ctx->cm_id = cm_id;  	return 0;  err2: -	rdma_destroy_id(ctx->cm_id); +	rdma_destroy_id(cm_id);  err1:  	mutex_lock(&mut);  	idr_remove(&ctx_idr, ctx->id);  	mutex_unlock(&mut); +	mutex_lock(&file->mut); +	list_del(&ctx->list); +	mutex_unlock(&file->mut);  	kfree(ctx);  	return ret;  } @@ -664,19 +670,23 @@ static ssize_t ucma_resolve_ip(struct ucma_file *file,  			       int in_len, int out_len)  {  	struct rdma_ucm_resolve_ip cmd; +	struct sockaddr *src, *dst;  	struct ucma_context *ctx;  	int ret;  	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))  		return -EFAULT; +	src = (struct sockaddr *) &cmd.src_addr; +	dst = (struct sockaddr *) &cmd.dst_addr; +	if (!rdma_addr_size(src) || !rdma_addr_size(dst)) +		return -EINVAL; +  	ctx = ucma_get_ctx(file, cmd.id);  	if (IS_ERR(ctx))  		return PTR_ERR(ctx); -	ret = rdma_resolve_addr(ctx->cm_id, (struct sockaddr *) &cmd.src_addr, -				(struct sockaddr *) &cmd.dst_addr, -				cmd.timeout_ms); +	ret = rdma_resolve_addr(ctx->cm_id, src, dst, cmd.timeout_ms);  	ucma_put_ctx(ctx);  	return ret;  } @@ -1149,6 +1159,9 @@ static ssize_t ucma_init_qp_attr(struct ucma_file *file,  	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))  		return -EFAULT; +	if (cmd.qp_state > IB_QPS_ERR) +		return -EINVAL; +  	ctx = ucma_get_ctx(file, cmd.id);  	if (IS_ERR(ctx))  		return PTR_ERR(ctx); @@ -1294,6 +1307,9 @@ static ssize_t ucma_set_option(struct ucma_file *file, const char __user *inbuf,  	if (IS_ERR(ctx))  		return PTR_ERR(ctx); +	if (unlikely(cmd.optval > KMALLOC_MAX_SIZE)) +		return -EINVAL; +  	optval = memdup_user((void __user *) (unsigned long) cmd.optval,  			     cmd.optlen);  	if (IS_ERR(optval)) { @@ -1343,7 +1359,7 @@ static ssize_t ucma_process_join(struct ucma_file *file,  		return -ENOSPC;  	addr = (struct sockaddr *) &cmd->addr; -	if (!cmd->addr_size || (cmd->addr_size != rdma_addr_size(addr))) +	if (cmd->addr_size != rdma_addr_size(addr))  		return -EINVAL;  	if (cmd->join_flags == RDMA_MC_JOIN_FLAG_FULLMEMBER) @@ -1411,6 +1427,9 @@ static ssize_t ucma_join_ip_multicast(struct ucma_file *file,  	join_cmd.uid = cmd.uid;  	join_cmd.id = cmd.id;  	join_cmd.addr_size = rdma_addr_size((struct sockaddr *) &cmd.addr); +	if (!join_cmd.addr_size) +		return -EINVAL; +  	join_cmd.join_flags = RDMA_MC_JOIN_FLAG_FULLMEMBER;  	memcpy(&join_cmd.addr, &cmd.addr, join_cmd.addr_size); @@ -1426,6 +1445,9 @@ static ssize_t ucma_join_multicast(struct ucma_file *file,  	if (copy_from_user(&cmd, inbuf, sizeof(cmd)))  		return -EFAULT; +	if (!rdma_addr_size((struct sockaddr *)&cmd.addr)) +		return -EINVAL; +  	return ucma_process_join(file, &cmd, out_len);  } diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 256934d1f64f..a148de35df8d 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -562,9 +562,10 @@ ssize_t ib_uverbs_open_xrcd(struct ib_uverbs_file *file,  	if (f.file)  		fdput(f); +	mutex_unlock(&file->device->xrcd_tree_mutex); +  	uobj_alloc_commit(&obj->uobject); -	mutex_unlock(&file->device->xrcd_tree_mutex);  	return in_len;  err_copy: @@ -603,10 +604,8 @@ ssize_t ib_uverbs_close_xrcd(struct ib_uverbs_file *file,  	uobj  = uobj_get_write(uobj_get_type(xrcd), cmd.xrcd_handle,  			       file->ucontext); -	if (IS_ERR(uobj)) { -		mutex_unlock(&file->device->xrcd_tree_mutex); +	if (IS_ERR(uobj))  		return PTR_ERR(uobj); -	}  	ret = uobj_remove_commit(uobj);  	return ret ?: in_len; @@ -979,6 +978,9 @@ static struct ib_ucq_object *create_cq(struct ib_uverbs_file *file,  	struct ib_uverbs_ex_create_cq_resp resp;  	struct ib_cq_init_attr attr = {}; +	if (!ib_dev->create_cq) +		return ERR_PTR(-EOPNOTSUPP); +  	if (cmd->comp_vector >= file->device->num_comp_vectors)  		return ERR_PTR(-EINVAL); @@ -1030,14 +1032,14 @@ static struct ib_ucq_object *create_cq(struct ib_uverbs_file *file,  	resp.response_length = offsetof(typeof(resp), response_length) +  		sizeof(resp.response_length); +	cq->res.type = RDMA_RESTRACK_CQ; +	rdma_restrack_add(&cq->res); +  	ret = cb(file, obj, &resp, ucore, context);  	if (ret)  		goto err_cb;  	uobj_alloc_commit(&obj->uobject); -	cq->res.type = RDMA_RESTRACK_CQ; -	rdma_restrack_add(&cq->res); -  	return obj;  err_cb: @@ -1518,7 +1520,8 @@ static int create_qp(struct ib_uverbs_file *file,  	if (cmd->qp_type == IB_QPT_XRC_TGT)  		qp = ib_create_qp(pd, &attr);  	else -		qp = _ib_create_qp(device, pd, &attr, uhw); +		qp = _ib_create_qp(device, pd, &attr, uhw, +				   &obj->uevent.uobject);  	if (IS_ERR(qp)) {  		ret = PTR_ERR(qp); @@ -1550,8 +1553,10 @@ static int create_qp(struct ib_uverbs_file *file,  			atomic_inc(&attr.srq->usecnt);  		if (ind_tbl)  			atomic_inc(&ind_tbl->usecnt); +	} else { +		/* It is done in _ib_create_qp for other QP types */ +		qp->uobject = &obj->uevent.uobject;  	} -	qp->uobject = &obj->uevent.uobject;  	obj->uevent.uobject.object = qp; @@ -1971,8 +1976,15 @@ static int modify_qp(struct ib_uverbs_file *file,  		goto release_qp;  	} +	if ((cmd->base.attr_mask & IB_QP_AV) && +	    !rdma_is_port_valid(qp->device, cmd->base.dest.port_num)) { +		ret = -EINVAL; +		goto release_qp; +	} +  	if ((cmd->base.attr_mask & IB_QP_ALT_PATH) && -	    !rdma_is_port_valid(qp->device, cmd->base.alt_port_num)) { +	    (!rdma_is_port_valid(qp->device, cmd->base.alt_port_num) || +	    !rdma_is_port_valid(qp->device, cmd->base.alt_dest.port_num))) {  		ret = -EINVAL;  		goto release_qp;  	} @@ -2941,6 +2953,11 @@ int ib_uverbs_ex_create_wq(struct ib_uverbs_file *file,  		wq_init_attr.create_flags = cmd.create_flags;  	obj->uevent.events_reported = 0;  	INIT_LIST_HEAD(&obj->uevent.event_list); + +	if (!pd->device->create_wq) { +		err = -EOPNOTSUPP; +		goto err_put_cq; +	}  	wq = pd->device->create_wq(pd, &wq_init_attr, uhw);  	if (IS_ERR(wq)) {  		err = PTR_ERR(wq); @@ -3084,7 +3101,12 @@ int ib_uverbs_ex_modify_wq(struct ib_uverbs_file *file,  		wq_attr.flags = cmd.flags;  		wq_attr.flags_mask = cmd.flags_mask;  	} +	if (!wq->device->modify_wq) { +		ret = -EOPNOTSUPP; +		goto out; +	}  	ret = wq->device->modify_wq(wq, &wq_attr, cmd.attr_mask, uhw); +out:  	uobj_put_obj_read(wq);  	return ret;  } @@ -3181,6 +3203,11 @@ int ib_uverbs_ex_create_rwq_ind_table(struct ib_uverbs_file *file,  	init_attr.log_ind_tbl_size = cmd.log_ind_tbl_size;  	init_attr.ind_tbl = wqs; + +	if (!ib_dev->create_rwq_ind_table) { +		err = -EOPNOTSUPP; +		goto err_uobj; +	}  	rwq_ind_tbl = ib_dev->create_rwq_ind_table(ib_dev, &init_attr, uhw);  	if (IS_ERR(rwq_ind_tbl)) { @@ -3770,6 +3797,9 @@ int ib_uverbs_ex_query_device(struct ib_uverbs_file *file,  	struct ib_device_attr attr = {0};  	int err; +	if (!ib_dev->query_device) +		return -EOPNOTSUPP; +  	if (ucore->inlen < sizeof(cmd))  		return -EINVAL; diff --git a/drivers/infiniband/core/uverbs_ioctl.c b/drivers/infiniband/core/uverbs_ioctl.c index d96dc1d17be1..339b85145044 100644 --- a/drivers/infiniband/core/uverbs_ioctl.c +++ b/drivers/infiniband/core/uverbs_ioctl.c @@ -59,6 +59,9 @@ static int uverbs_process_attr(struct ib_device *ibdev,  			return 0;  	} +	if (test_bit(attr_id, attr_bundle_h->valid_bitmap)) +		return -EINVAL; +  	spec = &attr_spec_bucket->attrs[attr_id];  	e = &elements[attr_id];  	e->uattr = uattr_ptr; diff --git a/drivers/infiniband/core/uverbs_ioctl_merge.c b/drivers/infiniband/core/uverbs_ioctl_merge.c index 062485f9300d..62e1eb1d2a28 100644 --- a/drivers/infiniband/core/uverbs_ioctl_merge.c +++ b/drivers/infiniband/core/uverbs_ioctl_merge.c @@ -114,6 +114,7 @@ static size_t get_elements_above_id(const void **iters,  	short min = SHRT_MAX;  	const void *elem;  	int i, j, last_stored = -1; +	unsigned int equal_min = 0;  	for_each_element(elem, i, j, elements, num_elements, num_offset,  			 data_offset) { @@ -136,6 +137,10 @@ static size_t get_elements_above_id(const void **iters,  		 */  		iters[last_stored == i ? num_iters - 1 : num_iters++] = elem;  		last_stored = i; +		if (min == GET_ID(id)) +			equal_min++; +		else +			equal_min = 1;  		min = GET_ID(id);  	} @@ -146,15 +151,10 @@ static size_t get_elements_above_id(const void **iters,  	 * Therefore, we need to clean the beginning of the array to make sure  	 * all ids of final elements are equal to min.  	 */ -	for (i = num_iters - 1; i >= 0 && -	     GET_ID(*(u16 *)(iters[i] + id_offset)) == min; i--) -		; - -	num_iters -= i + 1; -	memmove(iters, iters + i + 1, sizeof(*iters) * num_iters); +	memmove(iters, iters + num_iters - equal_min, sizeof(*iters) * equal_min);  	*min_id = min; -	return num_iters; +	return equal_min;  }  #define find_max_element_entry_id(num_elements, elements, num_objects_fld, \ @@ -322,7 +322,7 @@ static struct uverbs_method_spec *build_method_with_attrs(const struct uverbs_me  		hash = kzalloc(sizeof(*hash) +  			       ALIGN(sizeof(*hash->attrs) * (attr_max_bucket + 1),  				     sizeof(long)) + -			       BITS_TO_LONGS(attr_max_bucket) * sizeof(long), +			       BITS_TO_LONGS(attr_max_bucket + 1) * sizeof(long),  			       GFP_KERNEL);  		if (!hash) {  			res = -ENOMEM; @@ -509,7 +509,7 @@ static struct uverbs_object_spec *build_object_with_methods(const struct uverbs_  			 * first handler which != NULL. This also defines the  			 * set of flags used for this handler.  			 */ -			for (i = num_object_defs - 1; +			for (i = num_method_defs - 1;  			     i >= 0 && !method_defs[i]->handler; i--)  				;  			hash->methods[min_id++] = method; diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 395a3b091229..b1ca223aa380 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -650,12 +650,21 @@ static int verify_command_mask(struct ib_device *ib_dev, __u32 command)  	return -1;  } +static bool verify_command_idx(u32 command, bool extended) +{ +	if (extended) +		return command < ARRAY_SIZE(uverbs_ex_cmd_table); + +	return command < ARRAY_SIZE(uverbs_cmd_table); +} +  static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,  			     size_t count, loff_t *pos)  {  	struct ib_uverbs_file *file = filp->private_data;  	struct ib_device *ib_dev;  	struct ib_uverbs_cmd_hdr hdr; +	bool extended_command;  	__u32 command;  	__u32 flags;  	int srcu_key; @@ -688,6 +697,15 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,  	}  	command = hdr.command & IB_USER_VERBS_CMD_COMMAND_MASK; +	flags = (hdr.command & +		 IB_USER_VERBS_CMD_FLAGS_MASK) >> IB_USER_VERBS_CMD_FLAGS_SHIFT; + +	extended_command = flags & IB_USER_VERBS_CMD_FLAG_EXTENDED; +	if (!verify_command_idx(command, extended_command)) { +		ret = -EINVAL; +		goto out; +	} +  	if (verify_command_mask(ib_dev, command)) {  		ret = -EOPNOTSUPP;  		goto out; @@ -699,12 +717,8 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,  		goto out;  	} -	flags = (hdr.command & -		 IB_USER_VERBS_CMD_FLAGS_MASK) >> IB_USER_VERBS_CMD_FLAGS_SHIFT; -  	if (!flags) { -		if (command >= ARRAY_SIZE(uverbs_cmd_table) || -		    !uverbs_cmd_table[command]) { +		if (!uverbs_cmd_table[command]) {  			ret = -EINVAL;  			goto out;  		} @@ -725,8 +739,7 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf,  		struct ib_udata uhw;  		size_t written_count = count; -		if (command >= ARRAY_SIZE(uverbs_ex_cmd_table) || -		    !uverbs_ex_cmd_table[command]) { +		if (!uverbs_ex_cmd_table[command]) {  			ret = -ENOSYS;  			goto out;  		} @@ -942,6 +955,7 @@ static const struct file_operations uverbs_fops = {  	.llseek	 = no_llseek,  #if IS_ENABLED(CONFIG_INFINIBAND_EXP_USER_ACCESS)  	.unlocked_ioctl = ib_uverbs_ioctl, +	.compat_ioctl = ib_uverbs_ioctl,  #endif  }; @@ -954,6 +968,7 @@ static const struct file_operations uverbs_mmap_fops = {  	.llseek	 = no_llseek,  #if IS_ENABLED(CONFIG_INFINIBAND_EXP_USER_ACCESS)  	.unlocked_ioctl = ib_uverbs_ioctl, +	.compat_ioctl = ib_uverbs_ioctl,  #endif  }; diff --git a/drivers/infiniband/core/uverbs_std_types.c b/drivers/infiniband/core/uverbs_std_types.c index cab0ac3556eb..df1360e6774f 100644 --- a/drivers/infiniband/core/uverbs_std_types.c +++ b/drivers/infiniband/core/uverbs_std_types.c @@ -234,15 +234,18 @@ static void create_udata(struct uverbs_attr_bundle *ctx,  		uverbs_attr_get(ctx, UVERBS_UHW_OUT);  	if (!IS_ERR(uhw_in)) { -		udata->inbuf = uhw_in->ptr_attr.ptr;  		udata->inlen = uhw_in->ptr_attr.len; +		if (uverbs_attr_ptr_is_inline(uhw_in)) +			udata->inbuf = &uhw_in->uattr->data; +		else +			udata->inbuf = u64_to_user_ptr(uhw_in->ptr_attr.data);  	} else {  		udata->inbuf = NULL;  		udata->inlen = 0;  	}  	if (!IS_ERR(uhw_out)) { -		udata->outbuf = uhw_out->ptr_attr.ptr; +		udata->outbuf = u64_to_user_ptr(uhw_out->ptr_attr.data);  		udata->outlen = uhw_out->ptr_attr.len;  	} else {  		udata->outbuf = NULL; @@ -323,7 +326,8 @@ static int uverbs_create_cq_handler(struct ib_device *ib_dev,  	cq->res.type = RDMA_RESTRACK_CQ;  	rdma_restrack_add(&cq->res); -	ret = uverbs_copy_to(attrs, CREATE_CQ_RESP_CQE, &cq->cqe); +	ret = uverbs_copy_to(attrs, CREATE_CQ_RESP_CQE, &cq->cqe, +			     sizeof(cq->cqe));  	if (ret)  		goto err_cq; @@ -375,7 +379,7 @@ static int uverbs_destroy_cq_handler(struct ib_device *ib_dev,  	resp.comp_events_reported  = obj->comp_events_reported;  	resp.async_events_reported = obj->async_events_reported; -	return uverbs_copy_to(attrs, DESTROY_CQ_RESP, &resp); +	return uverbs_copy_to(attrs, DESTROY_CQ_RESP, &resp, sizeof(resp));  }  static DECLARE_UVERBS_METHOD( diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 16ebc6372c31..93025d2009b8 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -887,7 +887,7 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd,  	if (qp_init_attr->cap.max_rdma_ctxs)  		rdma_rw_init_qp(device, qp_init_attr); -	qp = _ib_create_qp(device, pd, qp_init_attr, NULL); +	qp = _ib_create_qp(device, pd, qp_init_attr, NULL, NULL);  	if (IS_ERR(qp))  		return qp; @@ -898,7 +898,6 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd,  	}  	qp->real_qp    = qp; -	qp->uobject    = NULL;  	qp->qp_type    = qp_init_attr->qp_type;  	qp->rwq_ind_tbl = qp_init_attr->rwq_ind_tbl;  | 

