Whamcloud - gitweb
1effc5a8aa93cd49ad07599bc4da71888b8f436e
[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         CLASSERT(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 = md_capable(mdt_ucred(info), CFS_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         mdt_lock_reg_init(lh, LCK_PR);
245         rc = mdt_object_lock(info, obj, lh, MDS_INODELOCK_LOOKUP);
246         if (rc < 0)
247                 GOTO(out_ucred, rc);
248
249         ma->ma_valid = 0;
250         ma->ma_need = MA_HSM;
251         rc = mdt_attr_get_complex(info, obj, ma);
252         if (rc)
253                 GOTO(out_unlock, rc);
254
255         hus = req_capsule_server_get(tsi->tsi_pill, &RMF_HSM_USER_STATE);
256         if (hus == NULL)
257                 GOTO(out_unlock, rc = -EPROTO);
258
259         /* Current HSM flags */
260         hus->hus_states = ma->ma_hsm.mh_flags;
261         hus->hus_archive_id = ma->ma_hsm.mh_arch_id;
262
263         EXIT;
264 out_unlock:
265         mdt_object_unlock(info, obj, lh, 1);
266 out_ucred:
267         mdt_exit_ucred(info);
268 out:
269         mdt_thread_info_fini(info);
270         return rc;
271 }
272
273 /**
274  * Change HSM state and archive number of a file.
275  *
276  * Archive number is changed iif the value is not 0.
277  * The new flagset that will be computed should result in a coherent state.
278  * This function checks that flags are compatible.
279  *
280  * This is MDS_HSM_STATE_SET RPC handler.
281  */
282 int mdt_hsm_state_set(struct tgt_session_info *tsi)
283 {
284         struct mdt_thread_info  *info = tsi2mdt_info(tsi);
285         struct mdt_object       *obj = info->mti_object;
286         struct md_attr          *ma = &info->mti_attr;
287         struct hsm_state_set    *hss;
288         struct mdt_lock_handle  *lh;
289         int                      rc;
290         __u64                    flags;
291         ENTRY;
292
293         hss = req_capsule_client_get(info->mti_pill, &RMF_HSM_STATE_SET);
294
295         if (info->mti_body == NULL || obj == NULL || hss == NULL)
296                 GOTO(out, rc = -EPROTO);
297
298         /* Only valid if client is remote */
299         rc = mdt_init_ucred(info, (struct mdt_body *)info->mti_body);
300         if (rc < 0)
301                 GOTO(out, rc = err_serious(rc));
302
303         lh = &info->mti_lh[MDT_LH_CHILD];
304         mdt_lock_reg_init(lh, LCK_PW);
305         rc = mdt_object_lock(info, obj, lh, MDS_INODELOCK_LOOKUP |
306                              MDS_INODELOCK_XATTR);
307         if (rc < 0)
308                 GOTO(out_ucred, rc);
309
310         /* Detect out-of range masks */
311         if ((hss->hss_setmask | hss->hss_clearmask) & ~HSM_FLAGS_MASK) {
312                 CDEBUG(D_HSM, "Incompatible masks provided (set %#llx"
313                        ", clear %#llx) vs supported set (%#x).\n",
314                        hss->hss_setmask, hss->hss_clearmask, HSM_FLAGS_MASK);
315                 GOTO(out_unlock, rc = -EINVAL);
316         }
317
318         /* Non-root users are forbidden to set or clear flags which are
319          * NOT defined in HSM_USER_MASK. */
320         if (((hss->hss_setmask | hss->hss_clearmask) & ~HSM_USER_MASK) &&
321             !md_capable(mdt_ucred(info), CFS_CAP_SYS_ADMIN)) {
322                 CDEBUG(D_HSM, "Incompatible masks provided (set %#llx"
323                        ", clear %#llx) vs unprivileged set (%#x).\n",
324                        hss->hss_setmask, hss->hss_clearmask, HSM_USER_MASK);
325                 GOTO(out_unlock, rc = -EPERM);
326         }
327
328         /* Read current HSM info */
329         ma->ma_valid = 0;
330         ma->ma_need = MA_HSM;
331         rc = mdt_attr_get_complex(info, obj, ma);
332         if (rc)
333                 GOTO(out_unlock, rc);
334
335         /* Change HSM flags depending on provided masks */
336         if (hss->hss_valid & HSS_SETMASK)
337                 ma->ma_hsm.mh_flags |= hss->hss_setmask;
338         if (hss->hss_valid & HSS_CLEARMASK)
339                 ma->ma_hsm.mh_flags &= ~hss->hss_clearmask;
340
341         /* Change archive_id if provided. */
342         if (hss->hss_valid & HSS_ARCHIVE_ID) {
343                 struct ptlrpc_request *req = mdt_info_req(info);
344                 struct obd_export *exp = req->rq_export;
345
346                 if (!(ma->ma_hsm.mh_flags & HS_EXISTS)) {
347                         CDEBUG(D_HSM, "Could not set an archive number for "
348                                DFID "if HSM EXISTS flag is not set.\n",
349                                PFID(&info->mti_body->mbo_fid1));
350                         GOTO(out_unlock, rc);
351                 }
352
353                 if (!exp_connect_archive_id_array(exp) &&
354                     hss->hss_archive_id > LL_HSM_ORIGIN_MAX_ARCHIVE) {
355                         CDEBUG(D_HSM, "archive id %u from old clients "
356                                "exceeds maximum %zu.\n",
357                                hss->hss_archive_id, LL_HSM_ORIGIN_MAX_ARCHIVE);
358                         GOTO(out_unlock, rc = -EINVAL);
359                 }
360
361                 ma->ma_hsm.mh_arch_id = hss->hss_archive_id;
362         }
363
364         /* Check for inconsistant HSM flagset.
365          * DIRTY without EXISTS: no dirty if no archive was created.
366          * DIRTY and RELEASED: a dirty file could not be released.
367          * RELEASED without ARCHIVED: do not release a non-archived file.
368          * LOST without ARCHIVED: cannot lost a non-archived file.
369          */
370         flags = ma->ma_hsm.mh_flags;
371         if ((flags & HS_DIRTY    && !(flags & HS_EXISTS)) ||
372             (flags & HS_RELEASED && flags & HS_DIRTY) ||
373             (flags & HS_RELEASED && !(flags & HS_ARCHIVED)) ||
374             (flags & HS_LOST     && !(flags & HS_ARCHIVED))) {
375                 CDEBUG(D_HSM, "Incompatible flag change on "DFID
376                               "flags=%#llx\n",
377                        PFID(&info->mti_body->mbo_fid1), flags);
378                 GOTO(out_unlock, rc = -EINVAL);
379         }
380
381         /* Save the modified flags */
382         rc = mdt_hsm_attr_set(info, obj, &ma->ma_hsm);
383         if (rc)
384                 GOTO(out_unlock, rc);
385
386         EXIT;
387
388 out_unlock:
389         mdt_object_unlock(info, obj, lh, 1);
390 out_ucred:
391         mdt_exit_ucred(info);
392 out:
393         mdt_thread_info_fini(info);
394         return rc;
395 }
396
397 /**
398  * Retrieve undergoing HSM requests for the fid provided in RPC body.
399  * Current requests are read from coordinator states.
400  *
401  * This is MDS_HSM_ACTION RPC handler.
402  */
403 int mdt_hsm_action(struct tgt_session_info *tsi)
404 {
405         struct mdt_thread_info *info;
406         struct hsm_current_action *hca;
407         enum hsm_copytool_action action; /* HSMA_* */
408         enum agent_req_status status; /* ARS_* */
409         struct hsm_extent extent;
410         int rc;
411         ENTRY;
412
413         hca = req_capsule_server_get(tsi->tsi_pill,
414                                      &RMF_MDS_HSM_CURRENT_ACTION);
415         if (hca == NULL)
416                 RETURN(err_serious(-EPROTO));
417
418         if (tsi->tsi_mdt_body == NULL)
419                 RETURN(-EPROTO);
420
421         info = tsi2mdt_info(tsi);
422         /* Only valid if client is remote */
423         rc = mdt_init_ucred(info, (struct mdt_body *)info->mti_body);
424         if (rc < 0)
425                 GOTO(out, rc = err_serious(rc));
426
427         rc = mdt_hsm_get_action(info, &info->mti_body->mbo_fid1, &action,
428                                 &status, &extent);
429         if (rc < 0)
430                 GOTO(out_ucred, rc);
431
432         switch (action) {
433         case HSMA_NONE:
434                 hca->hca_action = HUA_NONE;
435                 break;
436         case HSMA_ARCHIVE:
437                 hca->hca_action = HUA_ARCHIVE;
438                 break;
439         case HSMA_RESTORE:
440                 hca->hca_action = HUA_RESTORE;
441                 break;
442         case HSMA_REMOVE:
443                 hca->hca_action = HUA_REMOVE;
444                 break;
445         case HSMA_CANCEL:
446                 hca->hca_action = HUA_CANCEL;
447                 break;
448         default:
449                 hca->hca_action = HUA_NONE;
450                 CERROR("%s: Unknown hsm action: %d on "DFID"\n",
451                        mdt_obd_name(info->mti_mdt), action,
452                        PFID(&info->mti_body->mbo_fid1));
453                 break;
454         }
455
456         switch (status) {
457         case ARS_WAITING:
458                 hca->hca_state = HPS_WAITING;
459                 break;
460         case ARS_STARTED:
461                 hca->hca_state = HPS_RUNNING;
462                 break;
463         default:
464                 hca->hca_state = HPS_NONE;
465                 break;
466         }
467
468         hca->hca_location = extent;
469
470         EXIT;
471 out_ucred:
472         mdt_exit_ucred(info);
473 out:
474         mdt_thread_info_fini(info);
475         return rc;
476 }
477
478 /* Return true if a FID is present in an action list. */
479 static bool is_fid_in_hal(struct hsm_action_list *hal, const struct lu_fid *fid)
480 {
481         struct hsm_action_item *hai;
482         int i;
483
484         for (hai = hai_first(hal), i = 0;
485              i < hal->hal_count;
486              i++, hai = hai_next(hai)) {
487                 if (lu_fid_eq(&hai->hai_fid, fid))
488                         return true;
489         }
490
491         return false;
492 }
493
494 /**
495  * Process the HSM actions described in a struct hsm_user_request.
496  *
497  * The action described in hur will be send to coordinator to be saved and
498  * processed later or either handled directly if hur.hur_action is HUA_RELEASE.
499  *
500  * This is MDS_HSM_REQUEST RPC handler.
501  */
502 int mdt_hsm_request(struct tgt_session_info *tsi)
503 {
504         struct mdt_thread_info          *info;
505         struct req_capsule              *pill = tsi->tsi_pill;
506         struct hsm_request              *hr;
507         struct hsm_user_item            *hui;
508         struct hsm_action_list          *hal;
509         struct hsm_action_item          *hai;
510         const void                      *data;
511         int                              hui_list_size;
512         int                              data_size;
513         enum hsm_copytool_action         action = HSMA_NONE;
514         int                              hal_size, i, rc;
515         ENTRY;
516
517         hr = req_capsule_client_get(pill, &RMF_MDS_HSM_REQUEST);
518         hui = req_capsule_client_get(pill, &RMF_MDS_HSM_USER_ITEM);
519         data = req_capsule_client_get(pill, &RMF_GENERIC_DATA);
520
521         if (tsi->tsi_mdt_body == NULL || hr == NULL || hui == NULL || data == NULL)
522                 RETURN(-EPROTO);
523
524         /* Sanity check. Nothing to do with an empty list */
525         if (hr->hr_itemcount == 0)
526                 RETURN(0);
527
528         hui_list_size = req_capsule_get_size(pill, &RMF_MDS_HSM_USER_ITEM,
529                                              RCL_CLIENT);
530         if (hui_list_size < hr->hr_itemcount * sizeof(*hui))
531                 RETURN(-EPROTO);
532
533         data_size = req_capsule_get_size(pill, &RMF_GENERIC_DATA, RCL_CLIENT);
534         if (data_size != hr->hr_data_len)
535                 RETURN(-EPROTO);
536
537         info = tsi2mdt_info(tsi);
538         /* Only valid if client is remote */
539         rc = mdt_init_ucred(info, (struct mdt_body *)info->mti_body);
540         if (rc)
541                 GOTO(out, rc);
542
543         switch (hr->hr_action) {
544         /* code to be removed in hsm1_merge and final patch */
545         case HUA_RELEASE:
546                 CERROR("Release action is not working in hsm1_coord\n");
547                 GOTO(out_ucred, rc = -EINVAL);
548                 break;
549         /* end of code to be removed */
550         case HUA_ARCHIVE:
551                 action = HSMA_ARCHIVE;
552                 break;
553         case HUA_RESTORE:
554                 action = HSMA_RESTORE;
555                 break;
556         case HUA_REMOVE:
557                 action = HSMA_REMOVE;
558                 break;
559         case HUA_CANCEL:
560                 action = HSMA_CANCEL;
561                 break;
562         default:
563                 CERROR("Unknown hsm action: %d\n", hr->hr_action);
564                 GOTO(out_ucred, rc = -EINVAL);
565         }
566
567         hal_size = sizeof(*hal) + cfs_size_round(MTI_NAME_MAXLEN) /* fsname */ +
568                    (sizeof(*hai) + cfs_size_round(hr->hr_data_len)) *
569                    hr->hr_itemcount;
570
571         MDT_HSM_ALLOC(hal, hal_size);
572         if (hal == NULL)
573                 GOTO(out_ucred, rc = -ENOMEM);
574
575         hal->hal_version = HAL_VERSION;
576         hal->hal_archive_id = hr->hr_archive_id;
577         hal->hal_flags = hr->hr_flags;
578         obd_uuid2fsname(hal->hal_fsname, mdt_obd_name(info->mti_mdt),
579                         MTI_NAME_MAXLEN);
580
581         hal->hal_count = 0;
582         hai = hai_first(hal);
583         for (i = 0; i < hr->hr_itemcount; i++, hai = hai_next(hai)) {
584                 /* Get rid of duplicate entries. Otherwise we get
585                  * duplicated work in the llog. */
586                 if (is_fid_in_hal(hal, &hui[i].hui_fid))
587                         continue;
588
589                 hai->hai_action = action;
590                 hai->hai_cookie = 0;
591                 hai->hai_gid = 0;
592                 hai->hai_fid = hui[i].hui_fid;
593                 hai->hai_extent = hui[i].hui_extent;
594                 memcpy(hai->hai_data, data, hr->hr_data_len);
595                 hai->hai_len = sizeof(*hai) + hr->hr_data_len;
596
597                 hal->hal_count++;
598         }
599
600         rc = mdt_hsm_add_actions(info, hal);
601
602         MDT_HSM_FREE(hal, hal_size);
603
604         GOTO(out_ucred, rc);
605
606 out_ucred:
607         mdt_exit_ucred(info);
608 out:
609         mdt_thread_info_fini(info);
610         return rc;
611 }