Re: [patch v4] infiniband: uverbs: handle large number of entries

Roland Dreier
2010-11-24T22:07:40Z

> Crap! Apparently c99 initialization zeroes out the holes most of the
> time but not all the time.

I think in this case we would be OK, since the structs involved here
don't have holes. Right?

However, I think this final patch is not really what I would like here.
I didn't follow the discussion carefully, but it seems things ended up
off track.

poll_cq is a data path operation where performance matters, and the
whole point of passing in cmd.ne (the number of completion entries to
poll) is to allow the low level driver to batch things up to save
overhead. So converting this into a loop that polls one at a time is
not the best thing to do.

What was wrong with:

- Fixing the potential integer overflow by capping cmd.ne at some high
but safe value (1000 say)

- Fixing the information leaks by setting resp->reserved = 0 and inside
the loop resp->wc[i].reserved = 0, and then only copying the actual
number of completions polled to userspace

- And as a bonus handling the (nearly impossible) case of ib_poll_cq
returning a negative value

IOW a patch like the below (compile tested only):

diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index b342248..ec6e434 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -903,17 +903,17 @@ ssize_t ib_uverbs_poll_cq(struct ib_uverbs_file *file,
struct ib_wc *wc;
int ret = 0;
int i;
- int rsize;

if (copy_from_user(&cmd, buf, sizeof cmd))
return -EFAULT;

+ cmd.ne = min_t(u32, cmd.ne, IB_UVERBS_POLL_CQ_MAX_ENTRIES);
+
wc = kmalloc(cmd.ne * sizeof *wc, GFP_KERNEL);
if (!wc)
return -ENOMEM;

- rsize = sizeof *resp + cmd.ne * sizeof(struct ib_uverbs_wc);
- resp = kmalloc(rsize, GFP_KERNEL);
+ resp = kmalloc(sizeof *resp + cmd.ne * sizeof *resp->wc, GFP_KERNEL);
if (!resp) {
ret = -ENOMEM;
goto out_wc;
@@ -925,10 +925,17 @@ ssize_t ib_uverbs_poll_cq(struct ib_uverbs_file *file,
goto out;
}

- resp->count = ib_poll_cq(cq, cmd.ne, wc);
+ ret = ib_poll_cq(cq, cmd.ne, wc);
+ if (ret < 0) {
+ ret = -EIO;
+ goto out;
+ }

put_cq_read(cq);

+ resp->count = ret;
+ resp->reserved = 0;
+
for (i = 0; i < resp->count; i++) {
resp->wc[i].wr_id = wc[i].wr_id;
resp->wc[i].status = wc[i].status;
@@ -944,9 +951,11 @@ ssize_t ib_uverbs_poll_cq(struct ib_uverbs_file *file,
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;
+ resp->wc[i].reserved = 0;
}

- if (copy_to_user((void __user *) (unsigned long) cmd.response, resp, rsize))
+ if (copy_to_user((void __user *) (unsigned long) cmd.response, resp,
+ sizeof *resp + resp->count * sizeof *resp->wc))
ret = -EFAULT;

out:
diff --git a/include/rdma/ib_user_verbs.h b/include/rdma/ib_user_verbs.h
index fe5b051..61b0286 100644
--- a/include/rdma/ib_user_verbs.h
+++ b/include/rdma/ib_user_verbs.h
@@ -278,6 +278,10 @@ struct ib_uverbs_resize_cq_resp {
__u64 driver_data[0];
};

+enum {
+ IB_UVERBS_POLL_CQ_MAX_ENTRIES = 1000
+};
+
struct ib_uverbs_poll_cq {
__u64 response;
__u32 cq_handle;