|
@@ -1883,10 +1883,12 @@ static int remove_extent_backref(struct btrfs_trans_handle *trans,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+#define in_range(b, first, len) ((b) >= (first) && (b) < (first) + (len))
|
|
|
static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len,
|
|
|
u64 *discarded_bytes)
|
|
|
{
|
|
|
- int ret = 0;
|
|
|
+ int j, ret = 0;
|
|
|
+ u64 bytes_left, end;
|
|
|
u64 aligned_start = ALIGN(start, 1 << 9);
|
|
|
|
|
|
if (WARN_ON(start != aligned_start)) {
|
|
@@ -1896,11 +1898,60 @@ static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len,
|
|
|
}
|
|
|
|
|
|
*discarded_bytes = 0;
|
|
|
- if (len) {
|
|
|
- ret = blkdev_issue_discard(bdev, start >> 9, len >> 9,
|
|
|
+
|
|
|
+ if (!len)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ end = start + len;
|
|
|
+ bytes_left = len;
|
|
|
+
|
|
|
+ /* Skip any superblocks on this device. */
|
|
|
+ for (j = 0; j < BTRFS_SUPER_MIRROR_MAX; j++) {
|
|
|
+ u64 sb_start = btrfs_sb_offset(j);
|
|
|
+ u64 sb_end = sb_start + BTRFS_SUPER_INFO_SIZE;
|
|
|
+ u64 size = sb_start - start;
|
|
|
+
|
|
|
+ if (!in_range(sb_start, start, bytes_left) &&
|
|
|
+ !in_range(sb_end, start, bytes_left) &&
|
|
|
+ !in_range(start, sb_start, BTRFS_SUPER_INFO_SIZE))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Superblock spans beginning of range. Adjust start and
|
|
|
+ * try again.
|
|
|
+ */
|
|
|
+ if (sb_start <= start) {
|
|
|
+ start += sb_end - start;
|
|
|
+ if (start > end) {
|
|
|
+ bytes_left = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ bytes_left = end - start;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (size) {
|
|
|
+ ret = blkdev_issue_discard(bdev, start >> 9, size >> 9,
|
|
|
+ GFP_NOFS, 0);
|
|
|
+ if (!ret)
|
|
|
+ *discarded_bytes += size;
|
|
|
+ else if (ret != -EOPNOTSUPP)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ start = sb_end;
|
|
|
+ if (start > end) {
|
|
|
+ bytes_left = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ bytes_left = end - start;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (bytes_left) {
|
|
|
+ ret = blkdev_issue_discard(bdev, start >> 9, bytes_left >> 9,
|
|
|
GFP_NOFS, 0);
|
|
|
if (!ret)
|
|
|
- *discarded_bytes = len;
|
|
|
+ *discarded_bytes += bytes_left;
|
|
|
}
|
|
|
return ret;
|
|
|
}
|