Whamcloud - gitweb
LU-10092 pcc: Non-blocking PCC caching
[fs/lustre-release.git] / lustre / utils / liblustreapi_pcc.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  * Copyright (c) 2017, DDN Storage Corporation.
21  */
22 /*
23  * This file is part of Lustre, http://www.lustre.org/
24  */
25 /*
26  *
27  * lustreapi library for Persistent Client Cache.
28  *
29  * Author: Li Xi <lixi@ddn.com>
30  * Author: Qian Yingjin <qian@ddn.com>
31  */
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <lustre/lustreapi.h>
36 #include <linux/lustre/lustre_user.h>
37 #include <linux/lustre/lustre_fid.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <sys/ioctl.h>
42 #include "lustreapi_internal.h"
43
44 /**
45  * Fetch and attach a file to readwrite PCC.
46  *
47  */
48 static int llapi_readwrite_pcc_attach_fd(int fd, __u32 archive_id)
49 {
50         int rc;
51         struct ll_ioc_lease *data;
52
53         rc = llapi_lease_acquire(fd, LL_LEASE_WRLCK);
54         if (rc < 0) {
55                 llapi_error(LLAPI_MSG_ERROR, rc, "cannot get lease");
56                 return rc;
57         }
58
59         data = malloc(offsetof(typeof(*data), lil_ids[1]));
60         if (!data) {
61                 rc = -ENOMEM;
62                 llapi_err_noerrno(LLAPI_MSG_ERROR,
63                                   "failed to allocate memory");
64                 return rc;
65         }
66
67         data->lil_mode = LL_LEASE_UNLCK;
68         data->lil_flags = LL_LEASE_PCC_ATTACH;
69         data->lil_count = 1;
70         data->lil_ids[0] = archive_id;
71         rc = llapi_lease_set(fd, data);
72         if (rc <= 0) {
73                 if (rc == 0) /* lost lease lock */
74                         rc = -EBUSY;
75                 llapi_error(LLAPI_MSG_ERROR, rc,
76                             "cannot attach with ID: %u", archive_id);
77         } else {
78                 rc = 0;
79         }
80
81         free(data);
82         return rc;
83 }
84
85 static int llapi_readwrite_pcc_attach(const char *path, __u32 archive_id)
86 {
87         int fd;
88         int rc;
89
90         fd = open(path, O_RDWR | O_NONBLOCK);
91         if (fd < 0) {
92                 rc = -errno;
93                 llapi_error(LLAPI_MSG_ERROR, rc, "cannot open '%s'",
94                             path);
95                 return rc;
96         }
97
98         rc = llapi_readwrite_pcc_attach_fd(fd, archive_id);
99
100         close(fd);
101         return rc;
102 }
103
104 int llapi_pcc_attach(const char *path, __u32 id, enum lu_pcc_type type)
105 {
106         int rc;
107
108         switch (type) {
109         case LU_PCC_READWRITE:
110                 rc = llapi_readwrite_pcc_attach(path, id);
111                 break;
112         default:
113                 rc = -EINVAL;
114                 break;
115         }
116         return rc;
117 }
118
119 static int llapi_readwrite_pcc_attach_fid(const char *mntpath,
120                                           const struct lu_fid *fid,
121                                           __u32 id)
122 {
123         int rc;
124         int fd;
125
126         fd = llapi_open_by_fid(mntpath, fid, O_RDWR | O_NONBLOCK);
127         if (fd < 0) {
128                 rc = -errno;
129                 llapi_error(LLAPI_MSG_ERROR, rc,
130                             "llapi_open_by_fid for " DFID "failed",
131                             PFID(fid));
132                 return rc;
133         }
134
135         rc = llapi_readwrite_pcc_attach_fd(fd, id);
136
137         close(fd);
138         return rc;
139 }
140
141 int llapi_pcc_attach_fid(const char *mntpath, const struct lu_fid *fid,
142                          __u32 id, enum lu_pcc_type type)
143 {
144         int rc;
145
146         switch (type) {
147         case LU_PCC_READWRITE:
148                 rc = llapi_readwrite_pcc_attach_fid(mntpath, fid, id);
149                 break;
150         default:
151                 rc = -EINVAL;
152                 break;
153         }
154         return rc;
155 }
156
157
158 int llapi_pcc_attach_fid_str(const char *mntpath, const char *fidstr,
159                              __u32 id, enum lu_pcc_type type)
160 {
161         int rc;
162         struct lu_fid fid;
163         const char *fidstr_orig = fidstr;
164
165         while (*fidstr == '[')
166                 fidstr++;
167         rc = sscanf(fidstr, SFID, RFID(&fid));
168         if (rc != 3) {
169                 llapi_err_noerrno(LLAPI_MSG_ERROR,
170                                   "bad FID format '%s', should be [seq:oid:ver]"
171                                   " (e.g. "DFID")\n", fidstr_orig,
172                                   (unsigned long long)FID_SEQ_NORMAL, 2, 0);
173                 return -EINVAL;
174         }
175
176         rc = llapi_pcc_attach_fid(mntpath, &fid, id, type);
177
178         return rc;
179 }
180
181 /**
182  * detach PCC cache of a file by using fd.
183  *
184  * \param fd            File handle.
185  *
186  * \return 0 on success, an error code otherwise.
187  */
188 int llapi_pcc_detach_fd(int fd)
189 {
190         int rc;
191
192         rc = ioctl(fd, LL_IOC_PCC_DETACH);
193         return rc;
194 }
195
196 /**
197  * detach PCC cache of a file via FID.
198  *
199  * \param mntpath       Fullpath to the client mount point.
200  * \param fid           FID of the file.
201  *
202  * \return 0 on success, an error code otherwise.
203  */
204 int llapi_pcc_detach_fid(const char *mntpath, const struct lu_fid *fid)
205 {
206         int rc;
207         int fd;
208         struct lu_pcc_detach detach;
209
210         rc = get_root_path(WANT_FD, NULL, &fd, (char *)mntpath, -1);
211         if (rc) {
212                 llapi_error(LLAPI_MSG_ERROR, rc, "cannot get root path: %s",
213                             mntpath);
214                 return rc;
215         }
216
217         /*
218          * PCC prefetching algorithm scans Lustre OPEN/CLOSE changelogs
219          * to determine the candidate files needing to prefetch into
220          * PCC. To avoid generattion of unnecessary open/close changelogs,
221          * we implement a new dir ioctl LL_IOC_PCC_DETACH_BY_FID to detach
222          * files.
223          */
224         detach.pccd_fid = *fid;
225         rc = ioctl(fd, LL_IOC_PCC_DETACH_BY_FID, &detach);
226         close(fd);
227         return rc;
228 }
229
230 /**
231  * detach PCC cache of a file via FID.
232  *
233  * \param mntpath       Fullpath to the client mount point.
234  * \param fid           FID string of the file.
235  *
236  * \return 0 on success, an error code otherwise.
237  */
238 int llapi_pcc_detach_fid_str(const char *mntpath, const char *fidstr)
239 {
240         int rc;
241         struct lu_fid fid;
242         const char *fidstr_orig = fidstr;
243
244         while (*fidstr == '[')
245                 fidstr++;
246         rc = sscanf(fidstr, SFID, RFID(&fid));
247         if (rc != 3 || !fid_is_sane(&fid)) {
248                 llapi_err_noerrno(LLAPI_MSG_ERROR,
249                                   "bad FID format '%s', should be [seq:oid:ver]"
250                                   " (e.g. "DFID")\n", fidstr_orig,
251                                   (unsigned long long)FID_SEQ_NORMAL, 2, 0);
252                 return -EINVAL;
253         }
254
255         rc = llapi_pcc_detach_fid(mntpath, &fid);
256
257         return rc;
258 }
259
260 /**
261  * detach PCC cache of a file.
262  *
263  * \param path    Fullpath to the file to operate on.
264  *
265  * \return 0 on success, an error code otherwise.
266  */
267 int llapi_pcc_detach_file(const char *path)
268 {
269         int rc;
270         int fd;
271
272         fd = open(path, O_RDWR | O_NONBLOCK);
273         if (fd < 0) {
274                 rc = -errno;
275                 llapi_error(LLAPI_MSG_ERROR, rc, "cannot open '%s'",
276                             path);
277                 return rc;
278         }
279
280         rc = llapi_pcc_detach_fd(fd);
281         close(fd);
282         return rc;
283 }
284
285 /**
286  * Return the current PCC state related to a file.
287  *
288  * \param fd    File handle.
289  * \param state PCC state info.
290  *
291  * \return 0 on success, an error code otherwise.
292  */
293 int llapi_pcc_state_get_fd(int fd, struct lu_pcc_state *state)
294 {
295         int rc;
296
297         rc = ioctl(fd, LL_IOC_PCC_STATE, state);
298         /* If error, save errno value */
299         rc = rc ? -errno : 0;
300
301         return rc;
302 }
303
304 /**
305  * Return the current PCC state related to file pointed by a path.
306  *
307  * see llapi_pcc_state_get_fd() for args use and return
308  */
309 int llapi_pcc_state_get(const char *path, struct lu_pcc_state *state)
310 {
311         int fd;
312         int rc;
313
314         fd = open(path, O_RDONLY | O_NONBLOCK);
315         if (fd < 0)
316                 return -errno;
317
318         rc = llapi_pcc_state_get_fd(fd, state);
319
320         close(fd);
321         return rc;
322 }
323
324 /**
325  * Add/delete a PCC backend on a client.
326  */
327 int llapi_pccdev_set(const char *mntpath, const char *cmd)
328 {
329         char buf[sizeof(struct obd_uuid)];
330         glob_t path;
331         ssize_t count;
332         int fd;
333         int rc;
334
335         rc = llapi_getname(mntpath, buf, sizeof(buf));
336         if (rc < 0) {
337                 llapi_error(LLAPI_MSG_ERROR, rc,
338                             "cannot get name for '%s'\n", mntpath);
339                 return rc;
340         }
341
342         rc = cfs_get_param_paths(&path, "llite/%s/pcc", buf);
343         if (rc != 0)
344                 return -errno;
345
346         fd = open(path.gl_pathv[0], O_WRONLY);
347         if (fd < 0) {
348                 rc = -errno;
349                 llapi_error(LLAPI_MSG_ERROR, rc, "error opening %s",
350                             path.gl_pathv[0]);
351                 goto out;
352         }
353
354         count = write(fd, cmd, strlen(cmd));
355         if (count < 0) {
356                 rc = errno;
357                 if (errno != EIO)
358                         llapi_error(LLAPI_MSG_ERROR, rc,
359                                     "error: setting llite.%s.pcc=\"%s\"\n",
360                                     buf, cmd);
361         } else if (count < strlen(cmd)) { /* Truncate case */
362                 rc = -EINVAL;
363                 llapi_error(LLAPI_MSG_ERROR, rc,
364                             "setting llite.%s.pcc=\"%s\": wrote only %zd\n",
365                             buf, cmd, count);
366         }
367         close(fd);
368 out:
369         cfs_free_param_data(&path);
370         return rc;
371 }
372
373 /**
374  * List all PCC backend devices on a client.
375  */
376 int llapi_pccdev_get(const char *mntpath)
377 {
378         long page_size = sysconf(_SC_PAGESIZE);
379         char pathbuf[sizeof(struct obd_uuid)];
380         glob_t path;
381         char *buf;
382         int fd;
383         int rc;
384
385         rc = llapi_getname(mntpath, pathbuf, sizeof(pathbuf));
386         if (rc < 0) {
387                 llapi_error(LLAPI_MSG_ERROR, rc,
388                             "cannot get name for '%s'\n", mntpath);
389                 return rc;
390         }
391
392         rc = cfs_get_param_paths(&path, "llite/%s/pcc", pathbuf);
393         if (rc != 0)
394                 return -errno;
395
396         /* Read the contents of file to stdout */
397         fd = open(path.gl_pathv[0], O_RDONLY);
398         if (fd < 0) {
399                 rc = -errno;
400                 llapi_error(LLAPI_MSG_ERROR, rc,
401                             "error: pccdev_get: opening '%s'\n",
402                             path.gl_pathv[0]);
403                 goto out_free_param;
404         }
405
406         buf = calloc(1, page_size);
407         if (buf == NULL) {
408                 rc = -ENOMEM;
409                 llapi_error(LLAPI_MSG_ERROR, rc,
410                             "error: pccdev_get: allocating '%s' buffer\n",
411                             path.gl_pathv[0]);
412                 goto out_close;
413         }
414
415         while (1) {
416                 ssize_t count = read(fd, buf, page_size);
417
418                 if (count == 0)
419                         break;
420                 if (count < 0) {
421                         rc = -errno;
422                         if (errno != EIO) {
423                                 llapi_error(LLAPI_MSG_ERROR, rc,
424                                             "error: pccdev_get: "
425                                             "reading failed\n");
426                         }
427                         break;
428                 }
429
430                 if (fwrite(buf, 1, count, stdout) != count) {
431                         rc = -errno;
432                         llapi_error(LLAPI_MSG_ERROR, rc,
433                                     "error: get_param: write to stdout\n");
434                         break;
435                 }
436         }
437 out_close:
438         close(fd);
439         free(buf);
440 out_free_param:
441         cfs_free_param_data(&path);
442         return rc;
443 }