emac: Fix clock control for 405EX and 405EXr chips
[safe/jmp/linux-2.6] / drivers / md / dm-table.c
index a63161a..2fd66c3 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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.
  */
@@ -15,6 +15,7 @@
 #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;
@@ -38,6 +52,8 @@ struct dm_table {
        sector_t *highs;
        struct dm_target *targets;
 
+       unsigned barriers_supported:1;
+
        /*
         * Indicates the rw permissions for the new logical
         * device.  This should be a combination of FMODE_READ
@@ -226,7 +242,8 @@ int dm_table_create(struct dm_table **result, fmode_t mode,
                return -ENOMEM;
 
        INIT_LIST_HEAD(&t->devices);
-       atomic_set(&t->holders, 1);
+       atomic_set(&t->holders, 0);
+       t->barriers_supported = 1;
 
        if (!num_targets)
                num_targets = KEYS_PER_NODE;
@@ -256,10 +273,14 @@ static void free_devices(struct list_head *devices)
        }
 }
 
-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]);
@@ -297,8 +318,8 @@ void dm_table_put(struct dm_table *t)
        if (!t)
                return;
 
-       if (atomic_dec_and_test(&t->holders))
-               table_destroy(t);
+       smp_mb__before_atomic_dec();
+       atomic_dec(&t->holders);
 }
 
 /*
@@ -668,7 +689,7 @@ static void check_for_valid_limits(struct io_restrictions *rs)
        if (!rs->max_segment_size)
                rs->max_segment_size = MAX_SEGMENT_SIZE;
        if (!rs->seg_boundary_mask)
-               rs->seg_boundary_mask = -1;
+               rs->seg_boundary_mask = BLK_SEG_BOUNDARY_MASK;
        if (!rs->bounce_pfn)
                rs->bounce_pfn = -1;
 }
@@ -728,6 +749,10 @@ int dm_table_add_target(struct dm_table *t, const char *type,
        /* FIXME: the plan is to combine high here and then have
         * the merge fn apply the target level restrictions. */
        combine_restrictions_low(&t->limits, &tgt->limits);
+
+       if (!(tgt->type->features & DM_TARGET_SUPPORTS_BARRIERS))
+               t->barriers_supported = 0;
+
        return 0;
 
  bad:
@@ -772,6 +797,12 @@ int dm_table_complete(struct dm_table *t)
 
        check_for_valid_limits(&t->limits);
 
+       /*
+        * We only support barriers if there is exactly one underlying device.
+        */
+       if (!list_is_singular(&t->devices))
+               t->barriers_supported = 0;
+
        /* how many indexes will the btree have ? */
        leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE);
        t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE);
@@ -986,6 +1017,12 @@ struct mapped_device *dm_table_get_md(struct dm_table *t)
        return t->md;
 }
 
+int dm_table_barrier_ok(struct dm_table *t)
+{
+       return t->barriers_supported;
+}
+EXPORT_SYMBOL(dm_table_barrier_ok);
+
 EXPORT_SYMBOL(dm_vcalloc);
 EXPORT_SYMBOL(dm_get_device);
 EXPORT_SYMBOL(dm_put_device);