RDS: Do not call set_page_dirty() with irqs off
authorAndy Grover <andy.grover@oracle.com>
Thu, 11 Mar 2010 13:50:06 +0000 (13:50 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 17 Mar 2010 04:17:01 +0000 (21:17 -0700)
set_page_dirty() unconditionally re-enables interrupts, so
if we call it with irqs off, they will be on after the call,
and that's bad. This patch moves the call after we've re-enabled
interrupts in send_drop_to(), so it's safe.

Also, add BUG_ONs to let us know if we ever do call set_page_dirty
with interrupts off.

Signed-off-by: Andy Grover <andy.grover@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/rds/ib_rdma.c
net/rds/rdma.c
net/rds/send.c

index 65e668defe4285648f79193d1c342f877a3f25fb..cfb1d904ed00c9abcde8e96d433e99a99cba89ff 100644 (file)
@@ -440,6 +440,7 @@ static void __rds_ib_teardown_mr(struct rds_ib_mr *ibmr)
 
                        /* FIXME we need a way to tell a r/w MR
                         * from a r/o MR */
+                       BUG_ON(in_interrupt());
                        set_page_dirty(page);
                        put_page(page);
                }
index 4c64daa1f5d52b0eb72b6920bd7f4a39a3a756b9..61b359d9dffd5aafdd3efaaee54eb580b560297d 100644 (file)
@@ -438,8 +438,10 @@ void rds_rdma_free_op(struct rds_rdma_op *ro)
                /* Mark page dirty if it was possibly modified, which
                 * is the case for a RDMA_READ which copies from remote
                 * to local memory */
-               if (!ro->r_write)
+               if (!ro->r_write) {
+                       BUG_ON(in_interrupt());
                        set_page_dirty(page);
+               }
                put_page(page);
        }
 
index 51e2def50b12fabe5a8c90fdf3a8b7714c6c406e..4629a0b63bbd8f6cc4d0763f945c197ade8c9d92 100644 (file)
@@ -507,12 +507,13 @@ EXPORT_SYMBOL_GPL(rds_send_get_message);
  */
 void rds_send_remove_from_sock(struct list_head *messages, int status)
 {
-       unsigned long flags = 0; /* silence gcc :P */
+       unsigned long flags;
        struct rds_sock *rs = NULL;
        struct rds_message *rm;
 
-       local_irq_save(flags);
        while (!list_empty(messages)) {
+               int was_on_sock = 0;
+
                rm = list_entry(messages->next, struct rds_message,
                                m_conn_item);
                list_del_init(&rm->m_conn_item);
@@ -527,7 +528,7 @@ void rds_send_remove_from_sock(struct list_head *messages, int status)
                 * while we're messing with it. It does not prevent the
                 * message from being removed from the socket, though.
                 */
-               spin_lock(&rm->m_rs_lock);
+               spin_lock_irqsave(&rm->m_rs_lock, flags);
                if (!test_bit(RDS_MSG_ON_SOCK, &rm->m_flags))
                        goto unlock_and_drop;
 
@@ -556,21 +557,22 @@ void rds_send_remove_from_sock(struct list_head *messages, int status)
                                        notifier->n_status = status;
                                rm->m_rdma_op->r_notifier = NULL;
                        }
-                       rds_message_put(rm);
+                       was_on_sock = 1;
                        rm->m_rs = NULL;
                }
                spin_unlock(&rs->rs_lock);
 
 unlock_and_drop:
-               spin_unlock(&rm->m_rs_lock);
+               spin_unlock_irqrestore(&rm->m_rs_lock, flags);
                rds_message_put(rm);
+               if (was_on_sock)
+                       rds_message_put(rm);
        }
 
        if (rs) {
                rds_wake_sk_sleep(rs);
                sock_put(rds_rs_to_sk(rs));
        }
-       local_irq_restore(flags);
 }
 
 /*