s390utils/SOURCES/s390-tools-zipl-fiemap.patch

107 lines
3.2 KiB
Diff

From 0318dfbc726a82ce1e9309e55186f3c4faae72f1 Mon Sep 17 00:00:00 2001
From: Eric Sandeen <sandeen@sandeen.net>
Date: Wed, 12 Sep 2018 09:40:22 -0500
Subject: [PATCH] zipl: use FIEMAP mapping ioctl if it exists
zipl currently uses the FIBMAP ioctl to map blocks for the bootloader;
on XFS, if FIBMAP is requested on a reflinked file, it will fail -
and FIBMAP returns 0 in this case, which is indistinguishable from a
hole. This causes boot to fail because the file is not mapped.
We can use the FIEMAP ioctl instead, which is able to map reflinked
files. While FIEMAP is able to map entire extents at once, here we
simply use it to obtain the mapping block-by-block so that it fits
in with the current FIBMAP calls.
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
zipl/src/disk.c | 57 +++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 53 insertions(+), 4 deletions(-)
diff --git a/zipl/src/disk.c b/zipl/src/disk.c
index 0d8e7796..43092bf1 100644
--- a/zipl/src/disk.c
+++ b/zipl/src/disk.c
@@ -21,6 +21,8 @@
#include <sys/sysmacros.h>
#include <sys/vfs.h>
#include <unistd.h>
+#include <linux/fs.h>
+#include <linux/fiemap.h>
#include "lib/util_proc.h"
@@ -550,8 +552,12 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
{
struct statfs buf;
blocknum_t phy_per_fs;
- int mapped;
+ blocknum_t mapped;
+ int block;
int subblock;
+ int fiemap_size;
+ int map_offset;
+ struct fiemap *fiemap;
/* No file system: partition or raw disk */
if (info->fs_block_size == -1) {
@@ -576,12 +582,55 @@ disk_get_blocknum(int fd, int fd_is_basedisk, blocknum_t logical,
}
/* Get mapping in file system blocks */
phy_per_fs = info->fs_block_size / info->phy_block_size;
- mapped = logical / phy_per_fs;
subblock = logical % phy_per_fs;
- if (ioctl(fd, FIBMAP, &mapped)) {
- error_reason("Could not get file mapping");
+
+ /* First try FIEMAP, more complicated to set up */
+ fiemap_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent);
+
+ fiemap = misc_malloc(fiemap_size);
+ if (!fiemap)
return -1;
+ memset(fiemap, 0, fiemap_size);
+
+ fiemap->fm_extent_count = 1;
+ fiemap->fm_flags = FIEMAP_FLAG_SYNC;
+ /* fm_start, fm_length in bytes; logical is in physical block units */
+ fiemap->fm_start = logical * info->phy_block_size;
+ fiemap->fm_length = info->phy_block_size;
+
+ if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap)) {
+ /* FIEMAP failed, fall back to FIBMAP */
+ block = logical / phy_per_fs;
+ if (ioctl(fd, FIBMAP, &block)) {
+ error_reason("Could not get file mapping");
+ free(fiemap);
+ return -1;
+ }
+ mapped = block;
+ } else {
+ if (fiemap->fm_mapped_extents) {
+ if (fiemap->fm_extents[0].fe_flags &
+ FIEMAP_EXTENT_ENCODED) {
+ error_reason("File mapping is encoded");
+ free(fiemap);
+ return -1;
+ }
+ /*
+ * returned extent may start prior to our request
+ */
+ map_offset = fiemap->fm_start -
+ fiemap->fm_extents[0].fe_logical;
+ mapped = fiemap->fm_extents[0].fe_physical +
+ map_offset;
+ /* set mapped to fs block units */
+ mapped = mapped / info->fs_block_size;
+ } else {
+ mapped = 0;
+ }
}
+
+ free(fiemap);
+
if (mapped == 0) {
/* This is a hole in the file */
*physical = 0;