summaryrefslogtreecommitdiff
blob: b67cb712ad517f562bdbcf7f728d69b6c0e40821 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
From c26a519da1ed182e7cfd67e7a353932dda53d811 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jean-Pierre=20Andr=C3=A9?= <jpandre@users.sourceforge.net>
Date: Mon, 4 Aug 2014 17:39:50 +0200
Subject: [PATCH] Fixed fstrim(8) applied to partitions

The new way goes via /sys/dev/block/MAJOR:MINOR to map partitions to
devices and get discard parameters of the parent device. It also ensures
that the partition is aligned to the discard block size.

Contributed by Richard W.M. Jones
---
 libntfs-3g/ioctl.c | 140 ++++++++++++++++++++++++++---------------------------
 1 file changed, 68 insertions(+), 72 deletions(-)

diff --git a/libntfs-3g/ioctl.c b/libntfs-3g/ioctl.c
index bbbceb9..eb7c8e7 100644
--- a/libntfs-3g/ioctl.c
+++ b/libntfs-3g/ioctl.c
@@ -66,8 +66,6 @@
 #include <linux/fs.h>
 #endif
 
-#include <dirent.h>
-
 #include "compat.h"
 #include "debug.h"
 #include "bitmap.h"
@@ -135,17 +133,14 @@ static int read_u64(const char *path, u64 *n)
 }
 
 /* Find discard limits for current backing device.
- * XXX Kernel makes this a pain in the neck.
  */
