Remove the additional data copy when handing a received MAD up to userspace.
Only a single data copy to the user's buffer is now performed.  Checks for
the correct userspace buffer size are also tightened.

Signed-off-by: Sean Hefty <[EMAIL PROTECTED]>

---

NOTE: I tested this on a node running opensm, but I have no way of testing that
received RMPP MADs are copied correctly.


Index: user_mad.c
===================================================================
--- user_mad.c  (revision 5532)
+++ user_mad.c  (working copy)
@@ -121,9 +121,9 @@ struct ib_umad_file {
 
 struct ib_umad_packet {
        struct ib_mad_send_buf *msg;
+       struct ib_mad_recv_wc  *recv_wc;
        struct list_head   list;
        int                length;
-       struct list_head   seg_list;
        struct ib_user_mad mad;
 };
 
@@ -188,62 +188,6 @@ static int data_offset(u8 mgmt_class)
                return IB_MGMT_RMPP_HDR;
 }
 
-static int copy_recv_mad(struct ib_mad_recv_wc *mad_recv_wc,
-                        struct ib_umad_packet *packet)
-{
-       struct ib_mad_recv_buf *seg_buf;
-       struct ib_rmpp_mad *rmpp_mad;
-       void *data;
-       struct ib_rmpp_segment *seg;
-       int size, len, offset;
-       u8 flags;
-
-       len = mad_recv_wc->mad_len;
-       if (len <= sizeof(struct ib_mad)) {
-               memcpy(&packet->mad.data, mad_recv_wc->recv_buf.mad, len);
-               return 0;
-       }
-
-       /* Multipacket (RMPP) MAD */
-       offset = data_offset(mad_recv_wc->recv_buf.mad->mad_hdr.mgmt_class);
-
-       list_for_each_entry(seg_buf, &mad_recv_wc->rmpp_list, list) {
-               rmpp_mad = (struct ib_rmpp_mad *) seg_buf->mad;
-               flags = ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr);
-
-               if (flags & IB_MGMT_RMPP_FLAG_FIRST) {
-                       size = sizeof(*rmpp_mad);
-                       memcpy(&packet->mad.data, rmpp_mad, size);
-               } else {
-                       data = (void *) rmpp_mad + offset;
-                       if (flags & IB_MGMT_RMPP_FLAG_LAST)
-                               size = len;
-                       else
-                               size = sizeof(*rmpp_mad) - offset;
-                       seg = kmalloc(sizeof(struct ib_rmpp_segment) +
-                                     sizeof(struct ib_rmpp_mad) - offset,
-                                     GFP_KERNEL);
-                       if (!seg)
-                               return -ENOMEM;
-                       memcpy(seg->data, data, size);
-                       list_add_tail(&seg->list, &packet->seg_list);
-               }
-               len -= size;
-       }
-       return 0;
-}
-
-static void free_packet(struct ib_umad_packet *packet)
-{
-       struct ib_rmpp_segment *seg, *tmp;
-
-       list_for_each_entry_safe(seg, tmp, &packet->seg_list, list) {
-               list_del(&seg->list);
-               kfree(seg);
-       }
-       kfree(packet);
-}
-
 static void send_handler(struct ib_mad_agent *agent,
                         struct ib_mad_send_wc *send_wc)
 {
@@ -267,25 +211,20 @@ static void recv_handler(struct ib_mad_a
 {
        struct ib_umad_file *file = agent->context;
        struct ib_umad_packet *packet;
-       int length;
 
        if (mad_recv_wc->wc->status != IB_WC_SUCCESS)
-               goto out;
+               goto err1;
 
-       length = mad_recv_wc->mad_len;
-       packet = kzalloc(sizeof *packet + sizeof(struct ib_mad), GFP_KERNEL);
+       packet = kzalloc(sizeof *packet, GFP_KERNEL);
        if (!packet)
-               goto out;
-       INIT_LIST_HEAD(&packet->seg_list);
-       packet->length = length;
+               goto err1;
 
-       if (copy_recv_mad(mad_recv_wc, packet)) {
-               free_packet(packet);
-               goto out;
-       }
+       packet->length = mad_recv_wc->mad_len;
+       packet->recv_wc = mad_recv_wc;
 
        packet->mad.hdr.status    = 0;
-       packet->mad.hdr.length    = length + sizeof (struct ib_user_mad);
+       packet->mad.hdr.length    = sizeof (struct ib_user_mad) +
+                                   mad_recv_wc->mad_len;
        packet->mad.hdr.qpn       = cpu_to_be32(mad_recv_wc->wc->src_qp);
        packet->mad.hdr.lid       = cpu_to_be16(mad_recv_wc->wc->slid);
        packet->mad.hdr.sl        = mad_recv_wc->wc->sl;
@@ -301,21 +240,87 @@ static void recv_handler(struct ib_mad_a
        }
 
        if (queue_packet(file, agent, packet))
-               free_packet(packet);
+               goto err2;
+       return;
 
-out:
+err2:
+       kfree(packet);
+err1:
        ib_free_recv_mad(mad_recv_wc);
 }
 
+static ssize_t copy_recv_mad(char __user *buf, struct ib_umad_packet *packet,
+                            size_t count)
+{
+       struct ib_mad_recv_buf *recv_buf;
+       int left, seg_payload, offset, max_seg_payload;
+
+       /* We need enough room to copy the first (or only) MAD segment. */
+       recv_buf = &packet->recv_wc->recv_buf;
+       if ((packet->length <= sizeof (*recv_buf->mad) &&
+            count < sizeof (packet->mad) + packet->length) ||
+           (packet->length > sizeof (*recv_buf->mad) &&
+            count < sizeof (packet->mad) + sizeof (*recv_buf->mad)))
+               return -EINVAL;
+
+       if (copy_to_user(buf, &packet->mad, sizeof (packet->mad)))
+               return -EFAULT;
+
+       buf += sizeof (packet->mad);
+       seg_payload = min_t(int, packet->length, sizeof (*recv_buf->mad));
+       if (copy_to_user(buf, recv_buf->mad, seg_payload))
+               return -EFAULT;
+
+       if (seg_payload < packet->length) {
+               /*
+                * Multipacket RMPP MAD message. Copy remainder of message.
+                * Note that last segment may have a shorter payload.
+                */
+               if (count < sizeof (packet->mad) + packet->length) {
+                       /*
+                        * The buffer is too small, return the first RMPP 
segment,
+                        * which includes the RMPP message length.
+                        */
+                       return -ENOSPC;
+               }
+               offset = data_offset(recv_buf->mad->mad_hdr.mgmt_class);
+               max_seg_payload = sizeof (struct ib_mad) - offset;
+
+               for (left = packet->length - seg_payload, buf += seg_payload;
+                    left; left -= seg_payload, buf += seg_payload) {
+                       recv_buf = container_of(recv_buf->list.next,
+                                               struct ib_mad_recv_buf, list);
+                       seg_payload = min(left, max_seg_payload);
+                       if (copy_to_user(buf, ((void *) recv_buf->mad) + offset,
+                                        seg_payload))
+                               return -EFAULT;
+               }
+       }
+       return sizeof (packet->mad) + packet->length;
+}
+
+static ssize_t copy_send_mad(char __user *buf, struct ib_umad_packet *packet,
+                            size_t count)
+{
+       ssize_t size = sizeof (packet->mad) + packet->length;
+
+       if (count < size)
+               return -EINVAL;
+
+       if (copy_to_user(buf, &packet->mad, size))
+               return -EFAULT;
+
+       return size;
+}
+
 static ssize_t ib_umad_read(struct file *filp, char __user *buf,
                            size_t count, loff_t *pos)
 {
        struct ib_umad_file *file = filp->private_data;
-       struct ib_rmpp_segment *seg;
        struct ib_umad_packet *packet;
-       ssize_t ret, size;
+       ssize_t ret;
 
-       if (count < sizeof (struct ib_user_mad) + sizeof (struct ib_mad))
+       if (count < sizeof (struct ib_user_mad))
                return -EINVAL;
 
        spin_lock_irq(&file->recv_lock);
@@ -338,52 +343,21 @@ static ssize_t ib_umad_read(struct file 
 
        spin_unlock_irq(&file->recv_lock);
 
-       size = min_t(int, sizeof (struct ib_mad), packet->length);
-       if (copy_to_user(buf, &packet->mad,
-                        sizeof(struct ib_user_mad) + size)) {
-               ret = -EFAULT;
-               goto err;
-       }
+       if (packet->recv_wc)
+               ret = copy_recv_mad(buf, packet, count);
+       else
+               ret = copy_send_mad(buf, packet, count);
 
-       if (count < packet->length + sizeof (struct ib_user_mad))
-               /*
-                * User buffer too small. Return first RMPP segment (which
-                * includes RMPP message length).
-                */
-               ret = -ENOSPC;
-       else if (packet->length <= sizeof(struct ib_mad))
-               ret = packet->length + sizeof(struct ib_user_mad);
-       else {
-               int len = packet->length - sizeof(struct ib_mad);
-               struct ib_rmpp_mad *rmpp_mad =
-                               (struct ib_rmpp_mad *) packet->mad.data;
-               int max_seg_payload = sizeof(struct ib_mad) -
-                                     data_offset(rmpp_mad->mad_hdr.mgmt_class);
-               int seg_payload;
-               /*
-                * Multipacket RMPP MAD message. Copy remainder of message.
-                * Note that last segment may have a shorter payload.
-                */
-               buf += sizeof(struct ib_user_mad) + sizeof(struct ib_mad);
-               list_for_each_entry(seg, &packet->seg_list, list) {
-                       seg_payload = min_t(int, len, max_seg_payload);
-                       if (copy_to_user(buf, seg->data, seg_payload)) {
-                               ret = -EFAULT;
-                               goto err;
-                       }
-                       buf += seg_payload;
-                       len -= seg_payload;
-               }
-               ret = packet->length + sizeof (struct ib_user_mad);
-       }
-err:
        if (ret < 0) {
                /* Requeue packet */
                spin_lock_irq(&file->recv_lock);
                list_add(&packet->list, &file->recv_list);
                spin_unlock_irq(&file->recv_lock);
-       } else
-               free_packet(packet);
+       } else {
+               if (packet->recv_wc)
+                       ib_free_recv_mad(packet->recv_wc);
+               kfree(packet);
+       }
        return ret;
 }
 



_______________________________________________
openib-general mailing list
[email protected]
http://openib.org/mailman/listinfo/openib-general

To unsubscribe, please visit http://openib.org/mailman/listinfo/openib-general

Reply via email to