Whamcloud - gitweb
LU-9771 flr: mirror read and write
[fs/lustre-release.git] / lustre / utils / liblustreapi_mirror.c
1 /*
2  * LGPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the GNU Lesser General Public License
8  * (LGPL) version 2.1 or (at your discretion) any later version.
9  * (LGPL) version 2.1 accompanies this distribution, and is available at
10  * http://www.gnu.org/licenses/lgpl-2.1.html
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * LGPL HEADER END
18  */
19 /*
20  * This file is part of Lustre, http://www.lustre.org/
21  *
22  * lustre/utils/liblustreapi_mirror.c
23  *
24  * Copyright (c) 2017, Intel Corporation.
25  *
26  * Author: Jinshan Xiong <jinshan.xiong@intel.com>
27  */
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <stddef.h>
33 #include <sys/ioctl.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <dirent.h>
38 #include <stdarg.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <sys/xattr.h>
42 #include <assert.h>
43
44 #include <libcfs/util/ioctl.h>
45 #include <lustre/lustreapi.h>
46 #include <linux/lustre/lustre_ioctl.h>
47
48 /**
49  * Set the mirror id for the opening file pointed by @fd, once the mirror
50  * is set successfully, the policy to choose mirrors will be disabed and the
51  * following I/O from this file descriptor will be led to this dedicated
52  * mirror @id.
53  * If @id is zero, it will clear the mirror id setting.
54  *
55  * \param fd    file descriptor, must be opened with O_DIRECT
56  * \param id    mirror id
57  *
58  * \retval      0 on success.
59  * \retval      -errno on failure.
60  */
61 int llapi_mirror_set(int fd, unsigned int id)
62 {
63         struct stat stbuf;
64         int rc;
65
66         rc = ioctl(fd, LL_IOC_FLR_SET_MIRROR, id);
67         if (rc < 0) {
68                 rc = -errno;
69                 return rc;
70         }
71
72         if (!id)
73                 return 0;
74
75         /* in the current implementation, llite doesn't verify if the mirror
76          * id is valid, it has to be verified in an I/O context so the fstat()
77          * call is to verify that the mirror id is correct. */
78         rc = fstat(fd, &stbuf);
79         if (rc < 0) {
80                 rc = -errno;
81
82                 (void) ioctl(fd, LL_IOC_FLR_SET_MIRROR, 0);
83         }
84
85         return rc;
86 }
87
88 /**
89  * Clear mirror id setting.
90  *
91  * \See llapi_mirror_set() for details.
92  */
93 int llapi_mirror_clear(int fd)
94 {
95         return llapi_mirror_set(fd, 0);
96 }
97
98 /**
99  * Read data from a specified mirror with @id. This function won't read
100  * partial read result; either file end is reached, or number of @count bytes
101  * is read, or an error will be returned.
102  *
103  * \param fd    file descriptor, should be opened with O_DIRECT
104  * \param id    mirror id to be read from
105  * \param buf   read buffer
106  * \param count number of bytes to be read
107  * \param pos   file postion where the read starts
108  *
109  * \result >= 0 Number of bytes has been read
110  * \result < 0  The last seen error
111  */
112 ssize_t llapi_mirror_read(int fd, unsigned int id, void *buf, size_t count,
113                           off_t pos)
114 {
115         size_t page_size = sysconf(_SC_PAGESIZE);
116         ssize_t result = 0;
117         int rc;
118
119         rc = llapi_mirror_set(fd, id);
120         if (rc < 0)
121                 return rc;
122
123         while (count > 0) {
124                 ssize_t bytes_read;
125
126                 bytes_read = pread(fd, buf, count, pos);
127                 if (!bytes_read) /* end of file */
128                         break;
129
130                 if (bytes_read < 0) {
131                         result = -errno;
132                         break;
133                 }
134
135                 result += bytes_read;
136                 pos += bytes_read;
137                 buf += bytes_read;
138                 count -= bytes_read;
139
140                 if (bytes_read & (page_size - 1)) /* end of file */
141                         break;
142         }
143
144         (void) llapi_mirror_clear(fd);
145
146         return result;
147 }
148
149 static ssize_t llapi_mirror_write(int fd, unsigned int id,
150                                    const void *buf, size_t count, off_t pos)
151 {
152         size_t page_size = sysconf(_SC_PAGESIZE);
153         ssize_t result = 0;
154         int rc;
155
156         if (((unsigned long)buf & (page_size - 1)) || pos & (page_size - 1))
157                 return -EINVAL;
158
159         rc = llapi_mirror_set(fd, id);
160         if (rc < 0)
161                 return rc;
162
163         while (count > 0) {
164                 ssize_t bytes_written;
165
166                 if (pos & (page_size - 1)) {
167                         result = -EINVAL;
168                         break;
169                 }
170
171                 bytes_written = pwrite(fd, buf, count, pos);
172                 if (bytes_written < 0) {
173                         result = -errno;
174                         break;
175                 }
176
177                 result += bytes_written;
178                 pos += bytes_written;
179                 buf += bytes_written;
180                 count -= bytes_written;
181         }
182
183         (void) llapi_mirror_clear(fd);
184
185         return result;
186 }
187
188 static int llapi_mirror_truncate(int fd, unsigned int id, off_t length)
189 {
190         int rc;
191
192         rc = llapi_mirror_set(fd, id);
193         if (rc < 0)
194                 return rc;
195
196         rc = ftruncate(fd, length);
197
198         (void) llapi_mirror_clear(fd);
199
200         return rc;
201 }
202
203 /**
204  * Copy data contents from source mirror @src to multiple destinations
205  * pointed by @dst. The destination array @dst will be altered to store
206  * successfully copied mirrors.
207  *
208  * \param fd    file descriptor, should be opened with O_DIRECT
209  * \param src   source mirror id, usually a valid mirror
210  * \param dst   an array of destination mirror ids
211  * \param count number of elements in array @dst
212  *
213  * \result > 0  Number of mirrors successfully copied
214  * \result < 0  The last seen error
215  */
216 ssize_t llapi_mirror_copy_many(int fd, unsigned int src, unsigned int *dst,
217                                 size_t count)
218 {
219         const size_t buflen = 4 * 1024 * 1024; /* 4M */
220         void *buf;
221         loff_t pos = 0;
222         size_t page_size = sysconf(_SC_PAGESIZE);
223         ssize_t result = 0;
224         bool eof = false;
225         int nr;
226         int i;
227         int rc;
228
229         if (!count)
230                 return 0;
231
232         rc = posix_memalign(&buf, page_size, buflen);
233         if (rc) /* error code is returned directly */
234                 return -rc;
235
236         nr = count;
237         while (!eof) {
238                 ssize_t bytes_read;
239                 size_t to_write;
240
241                 bytes_read = llapi_mirror_read(fd, src, buf, buflen, pos);
242                 if (!bytes_read) { /* end of file */
243                         break;
244                 } else if (bytes_read < 0) {
245                         result = bytes_read;
246                         nr = 0;
247                         break;
248                 }
249
250                 /* round up to page align to make direct IO happy.
251                  * this implies the last segment to write. */
252                 to_write = (bytes_read + page_size - 1) & ~(page_size - 1);
253
254                 for (i = 0; i < nr; i++) {
255                         ssize_t written;
256
257                         written = llapi_mirror_write(fd, dst[i], buf,
258                                                       to_write, pos);
259                         if (written < 0) {
260                                 result = written;
261
262                                 /* this mirror is not written succesfully,
263                                  * get rid of it from the array */
264                                 dst[i] = dst[--nr];
265                                 i--;
266                                 continue;
267                         }
268
269                         assert(written == to_write);
270                 }
271
272                 pos += bytes_read;
273                 eof = bytes_read < buflen;
274         }
275
276         free(buf);
277
278         if (nr > 0) {
279                 for (i = 0; i < nr; i++) {
280                         rc = llapi_mirror_truncate(fd, dst[i], pos);
281                         if (rc < 0) {
282                                 result = rc;
283
284                                 /* exclude the failed one */
285                                 dst[i] = dst[--nr];
286                                 --i;
287                                 continue;
288                         }
289                 }
290         }
291
292         return nr > 0 ? nr : result;
293 }
294
295 int llapi_mirror_copy(int fd, unsigned int src, unsigned int dst)
296 {
297         ssize_t rc;
298
299         rc = llapi_mirror_copy_many(fd, src, &dst, 1);
300         return rc > 0 ? 0 : rc;
301 }