summaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
authorMario Limonciello <mario.limonciello@dell.com>2018-03-08 02:41:50 -0600
committerSven Eden <yamakuzure@gmx.net>2018-08-24 16:47:08 +0200
commit5a59b1ebb02430ccba5b51cc00075c28fc720d2f (patch)
tree574f60027a1c359bedbe8e1bc9d1294958c62f7c /src/shared
parent91dc346602da427c43b26876831c8690505d2e79 (diff)
sleep: Add support for setting a disk offset when hibernating
The Linux kernel is adding support for configuring the offset into a disk. This allows swapfiles to be more usable as users will no longer need to set the offset on their kernel command line. Use this API in systemd when hibernating as well. Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/sleep-config.c116
-rw-r--r--src/shared/sleep-config.h7
2 files changed, 110 insertions, 13 deletions
diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c
index a9167fc29..1086e689f 100644
--- a/src/shared/sleep-config.c
+++ b/src/shared/sleep-config.c
@@ -20,6 +20,7 @@
***/
//#include <errno.h>
+//#include <linux/fs.h>
//#include <stdbool.h>
//#include <stddef.h>
//#include <stdio.h>
@@ -193,13 +194,10 @@ int can_sleep_disk(char **types) {
#define HIBERNATION_SWAP_THRESHOLD 0.98
-static int hibernation_partition_size(size_t *size, size_t *used) {
+int find_hibernate_location(char **device, char **type, size_t *size, size_t *used) {
_cleanup_fclose_ FILE *f;
unsigned i;
- assert(size);
- assert(used);
-
f = fopen("/proc/swaps", "re");
if (!f) {
log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
@@ -211,7 +209,7 @@ static int hibernation_partition_size(size_t *size, size_t *used) {
(void) fscanf(f, "%*s %*s %*s %*s %*s\n");
for (i = 1;; i++) {
- _cleanup_free_ char *dev = NULL, *type = NULL;
+ _cleanup_free_ char *dev_field = NULL, *type_field = NULL;
size_t size_field, used_field;
int k;
@@ -221,7 +219,7 @@ static int hibernation_partition_size(size_t *size, size_t *used) {
"%zu " /* swap size */
"%zu " /* used */
"%*i\n", /* priority */
- &dev, &type, &size_field, &used_field);
+ &dev_field, &type_field, &size_field, &used_field);
if (k != 4) {
if (k == EOF)
break;
@@ -230,13 +228,18 @@ static int hibernation_partition_size(size_t *size, size_t *used) {
continue;
}
- if (streq(type, "partition") && endswith(dev, "\\040(deleted)")) {
- log_warning("Ignoring deleted swapfile '%s'.", dev);
+ if (streq(type_field, "partition") && endswith(dev_field, "\\040(deleted)")) {
+ log_warning("Ignoring deleted swapfile '%s'.", dev_field);
continue;
}
-
- *size = size_field;
- *used = used_field;
+ if (device)
+ *device = TAKE_PTR(dev_field);
+ if (type)
+ *type = TAKE_PTR(type_field);
+ if (size)
+ *size = size_field;
+ if (used)
+ *used = used_field;
return 0;
}
@@ -255,7 +258,7 @@ static bool enough_memory_for_hibernation(void) {
return true;
#endif // 0
- r = hibernation_partition_size(&size, &used);
+ r = find_hibernate_location(NULL, NULL, &size, &used);
if (r < 0)
return false;
@@ -279,6 +282,95 @@ static bool enough_memory_for_hibernation(void) {
return r;
}
+int read_fiemap(int fd, struct fiemap **ret) {
+ _cleanup_free_ struct fiemap *fiemap = NULL, *result_fiemap = NULL;
+ int extents_size;
+ struct stat statinfo;
+ uint32_t result_extents = 0;
+ uint64_t fiemap_start = 0, fiemap_length;
+ size_t fiemap_size = 1, result_fiemap_size = 1;
+
+ if (fstat(fd, &statinfo) < 0)
+ return log_debug_errno(errno, "Cannot determine file size: %m");
+ if (!S_ISREG(statinfo.st_mode))
+ return -ENOTTY;
+ fiemap_length = statinfo.st_size;
+
+ /* zero this out in case we run on a file with no extents */
+ fiemap = new0(struct fiemap, 1);
+ if (!fiemap)
+ return -ENOMEM;
+
+ result_fiemap = new(struct fiemap, 1);
+ if (!result_fiemap)
+ return -ENOMEM;
+
+ /* XFS filesystem has incorrect implementation of fiemap ioctl and
+ * returns extents for only one block-group at a time, so we need
+ * to handle it manually, starting the next fiemap call from the end
+ * of the last extent
+ */
+ while (fiemap_start < fiemap_length) {
+ *fiemap = (struct fiemap) {
+ .fm_start = fiemap_start,
+ .fm_length = fiemap_length,
+ .fm_flags = FIEMAP_FLAG_SYNC,
+ };
+
+ /* Find out how many extents there are */
+ if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0)
+ return log_debug_errno(errno, "Failed to read extents: %m");
+
+ /* Nothing to process */
+ if (fiemap->fm_mapped_extents == 0)
+ break;
+
+ /* Result fiemap has to hold all the extents for the whole file */
+ extents_size = DIV_ROUND_UP(sizeof(struct fiemap_extent) * fiemap->fm_mapped_extents,
+ sizeof(struct fiemap));
+
+ /* Resize fiemap to allow us to read in the extents */
+ if (!GREEDY_REALLOC0(fiemap, fiemap_size, extents_size))
+ return -ENOMEM;
+
+ fiemap->fm_extent_count = fiemap->fm_mapped_extents;
+ fiemap->fm_mapped_extents = 0;
+
+ if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0)
+ return log_debug_errno(errno, "Failed to read extents: %m");
+
+ extents_size = DIV_ROUND_UP(sizeof(struct fiemap_extent) * (result_extents + fiemap->fm_mapped_extents),
+ sizeof(struct fiemap));
+
+ /* Resize result_fiemap to allow us to read in the extents */
+ if (!GREEDY_REALLOC(result_fiemap, result_fiemap_size,
+ extents_size))
+ return -ENOMEM;
+
+ memcpy(result_fiemap->fm_extents + result_extents,
+ fiemap->fm_extents,
+ sizeof(struct fiemap_extent) * fiemap->fm_mapped_extents);
+
+ result_extents += fiemap->fm_mapped_extents;
+
+ /* Highly unlikely that it is zero */
+ if (fiemap->fm_mapped_extents > 0) {
+ uint32_t i = fiemap->fm_mapped_extents - 1;
+
+ fiemap_start = fiemap->fm_extents[i].fe_logical +
+ fiemap->fm_extents[i].fe_length;
+
+ if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST)
+ break;
+ }
+ }
+
+ memcpy(result_fiemap, fiemap, sizeof(struct fiemap));
+ result_fiemap->fm_mapped_extents = result_extents;
+ *ret = TAKE_PTR(result_fiemap);
+ return 0;
+}
+
#if 0 /// elogind has to do, or better, *can* do it differently
static bool can_s2h(void) {
int r;
diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h
index b06777291..767a69927 100644
--- a/src/shared/sleep-config.h
+++ b/src/shared/sleep-config.h
@@ -21,7 +21,12 @@
***/
#if 0 /// UNNEEDED by elogind
-int parse_sleep_config(const char *verb, char ***modes, char ***states);
+//#include <linux/fiemap.h>
+//#include "time-util.h"
+
+int read_fiemap(int fd, struct fiemap **ret);
+int parse_sleep_config(const char *verb, char ***modes, char ***states, usec_t *delay);
+int find_hibernate_location(char **device, char **type, size_t *size, size_t *used);
int can_sleep(const char *verb);
int can_sleep_disk(char **types);