-static int fstrim_limits(ntfs_volume *vol, u64 *discard_granularity,
+static int fstrim_limits(ntfs_volume *vol,
+			u64 *discard_alignment,
+			u64 *discard_granularity,
 			u64 *discard_max_bytes)
 {
 	struct stat statbuf;
-	DIR *dir;
-	struct dirent *d;
-	char path[80];
-	char line[64];
-	char dev[64];
+	char path1[80], path2[80];
 	int ret;
 
 	/* Stat the backing device.  Caller has ensured it is a block device. */
@@ -155,82 +150,78 @@ static int fstrim_limits(ntfs_volume *vol, u64 *discard_granularity,
 		return -errno;
 	}
 
-	/* Now look for a /sys/block/<dev>/dev file which contains
-	 * "major:minor\n".
+	/* For whole devices,
+	 * /sys/dev/block/MAJOR:MINOR/discard_alignment
+	 * /sys/dev/block/MAJOR:MINOR/queue/discard_granularity
+	 * /sys/dev/block/MAJOR:MINOR/queue/discard_max_bytes
+	 * will exist.
+	 * For partitions, we also need to check the parent device:
+	 * /sys/dev/block/MAJOR:MINOR/../queue/discard_granularity
+	 * /sys/dev/block/MAJOR:MINOR/../queue/discard_max_bytes
 	 */
-	snprintf(dev, sizeof dev, "%d:%d\n",
+	snprintf(path1, sizeof path1, "/sys/dev/block/%d:%d",
 		major(statbuf.st_rdev), minor(statbuf.st_rdev));
 
-	dir = opendir("/sys/block");
-	if (dir == NULL) {
-		ntfs_log_debug("fstrim_limits: could not open /sys/block\n");
-		return -errno;
+	snprintf(path2, sizeof path2, "%s/discard_alignment", path1);
+	ret = read_u64(path2, discard_alignment);
+	if (ret) {
+		if (ret != -ENOENT)
+			return ret;
+		else
+			/* We would expect this file to exist on all
+			 * modern kernels.  But for the sake of very
+			 * old kernels:
+			 */
+			goto not_found;
 	}
-	for (;;) {
-		errno = 0;
-		d = readdir(dir);
-		if (!d) break;
 
-		snprintf(path, sizeof path, "/sys/block/%s/dev", d->d_name);
-		ret = read_line(path, line, sizeof line);
-		if (ret)
-			continue;
-		if (strcmp(line, dev) == 0)
-			goto found;
+	snprintf(path2, sizeof path2, "%s/queue/discard_granularity", path1);
+	ret = read_u64(path2, discard_granularity);
+	if (ret) {
+		if (ret != -ENOENT)
+			return ret;
+		else {
+			snprintf(path2, sizeof path2,
+				"%s/../queue/discard_granularity", path1);
+			ret = read_u64(path2, discard_granularity);
+			if (ret) {
+				if (ret != -ENOENT)
+					return ret;
+				else
+					goto not_found;
+			}
+		}
 	}
 
-	/* Check readdir didn't fail. */
-	if (errno != 0) {
-		ret = -errno;
-		ntfs_log_debug("fstrim_limits: readdir failed\n");
-		goto out;
+	snprintf(path2, sizeof path2, "%s/queue/discard_max_bytes", path1);
+	ret = read_u64(path2, discard_max_bytes);
+	if (ret) {
+		if (ret != -ENOENT)
+			return ret;
+		else {
+			snprintf(path2, sizeof path2,
+				"%s/../queue/discard_max_bytes", path1);
+			ret = read_u64(path2, discard_max_bytes);
+			if (ret) {
+				if (ret != -ENOENT)
+					return ret;
+				else
+					goto not_found;
+			}
+		}
 	}
 
+	return 0;
+
+not_found:
 	/* If we reach here then we didn't find the device.  This is
 	 * not an error, but set discard_max_bytes = 0 to indicate
 	 * that discard is not available.
 	 */
+	*discard_alignment = 0;
 	*discard_granularity = 0;
 	*discard_max_bytes = 0;
-	ntfs_log_debug("fstrim_limits: /sys/block entry corresponding to device %s not found\n",
-		vol->dev->d_name);
-	ret = 0;
-	goto out;
-
-found:
-	/* Found the device at /sys/block/ + d->d_name */
-	snprintf (path, sizeof path,
-		"/sys/block/%s/queue/discard_granularity",
-		d->d_name);
-	ret = read_u64(path, discard_granularity);
-	if (ret) {
-		ntfs_log_debug("fstrim_limits: could not read %s\n", path);
-		goto out;
-	}
-
-	snprintf (path, sizeof path,
-		"/sys/block/%s/queue/discard_max_bytes",
-		d->d_name);
-	ret = read_u64(path, discard_max_bytes);
-	if (ret) {
-		ntfs_log_debug("fstrim_limits: could not read %s\n", path);
-		goto out;
-	}
-
-	ntfs_log_debug("fstrim_limits: device %s discard granularity = %llu max_bytes = %llu\n",
-		d->d_name,
-		(unsigned long long) *discard_granularity,
-		(unsigned long long) *discard_max_bytes);
-
-	ret = 0;
-out:
-	if (closedir (dir) == -1) {
-		ret = -errno;
-		ntfs_log_debug("fstrim_limits: closedir failed\n");
-		return ret;
-	}
-
-	return ret;
+	return 0;
 }
 
 #define FSTRIM_BUFSIZ 4096
@@ -247,7 +238,7 @@ static int fstrim(ntfs_volume *vol, void *data)
 	u64 start = range->start;
 	u64 len = range->len;
 	u64 minlen = range->minlen;
-	u64 discard_granularity, discard_max_bytes;
+	u64 discard_alignment, discard_granularity, discard_max_bytes;
 	u8 *buf = NULL;
 	LCN start_buf;
 	int ret;
@@ -279,9 +270,14 @@ static int fstrim(ntfs_volume *vol, void *data)
 		return -EOPNOTSUPP;
 	}
 
-	ret = fstrim_limits(vol, &discard_granularity, &discard_max_bytes);
+	ret = fstrim_limits(vol, &discard_alignment,
+			&discard_granularity, &discard_max_bytes);
 	if (ret)
 		return ret;
+	if (discard_alignment != 0) {
+		ntfs_log_debug("fstrim: backing device is not aligned for discards\n");
+		return -EOPNOTSUPP;
+	}
 	if (discard_granularity > vol->cluster_size) {
 		ntfs_log_debug("fstrim: discard granularity of backing device is larger than cluster size\n");
 		return -EOPNOTSUPP;
-- 
1.9.3