}
 
        if (hc->new_map)
-               dm_table_put(hc->new_map);
+               dm_table_destroy(hc->new_map);
        dm_put(hc->md);
        free_cell(hc);
 }
 
                r = dm_swap_table(md, new_map);
                if (r) {
+                       dm_table_destroy(new_map);
                        dm_put(md);
-                       dm_table_put(new_map);
                        return r;
                }
 
                        set_disk_ro(dm_disk(md), 0);
                else
                        set_disk_ro(dm_disk(md), 1);
-
-               dm_table_put(new_map);
        }
 
        if (dm_suspended(md))
        }
 
        if (hc->new_map)
-               dm_table_put(hc->new_map);
+               dm_table_destroy(hc->new_map);
        hc->new_map = t;
        up_write(&_hash_lock);
 
        }
 
        if (hc->new_map) {
-               dm_table_put(hc->new_map);
+               dm_table_destroy(hc->new_map);
                hc->new_map = NULL;
        }
 
 
 /*
  * Copyright (C) 2001 Sistina Software (UK) Limited.
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
  *
  * This file is released under the GPL.
  */
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/mutex.h>
+#include <linux/delay.h>
 #include <asm/atomic.h>
 
 #define DM_MSG_PREFIX "table"
 #define KEYS_PER_NODE (NODE_SIZE / sizeof(sector_t))
 #define CHILDREN_PER_NODE (KEYS_PER_NODE + 1)
 
+/*
+ * The table has always exactly one reference from either mapped_device->map
+ * or hash_cell->new_map. This reference is not counted in table->holders.
+ * A pair of dm_create_table/dm_destroy_table functions is used for table
+ * creation/destruction.
+ *
+ * Temporary references from the other code increase table->holders. A pair
+ * of dm_table_get/dm_table_put functions is used to manipulate it.
+ *
+ * When the table is about to be destroyed, we wait for table->holders to
+ * drop to zero.
+ */
+
 struct dm_table {
        struct mapped_device *md;
        atomic_t holders;
                return -ENOMEM;
 
        INIT_LIST_HEAD(&t->devices);
-       atomic_set(&t->holders, 1);
+       atomic_set(&t->holders, 0);
        t->barriers_supported = 1;
 
        if (!num_targets)
        }
 }
 
-static void table_destroy(struct dm_table *t)
+void dm_table_destroy(struct dm_table *t)
 {
        unsigned int i;
 
+       while (atomic_read(&t->holders))
+               msleep(1);
+       smp_mb();
+
        /* free the indexes (see dm_table_complete) */
        if (t->depth >= 2)
                vfree(t->index[t->depth - 2]);
        if (!t)
                return;
 
-       if (atomic_dec_and_test(&t->holders))
-               table_destroy(t);
+       smp_mb__before_atomic_dec();
+       atomic_dec(&t->holders);
 }
 
 /*
 
        struct mapped_device *md = congested_data;
        struct dm_table *map;
 
-       atomic_inc(&md->pending);
-
        if (!test_bit(DMF_BLOCK_IO, &md->flags)) {
                map = dm_get_table(md);
                if (map) {
                }
        }
 
-       if (!atomic_dec_return(&md->pending))
-               /* nudge anyone waiting on suspend queue */
-               wake_up(&md->wait);
-
        return r;
 }
 
 
        if (md->suspended_bdev)
                __set_size(md, size);
-       if (size == 0)
+
+       if (!size) {
+               dm_table_destroy(t);
                return 0;
+       }
 
-       dm_table_get(t);
        dm_table_event_callback(t, event_callback, md);
 
        write_lock(&md->map_lock);
        write_lock(&md->map_lock);
        md->map = NULL;
        write_unlock(&md->map_lock);
-       dm_table_put(map);
+       dm_table_destroy(map);
 }
 
 /*
 
 /*-----------------------------------------------------------------
  * Internal table functions.
  *---------------------------------------------------------------*/
+void dm_table_destroy(struct dm_table *t);
 void dm_table_event_callback(struct dm_table *t,
                             void (*fn)(void *), void *context);
 struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index);