diff options
Diffstat (limited to 'drivers/block/rbd.c')
-rw-r--r-- | drivers/block/rbd.c | 25 |
1 files changed, 7 insertions, 18 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 3ec85dfce124..8a86b62466f7 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c | |||
@@ -2098,32 +2098,26 @@ static void rbd_dev_parent_put(struct rbd_device *rbd_dev) | |||
2098 | * If an image has a non-zero parent overlap, get a reference to its | 2098 | * If an image has a non-zero parent overlap, get a reference to its |
2099 | * parent. | 2099 | * parent. |
2100 | * | 2100 | * |
2101 | * We must get the reference before checking for the overlap to | ||
2102 | * coordinate properly with zeroing the parent overlap in | ||
2103 | * rbd_dev_v2_parent_info() when an image gets flattened. We | ||
2104 | * drop it again if there is no overlap. | ||
2105 | * | ||
2106 | * Returns true if the rbd device has a parent with a non-zero | 2101 | * Returns true if the rbd device has a parent with a non-zero |
2107 | * overlap and a reference for it was successfully taken, or | 2102 | * overlap and a reference for it was successfully taken, or |
2108 | * false otherwise. | 2103 | * false otherwise. |
2109 | */ | 2104 | */ |
2110 | static bool rbd_dev_parent_get(struct rbd_device *rbd_dev) | 2105 | static bool rbd_dev_parent_get(struct rbd_device *rbd_dev) |
2111 | { | 2106 | { |
2112 | int counter; | 2107 | int counter = 0; |
2113 | 2108 | ||
2114 | if (!rbd_dev->parent_spec) | 2109 | if (!rbd_dev->parent_spec) |
2115 | return false; | 2110 | return false; |
2116 | 2111 | ||
2117 | counter = atomic_inc_return_safe(&rbd_dev->parent_ref); | 2112 | down_read(&rbd_dev->header_rwsem); |
2118 | if (counter > 0 && rbd_dev->parent_overlap) | 2113 | if (rbd_dev->parent_overlap) |
2119 | return true; | 2114 | counter = atomic_inc_return_safe(&rbd_dev->parent_ref); |
2120 | 2115 | up_read(&rbd_dev->header_rwsem); | |
2121 | /* Image was flattened, but parent is not yet torn down */ | ||
2122 | 2116 | ||
2123 | if (counter < 0) | 2117 | if (counter < 0) |
2124 | rbd_warn(rbd_dev, "parent reference overflow"); | 2118 | rbd_warn(rbd_dev, "parent reference overflow"); |
2125 | 2119 | ||
2126 | return false; | 2120 | return counter > 0; |
2127 | } | 2121 | } |
2128 | 2122 | ||
2129 | /* | 2123 | /* |
@@ -4239,7 +4233,6 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) | |||
4239 | */ | 4233 | */ |
4240 | if (rbd_dev->parent_overlap) { | 4234 | if (rbd_dev->parent_overlap) { |
4241 | rbd_dev->parent_overlap = 0; | 4235 | rbd_dev->parent_overlap = 0; |
4242 | smp_mb(); | ||
4243 | rbd_dev_parent_put(rbd_dev); | 4236 | rbd_dev_parent_put(rbd_dev); |
4244 | pr_info("%s: clone image has been flattened\n", | 4237 | pr_info("%s: clone image has been flattened\n", |
4245 | rbd_dev->disk->disk_name); | 4238 | rbd_dev->disk->disk_name); |
@@ -4285,7 +4278,6 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) | |||
4285 | * treat it specially. | 4278 | * treat it specially. |
4286 | */ | 4279 | */ |
4287 | rbd_dev->parent_overlap = overlap; | 4280 | rbd_dev->parent_overlap = overlap; |
4288 | smp_mb(); | ||
4289 | if (!overlap) { | 4281 | if (!overlap) { |
4290 | 4282 | ||
4291 | /* A null parent_spec indicates it's the initial probe */ | 4283 | /* A null parent_spec indicates it's the initial probe */ |
@@ -5114,10 +5106,7 @@ static void rbd_dev_unprobe(struct rbd_device *rbd_dev) | |||
5114 | { | 5106 | { |
5115 | struct rbd_image_header *header; | 5107 | struct rbd_image_header *header; |
5116 | 5108 | ||
5117 | /* Drop parent reference unless it's already been done (or none) */ | 5109 | rbd_dev_parent_put(rbd_dev); |
5118 | |||
5119 | if (rbd_dev->parent_overlap) | ||
5120 | rbd_dev_parent_put(rbd_dev); | ||
5121 | 5110 | ||
5122 | /* Free dynamic fields from the header, then zero it out */ | 5111 | /* Free dynamic fields from the header, then zero it out */ |
5123 | 5112 | ||