Commit 0ac8903c authored by Jason Gunthorpe's avatar Jason Gunthorpe
Browse files

RDMA/core: Allow the ioctl layer to abort a fully created uobject

While creating a uobject every create reaches a point where the uobject is
fully initialized. For ioctls that go on to copy_to_user this means they
need to open code the destruction of a fully created uobject - ie the
RDMA_REMOVE_DESTROY sort of flow.

Open coding this creates bugs, eg the CQ does not properly flush the
events list when it does its error unwind.

Provide a uverbs_finalize_uobj_create() function which indicates that the
uobject is fully initialized and that abort should call to destroy_hw to
destroy the uobj->object and related.

Methods can call this function if they go on to have error cases after
setting uobj->object. Once done those error cases can simply do return,
without an error unwind.

Link: https://lore.kernel.org/r/20200519072711.257271-2-leon@kernel.org


Signed-off-by: default avatarYishai Hadas <yishaih@mellanox.com>
Signed-off-by: default avatarLeon Romanovsky <leonro@mellanox.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
parent eafd47fc
Loading
Loading
Loading
Loading
+20 −5
Original line number Diff line number Diff line
@@ -130,6 +130,17 @@ static int uverbs_destroy_uobject(struct ib_uobject *uobj,
	lockdep_assert_held(&ufile->hw_destroy_rwsem);
	assert_uverbs_usecnt(uobj, UVERBS_LOOKUP_WRITE);

	if (reason == RDMA_REMOVE_ABORT_HWOBJ) {
		reason = RDMA_REMOVE_ABORT;
		ret = uobj->uapi_object->type_class->destroy_hw(uobj, reason,
								attrs);
		/*
		 * Drivers are not permitted to ignore RDMA_REMOVE_ABORT, see
		 * ib_is_destroy_retryable, cleanup_retryable == false here.
		 */
		WARN_ON(ret);
	}

	if (reason == RDMA_REMOVE_ABORT) {
		WARN_ON(!list_empty(&uobj->list));
		WARN_ON(!uobj->context);
@@ -647,11 +658,15 @@ void rdma_alloc_commit_uobject(struct ib_uobject *uobj,
 * object and anything else connected to uobj before calling this.
 */
void rdma_alloc_abort_uobject(struct ib_uobject *uobj,
			      struct uverbs_attr_bundle *attrs)
			      struct uverbs_attr_bundle *attrs,
			      bool hw_obj_valid)
{
	struct ib_uverbs_file *ufile = uobj->ufile;

	uverbs_destroy_uobject(uobj, RDMA_REMOVE_ABORT, attrs);
	uverbs_destroy_uobject(uobj,
			       hw_obj_valid ? RDMA_REMOVE_ABORT_HWOBJ :
					      RDMA_REMOVE_ABORT,
			       attrs);

	/* Matches the down_read in rdma_alloc_begin_uobject */
	up_read(&ufile->hw_destroy_rwsem);
@@ -921,8 +936,8 @@ uverbs_get_uobject_from_file(u16 object_id, enum uverbs_obj_access access,
}

void uverbs_finalize_object(struct ib_uobject *uobj,
			    enum uverbs_obj_access access, bool commit,
			    struct uverbs_attr_bundle *attrs)
			    enum uverbs_obj_access access, bool hw_obj_valid,
			    bool commit, struct uverbs_attr_bundle *attrs)
{
	/*
	 * refcounts should be handled at the object level and not at the
@@ -945,7 +960,7 @@ void uverbs_finalize_object(struct ib_uobject *uobj,
		if (commit)
			rdma_alloc_commit_uobject(uobj, attrs);
		else
			rdma_alloc_abort_uobject(uobj, attrs);
			rdma_alloc_abort_uobject(uobj, attrs, hw_obj_valid);
		break;
	default:
		WARN_ON(true);
+2 −2
Original line number Diff line number Diff line
@@ -64,8 +64,8 @@ uverbs_get_uobject_from_file(u16 object_id, enum uverbs_obj_access access,
			     s64 id, struct uverbs_attr_bundle *attrs);

void uverbs_finalize_object(struct ib_uobject *uobj,
			    enum uverbs_obj_access access, bool commit,
			    struct uverbs_attr_bundle *attrs);
			    enum uverbs_obj_access access, bool hw_obj_valid,
			    bool commit, struct uverbs_attr_bundle *attrs);

int uverbs_output_written(const struct uverbs_attr_bundle *bundle, size_t idx);

+1 −1
Original line number Diff line number Diff line
@@ -311,7 +311,7 @@ static int ib_uverbs_get_context(struct uverbs_attr_bundle *attrs)
	return 0;

err_uobj:
	rdma_alloc_abort_uobject(uobj, attrs);
	rdma_alloc_abort_uobject(uobj, attrs, false);
err_ucontext:
	kfree(attrs->context);
	attrs->context = NULL;
+20 −2
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ struct bundle_priv {

	DECLARE_BITMAP(uobj_finalize, UVERBS_API_ATTR_BKEY_LEN);
	DECLARE_BITMAP(spec_finalize, UVERBS_API_ATTR_BKEY_LEN);
	DECLARE_BITMAP(uobj_hw_obj_valid, UVERBS_API_ATTR_BKEY_LEN);

	/*
	 * Must be last. bundle ends in a flex array which overlaps
@@ -230,7 +231,8 @@ static void uverbs_free_idrs_array(const struct uverbs_api_attr *attr_uapi,

	for (i = 0; i != attr->len; i++)
		uverbs_finalize_object(attr->uobjects[i],
				       spec->u2.objs_arr.access, commit, attrs);
				       spec->u2.objs_arr.access, false, commit,
				       attrs);
}

static int uverbs_process_attr(struct bundle_priv *pbundle,
@@ -502,7 +504,9 @@ static void bundle_destroy(struct bundle_priv *pbundle, bool commit)

		uverbs_finalize_object(
			attr->obj_attr.uobject,
			attr->obj_attr.attr_elm->spec.u.obj.access, commit,
			attr->obj_attr.attr_elm->spec.u.obj.access,
			test_bit(i, pbundle->uobj_hw_obj_valid),
			commit,
			&pbundle->bundle);
	}

@@ -590,6 +594,8 @@ static int ib_uverbs_cmd_verbs(struct ib_uverbs_file *ufile,
	       sizeof(pbundle->bundle.attr_present));
	memset(pbundle->uobj_finalize, 0, sizeof(pbundle->uobj_finalize));
	memset(pbundle->spec_finalize, 0, sizeof(pbundle->spec_finalize));
	memset(pbundle->uobj_hw_obj_valid, 0,
	       sizeof(pbundle->uobj_hw_obj_valid));

	ret = ib_uverbs_run_method(pbundle, hdr->num_attrs);
	bundle_destroy(pbundle, ret == 0);
@@ -784,3 +790,15 @@ int uverbs_copy_to_struct_or_zero(const struct uverbs_attr_bundle *bundle,
	}
	return uverbs_copy_to(bundle, idx, from, size);
}

/* Once called an abort will call through to the type's destroy_hw() */
void uverbs_finalize_uobj_create(const struct uverbs_attr_bundle *bundle,
				 u16 idx)
{
	struct bundle_priv *pbundle =
		container_of(bundle, struct bundle_priv, bundle);

	__set_bit(uapi_bkey_attr(uapi_key_attr(idx)),
		  pbundle->uobj_hw_obj_valid);
}
EXPORT_SYMBOL(uverbs_finalize_uobj_create);
+2 −6
Original line number Diff line number Diff line
@@ -129,16 +129,12 @@ static int UVERBS_HANDLER(UVERBS_METHOD_CQ_CREATE)(
	obj->uevent.uobject.object = cq;
	obj->uevent.uobject.user_handle = user_handle;
	rdma_restrack_uadd(&cq->res);
	uverbs_finalize_uobj_create(attrs, UVERBS_ATTR_CREATE_CQ_HANDLE);

	ret = uverbs_copy_to(attrs, UVERBS_ATTR_CREATE_CQ_RESP_CQE, &cq->cqe,
			     sizeof(cq->cqe));
	if (ret)
		goto err_cq;
	return ret;

	return 0;
err_cq:
	ib_destroy_cq_user(cq, uverbs_get_cleared_udata(attrs));
	cq = NULL;
err_free:
	kfree(cq);
err_event_file:
Loading