Whamcloud - gitweb
LU-17744 ldiskfs: mballoc stats fixes
[fs/lustre-release.git] / lustre / mdt / mdt_hsm.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License version 2 for more details.  A copy is
14  * included in the COPYING file that accompanied this code.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2011, 2012 Commissariat a l'energie atomique et aux energies
24  *                          alternatives
25  * Use is subject to license terms.
26  *
27  * Copyright (c) 2012, 2015, Intel Corporation.
28  */
29 /*
30  * lustre/mdt/mdt_hsm.c
31  *
32  * Lustre Metadata Target (mdt) request handler
33  *
34  * Author: Aurelien Degremont <aurelien.degremont@cea.fr>
35  * Author: JC Lafoucriere <jacques-charles.lafoucriere@cea.fr>
36  */
37
38 #define DEBUG_SUBSYSTEM S_MDS
39
40 #include <lustre_errno.h>
41 #include "mdt_internal.h"
42
43 /* Max allocation to satisfy single HSM RPC. */
44 #define MDT_HSM_ALLOC_MAX (1 << 20)
45
46 #define MDT_HSM_ALLOC(ptr, size)                        \
47         do {                                            \
48                 if ((size) <= MDT_HSM_ALLOC_MAX)        \
49                         OBD_ALLOC_LARGE((ptr), (size)); \
50                 else                                    \
51                         (ptr) = NULL;                   \
52         } while (0)
53
54 #define MDT_HSM_FREE(ptr, size) OBD_FREE_LARGE((ptr), (size))
55
56 /**
57  * Update on-disk HSM attributes.
58  */
59 int mdt_hsm_attr_set(struct mdt_thread_info *info, struct mdt_object *obj,
60                      const struct md_hsm *mh)
61 {
62         struct md_object *next = mdt_object_child(obj);
63         struct lu_buf *buf = &info->mti_buf;
64         struct hsm_attrs *attrs;
65         int rc;
66         ENTRY;
67
68         attrs = (struct hsm_attrs *)info->mti_xattr_buf;
69         BUILD_BUG_ON(sizeof(info->mti_xattr_buf) < sizeof(*attrs));
70
71         /* pack HSM attributes */
72         lustre_hsm2buf(info->mti_xattr_buf, mh);
73
74         /* update HSM attributes */
75         buf->lb_buf = attrs;
76         buf->lb_len = sizeof(*attrs);
77         rc = mo_xattr_set(info->mti_env, next, buf, XATTR_NAME_HSM, 0);
78
79         RETURN(rc);
80 }
81
82 static inline bool mdt_hsm_is_admin(struct mdt_thread_info *info)
83 {
84         bool is_admin;
85         int rc;
86
87         if (info->mti_body == NULL)
88                 return false;
89
90         rc = mdt_init_ucred(info, (struct mdt_body *)info->mti_body);
91         if (rc < 0)
92                 return false;
93
94         is_admin = cap_raised(mdt_ucred(info)->uc_cap, CAP_SYS_ADMIN);
95
96         mdt_exit_ucred(info);
97
98         return is_admin;
99 }
100
101 /**
102  * Extract information coming from a copytool and asks coordinator to update
103  * a request status depending on the update content.
104  *
105  * Copytools could use this to report failure in their process.
106  *
107  * This is HSM_PROGRESS RPC handler.
108  */
109 int mdt_hsm_progress(struct tgt_session_info *tsi)
110 {
111         struct mdt_thread_info          *info;
112         struct hsm_progress_kernel      *hpk;
113         int                              rc;
114         ENTRY;
115
116         if (tsi->tsi_mdt_body == NULL)
117                 RETURN(-EPROTO);
118
119         hpk = req_capsule_client_get(tsi->tsi_pill, &RMF_MDS_HSM_PROGRESS);
120         if (hpk == NULL)
121                 RETURN(err_serious(-EPROTO));
122
123         hpk->hpk_errval = lustre_errno_ntoh(hpk->hpk_errval);
124
125         CDEBUG(D_HSM, "Progress on "DFID": len=%llu : rc = %d\n",
126                PFID(&hpk->hpk_fid), hpk->hpk_extent.length, hpk->hpk_errval);
127
128         if (hpk->hpk_errval)
129                 CDEBUG(D_HSM, "Copytool progress on "DFID" failed : rc = %d; %s.\n",
130                        PFID(&hpk->hpk_fid), hpk->hpk_errval,
131                        hpk->hpk_flags & HP_FLAG_RETRY ? "will retry" : "fatal");
132
133         if (hpk->hpk_flags & HP_FLAG_COMPLETED)
134                 CDEBUG(D_HSM, "Finished "DFID" : rc = %d; cancel cookie=%#llx\n",
135                        PFID(&hpk->hpk_fid), hpk->hpk_errval, hpk->hpk_cookie);
136
137         info = tsi2mdt_info(tsi);
138         if (!mdt_hsm_is_admin(info))
139                 GOTO(out, rc = -EPERM);
140
141         rc = mdt_hsm_update_request_state(info, hpk);
142 out:
143         mdt_thread_info_fini(info);
144         RETURN(rc);
145 }
146
147 int mdt_hsm_ct_register(struct tgt_session_info *tsi)
148 {
149         struct mdt_thread_info *info = tsi2mdt_info(tsi);
150         struct ptlrpc_request *req = mdt_info_req(info);
151         struct obd_export *exp = req->rq_export;
152         size_t archives_size;
153         __u32 *archives;
154         int archive_count;
155         int rc;
156         ENTRY;
157
158         if (!mdt_hsm_is_admin(info))
159                 GOTO(out, rc = -EPERM);
160
161         archives = req_capsule_client_get(tsi->tsi_pill, &RMF_MDS_HSM_ARCHIVE);
162         if (archives == NULL)
163                 GOTO(out, rc = err_serious(-EPROTO));
164
165         archives_size = req_capsule_get_size(tsi->tsi_pill,
166                                              &RMF_MDS_HSM_ARCHIVE, RCL_CLIENT);
167
168         /* compatibility check for the old clients */
169         if (!exp_connect_archive_id_array(exp)) {
170                 if (archives_size != sizeof(*archives))
171                         GOTO(out, rc = err_serious(-EPROTO));
172
173                 /* XXX: directly include this function here? */
174                 rc = mdt_hsm_agent_register_mask(info,
175                                                  &tsi->tsi_exp->exp_client_uuid,
176                                                  *archives);
177                 GOTO(out, rc);
178         }
179
180         if (archives_size % sizeof(*archives) != 0)
181                 GOTO(out, rc = err_serious(-EPROTO));
182
183         archive_count = archives_size / sizeof(*archives);
184         if (archive_count == 1 && *archives == 0) {
185                 archive_count = 0;
186                 archives = NULL;
187         }
188
189         rc = mdt_hsm_agent_register(info, &tsi->tsi_exp->exp_client_uuid,
190                                     archive_count, archives);
191
192 out:
193         mdt_thread_info_fini(info);
194         RETURN(rc);
195 }
196
197 int mdt_hsm_ct_unregister(struct tgt_session_info *tsi)
198 {
199         struct mdt_thread_info  *info;
200         int                      rc;
201         ENTRY;
202
203         if (tsi->tsi_mdt_body == NULL)
204                 RETURN(-EPROTO);
205
206         info = tsi2mdt_info(tsi);
207         if (!mdt_hsm_is_admin(info))
208                 GOTO(out, rc = -EPERM);
209
210         /* XXX: directly include this function here? */
211         rc = mdt_hsm_agent_unregister(info, &tsi->tsi_exp->exp_client_uuid);
212 out:
213         mdt_thread_info_fini(info);
214         RETURN(rc);
215 }
216
217 /**
218  * Retrieve the current HSM flags, archive id and undergoing HSM requests for
219  * the fid provided in RPC body.
220  *
221  * Current requests are read from coordinator states.
222  *
223  * This is MDS_HSM_STATE_GET RPC handler.
224  */
225 int mdt_hsm_state_get(struct tgt_session_info *tsi)
226 {
227         struct mdt_thread_info  *info = tsi2mdt_info(tsi);
228         struct mdt_object       *obj = info->mti_object;
229         struct md_attr          *ma  = &info->mti_attr;
230         struct hsm_user_state   *hus;
231         struct mdt_lock_handle  *lh;
232         int                      rc;
233         ENTRY;
234
235         if (info->mti_body == NULL || obj == NULL)
236                 GOTO(out, rc = -EPROTO);
237
238         /* Only valid if client is remote */
239         rc = mdt_init_ucred(info, (struct mdt_body *)info->mti_body);
240         if (rc < 0)
241                 GOTO(out, rc = err_serious(rc));
242
243         lh = &info->mti_lh[MDT_LH_CHILD];
244         rc = mdt_object_lock(info, obj, lh, MDS_INODELOCK_LOOKUP, LCK_PR);
245         if (rc < 0)
246                 GOTO(out_ucred, rc);
247
248         ma->ma_valid = 0;
249         ma->ma_need = MA_HSM;
250         rc = mdt_attr_get_complex(info, obj, ma);
251         if (rc)
252                 GOTO(out_unlock, rc);
253
254         hus = req_capsule_server_get(tsi->tsi_pill, &RMF_HSM_USER_STATE);
255         if (hus == NULL)
256                 GOTO(out_unlock, rc = -EPROTO);
257
258         /* Current HSM flags */
259         hus->hus_states = ma->ma_hsm.mh_flags;
260         hus->hus_archive_id = ma->ma_hsm.mh_arch_id;
261
262         EXIT;
263 out_unlock:
264         mdt_object_unlock(info, obj, lh, 1);
265 out_ucred:
266         mdt_exit_ucred(info);
267 out:
268         mdt_thread_info_fini(info);
269         return rc;
270 }
271
272 /**
273  * Change HSM state and archive number of a file.
274  *
275  * Archive number is changed iif the value is not 0.
276  * The new flagset that will be computed should result in a coherent state.
277  * This function checks that flags are compatible.
278  *
279  * This is MDS_HSM_STATE_SET RPC handler.
280  */
281 int mdt_hsm_state_set(struct tgt_session_info *tsi)
282 {
283         struct mdt_thread_info  *info = tsi2mdt_info(tsi);
284         struct mdt_object       *obj = info->mti_object;
285         struct md_attr          *ma = &info->mti_attr;
286         struct hsm_state_set    *hss;
287         struct mdt_lock_handle  *lh;
288         int                      rc;
289         __u64                    flags;
290         ENTRY;
291
292         hss = req_capsule_client_get(info->mti_pill, &RMF_HSM_STATE_SET);
293
294         if (info->mti_body == NULL || obj == NULL || hss == NULL)
295                 GOTO(out, rc = -EPROTO);
296
297         /* Only valid if client is remote */
298         rc = mdt_init_ucred(info, (struct mdt_body *)info->mti_body);
299         if (rc < 0)
300                 GOTO(out, rc = err_serious(rc));
301
302         lh = &info->mti_lh[MDT_LH_CHILD];
303         rc = mdt_object_lock(info, obj, lh, MDS_INODELOCK_LOOKUP |
304                              MDS_INODELOCK_XATTR, LCK_PW);
305         if (rc < 0)
306                 GOTO(out_ucred, rc);
307
308         /* Detect out-of range masks */
309         if ((hss->hss_setmask | hss->hss_clearmask) & ~HSM_FLAGS_MASK) {
310                 CDEBUG(D_HSM, "Incompatible masks provided (set %#llx"
311                        ", clear %#llx) vs supported set (%#x).\n",
312                        hss->hss_setmask, hss->hss_clearmask, HSM_FLAGS_MASK);
313                 GOTO(out_unlock, rc = -EINVAL);
314         }
315
316         /* Non-root users are forbidden to set or clear flags which are
317          * NOT defined in HSM_USER_MASK. */
318         if (((hss->hss_setmask | hss->hss_clearmask) & ~HSM_USER_MASK) &&
319             !cap_raised(mdt_ucred(info)->uc_cap, CAP_SYS_ADMIN)) {
320                 CDEBUG(D_HSM, "Incompatible masks provided (set %#llx"
321                        ", clear %#llx) vs unprivileged set (%#x).\n",
322                        hss->hss_setmask, hss->hss_clearmask, HSM_USER_MASK);
323                 GOTO(out_unlock, rc = -EPERM);
324         }
325
326         /* Read current HSM info */
327         ma->ma_valid = 0;
328         ma->ma_need = MA_HSM;
329         rc = mdt_attr_get_complex(info, obj, ma);
330         if (rc)
331                 GOTO(out_unlock, rc);
332
333         /* Change HSM flags depending on provided masks */
334         if (hss->hss_valid & HSS_SETMASK)
335                 ma->ma_hsm.mh_flags |= hss->hss_setmask;
336         if (hss->hss_valid & HSS_CLEARMASK)
337                 ma->ma_hsm.mh_flags &= ~hss->hss_clearmask;
338
339         /* Change archive_id if provided. */
340         if (hss->hss_valid & HSS_ARCHIVE_ID) {
341                 struct ptlrpc_request *req = mdt_info_req(info);
342                 struct obd_export *exp = req->rq_export;
343
344                 if (!(ma->ma_hsm.mh_flags & HS_EXISTS)) {
345                         CDEBUG(D_HSM, "Could not set an archive number for "
346                                DFID "if HSM EXISTS flag is not set.\n",
347                                PFID(&info->mti_body->mbo_fid1));
348                         GOTO(out_unlock, rc);
349                 }
350
351                 if (!exp_connect_archive_id_array(exp) &&
352                     hss->hss_archive_id > LL_HSM_ORIGIN_MAX_ARCHIVE) {
353                         CDEBUG(D_HSM, "archive id %u from old clients "
354                                "exceeds maximum %zu.\n",
355                                hss->hss_archive_id, LL_HSM_ORIGIN_MAX_ARCHIVE);
356                         GOTO(out_unlock, rc = -EINVAL);
357                 }
358
359                 ma->ma_hsm.mh_arch_id = hss->hss_archive_id;
360         }
361
362         /* Check for inconsistant HSM flagset.
363          * DIRTY without EXISTS: no dirty if no archive was created.
364          * DIRTY and RELEASED: a dirty file could not be released.
365          * RELEASED without ARCHIVED: do not release a non-archived file.
366          * LOST without ARCHIVED: cannot lost a non-archived file.
367          */
368         flags = ma->ma_hsm.mh_flags;
369         if ((flags & HS_DIRTY    && !(flags & HS_EXISTS)) ||
370             (flags & HS_RELEASED && flags & HS_DIRTY) ||
371             (flags & HS_RELEASED && !(flags & HS_ARCHIVED)) ||
372             (flags & HS_LOST     && !(flags & HS_ARCHIVED))) {
373                 CDEBUG(D_HSM, "Incompatible flag change on "DFID
374                               "flags=%#llx\n",
375                        PFID(&info->mti_body->mbo_fid1), flags);
376                 GOTO(out_unlock, rc = -EINVAL);
377         }
378
379         /* Save the modified flags */
380         rc = mdt_hsm_attr_set(info, obj, &ma->ma_hsm);
381         if (rc)
382                 GOTO(out_unlock, rc);
383
384         EXIT;
385
386 out_unlock:
387         mdt_object_unlock(info, obj, lh, 1);
388 out_ucred:
389         mdt_exit_ucred(info);
390 out:
391         mdt_thread_info_fini(info);
392         return rc;
393 }
394
395 /**
396  * Retrieve undergoing HSM requests for the fid provided in RPC body.
397  * Current requests are read from coordinator states.
398  *
399  * This is MDS_HSM_ACTION RPC handler.
400  */
401 int mdt_hsm_action(struct tgt_session_info *tsi)
402 {
403         struct mdt_thread_info *info;
404         struct hsm_current_action *hca;
405         enum hsm_copytool_action action; /* HSMA_* */
406         enum agent_req_status status; /* ARS_* */
407         struct hsm_extent extent;
408         int rc;
409         ENTRY;
410
411         hca = req_capsule_server_get(tsi->tsi_pill,
412                                      &RMF_MDS_HSM_CURRENT_ACTION);
413         if (hca == NULL)
414                 RETURN(err_serious(-EPROTO));
415
416         if (tsi->tsi_mdt_body == NULL)
417                 RETURN(-EPROTO);
418
419         info = tsi2mdt_info(tsi);
420         /* Only valid if client is remote */
421         rc = mdt_init_ucred(info, (struct mdt_body *)info->mti_body);
422         if (rc < 0)
423                 GOTO(out, rc = err_serious(rc));
424
425         rc = mdt_hsm_get_action(info, &info->mti_body->mbo_fid1, &action,
426                                 &status, &extent);
427         if (rc < 0)
428                 GOTO(out_ucred, rc);
429
430         switch (action) {
431         case HSMA_NONE:
432                 hca->hca_action = HUA_NONE;
433                 break;
434         case HSMA_ARCHIVE:
435                 hca->hca_action = HUA_ARCHIVE;
436                 break;
437         case HSMA_RESTORE:
438                 hca->hca_action = HUA_RESTORE;
439                 break;
440         case HSMA_REMOVE:
441                 hca->hca_action = HUA_REMOVE;
442                 break;
443         case HSMA_CANCEL:
444                 hca->hca_action = HUA_CANCEL;
445                 break;
446         default:
447                 hca->hca_action = HUA_NONE;
448                 CERROR("%s: Unknown hsm action: %d on "DFID"\n",
449                        mdt_obd_name(info->mti_mdt), action,
450                        PFID(&info->mti_body->mbo_fid1));
451                 break;
452         }
453
454         switch (status) {
455         case ARS_WAITING:
456                 hca->hca_state = HPS_WAITING;
457                 break;
458         case ARS_STARTED:
459                 hca->hca_state = HPS_RUNNING;
460                 break;
461         default:
462                 hca->hca_state = HPS_NONE;
463                 break;
464         }
465
466         hca->hca_location = extent;
467
468         EXIT;
469 out_ucred:
470         mdt_exit_ucred(info);
471 out:
472         mdt_thread_info_fini(info);
473         return rc;
474 }
475
476 /* Return true if a FID is present in an action list. */
477 static bool is_fid_in_hal(struct hsm_action_list *hal, const struct lu_fid *fid)
478 {
479         struct hsm_action_item *hai;
480         int i;
481
482         for (hai = hai_first(hal), i = 0;
483              i < hal->hal_count;
484              i++, hai = hai_next(hai)) {
485                 if (lu_fid_eq(&hai->hai_fid, fid))
486                         return true;
487         }
488
489         return false;
490 }
491
492 /**
493  * Process the HSM actions described in a struct hsm_user_request.
494  *
495  * The action described in hur will be send to coordinator to be saved and
496  * processed later or either handled directly if hur.hur_action is HUA_RELEASE.
497  *
498  * This is MDS_HSM_REQUEST RPC handler.
499  */
500 int mdt_hsm_request(struct tgt_session_info *tsi)
501 {
502         struct mdt_thread_info          *info;
503         struct req_capsule              *pill = tsi->tsi_pill;
504         struct hsm_request              *hr;
505         struct hsm_user_item            *hui;
506         struct hsm_action_list          *hal;
507         struct hsm_action_item          *hai;
508         const void                      *data;
509         int                              hui_list_size;
510         int                              data_size;
511         enum hsm_copytool_action         action = HSMA_NONE;
512         int                              hal_size, i, rc;
513         ENTRY;
514
515         hr = req_capsule_client_get(pill, &RMF_MDS_HSM_REQUEST);
516         hui = req_capsule_client_get(pill, &RMF_MDS_HSM_USER_ITEM);
517         data = req_capsule_client_get(pill, &RMF_GENERIC_DATA);
518
519         if (tsi->tsi_mdt_body == NULL || hr == NULL || hui == NULL || data == NULL)
520                 RETURN(-EPROTO);
521
522         /* Sanity check. Nothing to do with an empty list */
523         if (hr->hr_itemcount == 0)
524                 RETURN(0);
525
526         hui_list_size = req_capsule_get_size(pill, &RMF_MDS_HSM_USER_ITEM,
527                                              RCL_CLIENT);
528         if (hui_list_size < hr->hr_itemcount * sizeof(*hui))
529                 RETURN(-EPROTO);
530
531         data_size = req_capsule_get_size(pill, &RMF_GENERIC_DATA, RCL_CLIENT);
532         if (data_size != hr->hr_data_len)
533                 RETURN(-EPROTO);
534
535         info = tsi2mdt_info(tsi);
536         /* Only valid if client is remote */
537         rc = mdt_init_ucred(info, (struct mdt_body *)info->mti_body);
538         if (rc)
539                 GOTO(out, rc);
540
541         switch (hr->hr_action) {
542         /* code to be removed in hsm1_merge and final patch */
543         case HUA_RELEASE:
544                 CERROR("Release action is not working in hsm1_coord\n");
545                 GOTO(out_ucred, rc = -EINVAL);
546                 break;
547         /* end of code to be removed */
548         case HUA_ARCHIVE:
549                 action = HSMA_ARCHIVE;
550                 break;
551         case HUA_RESTORE:
552                 action = HSMA_RESTORE;
553                 break;
554         case HUA_REMOVE:
555                 action = HSMA_REMOVE;
556                 break;
557         case HUA_CANCEL:
558                 action = HSMA_CANCEL;
559                 break;
560         default:
561                 CERROR("Unknown hsm action: %d\n", hr->hr_action);
562                 GOTO(out_ucred, rc = -EINVAL);
563         }
564
565         hal_size = sizeof(*hal) + round_up(MTI_NAME_MAXLEN, 8) /* fsname */ +
566                    (sizeof(*hai) + round_up(hr->hr_data_len, 8)) *
567                    hr->hr_itemcount;
568
569         MDT_HSM_ALLOC(hal, hal_size);
570         if (hal == NULL)
571                 GOTO(out_ucred, rc = -ENOMEM);
572
573         hal->hal_version = HAL_VERSION;
574         hal->hal_archive_id = hr->hr_archive_id;
575         hal->hal_flags = hr->hr_flags;
576         obd_uuid2fsname(hal->hal_fsname, mdt_obd_name(info->mti_mdt),
577                         MTI_NAME_MAXLEN);
578
579         hal->hal_count = 0;
580         hai = hai_first(hal);
581         for (i = 0; i < hr->hr_itemcount; i++, hai = hai_next(hai)) {
582                 /* Get rid of duplicate entries. Otherwise we get
583                  * duplicated work in the llog. */
584                 if (is_fid_in_hal(hal, &hui[i].hui_fid))
585                         continue;
586
587                 hai->hai_action = action;
588                 hai->hai_cookie = 0;
589                 hai->hai_gid = 0;
590                 hai->hai_fid = hui[i].hui_fid;
591                 hai->hai_extent = hui[i].hui_extent;
592                 memcpy(hai->hai_data, data, hr->hr_data_len);
593                 hai->hai_len = sizeof(*hai) + hr->hr_data_len;
594
595                 hal->hal_count++;
596         }
597
598         rc = mdt_hsm_add_actions(info, hal);
599
600         MDT_HSM_FREE(hal, hal_size);
601
602         GOTO(out_ucred, rc);
603
604 out_ucred:
605         mdt_exit_ucred(info);
606 out:
607         mdt_thread_info_fini(info);
608         return rc;
609 }