Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / infiniband / core / user_mad.c
index afe70a5..1273f88 100644 (file)
@@ -112,8 +112,10 @@ struct ib_umad_device {
 struct ib_umad_file {
        struct ib_umad_port    *port;
        struct list_head        recv_list;
+       struct list_head        send_list;
        struct list_head        port_list;
        spinlock_t              recv_lock;
+       spinlock_t              send_lock;
        wait_queue_head_t       recv_wait;
        struct ib_mad_agent    *agent[IB_UMAD_MAX_AGENTS];
        int                     agents_dead;
@@ -177,12 +179,21 @@ static int queue_packet(struct ib_umad_file *file,
        return ret;
 }
 
+static void dequeue_send(struct ib_umad_file *file,
+                        struct ib_umad_packet *packet)
+ {
+       spin_lock_irq(&file->send_lock);
+       list_del(&packet->list);
+       spin_unlock_irq(&file->send_lock);
+ }
+
 static void send_handler(struct ib_mad_agent *agent,
                         struct ib_mad_send_wc *send_wc)
 {
        struct ib_umad_file *file = agent->context;
        struct ib_umad_packet *packet = send_wc->send_buf->context[0];
 
+       dequeue_send(file, packet);
        ib_destroy_ah(packet->msg->ah);
        ib_free_send_mad(packet->msg);
 
@@ -370,6 +381,51 @@ static int copy_rmpp_mad(struct ib_mad_send_buf *msg, const char __user *buf)
        return 0;
 }
 
+static int same_destination(struct ib_user_mad_hdr *hdr1,
+                           struct ib_user_mad_hdr *hdr2)
+{
+       if (!hdr1->grh_present && !hdr2->grh_present)
+          return (hdr1->lid == hdr2->lid);
+
+       if (hdr1->grh_present && hdr2->grh_present)
+          return !memcmp(hdr1->gid, hdr2->gid, 16);
+
+       return 0;
+}
+
+static int is_duplicate(struct ib_umad_file *file,
+                       struct ib_umad_packet *packet)
+{
+       struct ib_umad_packet *sent_packet;
+       struct ib_mad_hdr *sent_hdr, *hdr;
+
+       hdr = (struct ib_mad_hdr *) packet->mad.data;
+       list_for_each_entry(sent_packet, &file->send_list, list) {
+               sent_hdr = (struct ib_mad_hdr *) sent_packet->mad.data;
+
+               if ((hdr->tid != sent_hdr->tid) ||
+                   (hdr->mgmt_class != sent_hdr->mgmt_class))
+                       continue;
+
+               /*
+                * No need to be overly clever here.  If two new operations have
+                * the same TID, reject the second as a duplicate.  This is more
+                * restrictive than required by the spec.
+                */
+               if (!ib_response_mad((struct ib_mad *) hdr)) {
+                       if (!ib_response_mad((struct ib_mad *) sent_hdr))
+                               return 1;
+                       continue;
+               } else if (!ib_response_mad((struct ib_mad *) sent_hdr))
+                       continue;
+
+               if (same_destination(&packet->mad.hdr, &sent_packet->mad.hdr))
+                       return 1;
+       }
+
+       return 0;
+}
+
 static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
                             size_t count, loff_t *pos)
 {
@@ -379,7 +435,6 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
        struct ib_ah_attr ah_attr;
        struct ib_ah *ah;
        struct ib_rmpp_mad *rmpp_mad;
-       u8 method;
        __be64 *tid;
        int ret, data_len, hdr_len, copy_offset, rmpp_active;
 
@@ -473,28 +528,36 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
        }
 
        /*
-        * If userspace is generating a request that will generate a
-        * response, we need to make sure the high-order part of the
-        * transaction ID matches the agent being used to send the
-        * MAD.
+        * Set the high-order part of the transaction ID to make MADs from
+        * different agents unique, and allow routing responses back to the
+        * original requestor.
         */
-       method = ((struct ib_mad_hdr *) packet->msg->mad)->method;
-
-       if (!(method & IB_MGMT_METHOD_RESP)       &&
-           method != IB_MGMT_METHOD_TRAP_REPRESS &&
-           method != IB_MGMT_METHOD_SEND) {
+       if (!ib_response_mad(packet->msg->mad)) {
                tid = &((struct ib_mad_hdr *) packet->msg->mad)->tid;
                *tid = cpu_to_be64(((u64) agent->hi_tid) << 32 |
                                   (be64_to_cpup(tid) & 0xffffffff));
+               rmpp_mad->mad_hdr.tid = *tid;
+       }
+
+       spin_lock_irq(&file->send_lock);
+       ret = is_duplicate(file, packet);
+       if (!ret)
+               list_add_tail(&packet->list, &file->send_list);
+       spin_unlock_irq(&file->send_lock);
+       if (ret) {
+               ret = -EINVAL;
+               goto err_msg;
        }
 
        ret = ib_post_send_mad(packet->msg, NULL);
        if (ret)
-               goto err_msg;
+               goto err_send;
 
        up_read(&file->port->mutex);
        return count;
 
+err_send:
+       dequeue_send(file, packet);
 err_msg:
        ib_free_send_mad(packet->msg);
 err_ah:
@@ -657,7 +720,9 @@ static int ib_umad_open(struct inode *inode, struct file *filp)
        }
 
        spin_lock_init(&file->recv_lock);
+       spin_lock_init(&file->send_lock);
        INIT_LIST_HEAD(&file->recv_list);
+       INIT_LIST_HEAD(&file->send_list);
        init_waitqueue_head(&file->recv_wait);
 
        file->port = port;