Whamcloud - gitweb
LU-9312 hsm: convert cdt_llog_lock to a rw semaphore
[fs/lustre-release.git] / lustre / mdt / mdt_hsm_cdt_client.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  * (C) Copyright 2012 Commissariat a l'energie atomique et aux energies
24  *     alternatives
25  *
26  * Copyright (c) 2013, 2014, Intel Corporation.
27  */
28 /*
29  * lustre/mdt/mdt_hsm_cdt_client.c
30  *
31  * Lustre HSM Coordinator
32  *
33  * Author: Jacques-Charles Lafoucriere <jacques-charles.lafoucriere@cea.fr>
34  * Author: Aurelien Degremont <aurelien.degremont@cea.fr>
35  */
36
37 #define DEBUG_SUBSYSTEM S_MDS
38
39 #include <obd_support.h>
40 #include <lustre_export.h>
41 #include <obd.h>
42 #include <lprocfs_status.h>
43 #include <lustre_log.h>
44 #include "mdt_internal.h"
45
46 /**
47  * data passed to llog_cat_process() callback
48  * to find compatible requests
49  */
50 struct hsm_compat_data_cb {
51         struct coordinator      *cdt;
52         struct hsm_action_list  *hal;
53 };
54
55 /**
56  * llog_cat_process() callback, used to find record
57  * compatibles with a new hsm_action_list
58  * \param env [IN] environment
59  * \param llh [IN] llog handle
60  * \param hdr [IN] llog record
61  * \param data [IN] cb data = hsm_compat_data_cb
62  * \retval 0 success
63  * \retval -ve failure
64  */
65 static int hsm_find_compatible_cb(const struct lu_env *env,
66                                   struct llog_handle *llh,
67                                   struct llog_rec_hdr *hdr, void *data)
68 {
69         struct llog_agent_req_rec       *larr;
70         struct hsm_compat_data_cb       *hcdcb;
71         struct hsm_action_item          *hai;
72         int                              i;
73         ENTRY;
74
75         larr = (struct llog_agent_req_rec *)hdr;
76         hcdcb = data;
77         /* a compatible request must be WAITING or STARTED
78          * and not a cancel */
79         if ((larr->arr_status != ARS_WAITING &&
80              larr->arr_status != ARS_STARTED) ||
81             larr->arr_hai.hai_action == HSMA_CANCEL)
82                 RETURN(0);
83
84         hai = hai_first(hcdcb->hal);
85         for (i = 0; i < hcdcb->hal->hal_count; i++, hai = hai_next(hai)) {
86                 /* if request is a CANCEL:
87                  * if cookie set in the request, there is no need to find a
88                  * compatible one, the cookie in the request is directly used.
89                  * if cookie is not set, we use the FID to find the request
90                  * to cancel (the "compatible" one)
91                  * if the caller sets the cookie, we assume he also sets the
92                  * arr_archive_id
93                  */
94                 if (hai->hai_action == HSMA_CANCEL && hai->hai_cookie != 0)
95                         continue;
96
97                 if (!lu_fid_eq(&hai->hai_fid, &larr->arr_hai.hai_fid))
98                         continue;
99
100                 /* HSMA_NONE is used to find running request for some FID */
101                 if (hai->hai_action == HSMA_NONE) {
102                         hcdcb->hal->hal_archive_id = larr->arr_archive_id;
103                         hcdcb->hal->hal_flags = larr->arr_flags;
104                         *hai = larr->arr_hai;
105                         continue;
106                 }
107                 /* in V1 we do not manage partial transfer
108                  * so extent is always whole file
109                  */
110                 hai->hai_cookie = larr->arr_hai.hai_cookie;
111                 /* we read the archive number from the request we cancel */
112                 if (hai->hai_action == HSMA_CANCEL &&
113                     hcdcb->hal->hal_archive_id == 0)
114                         hcdcb->hal->hal_archive_id = larr->arr_archive_id;
115         }
116         RETURN(0);
117 }
118
119 /**
120  * find compatible requests already recorded
121  * \param env [IN] environment
122  * \param mdt [IN] MDT device
123  * \param hal [IN/OUT] new request
124  *    cookie set to compatible found or to 0 if not found
125  *    for cancel request, see callback hsm_find_compatible_cb()
126  * \retval 0 success
127  * \retval -ve failure
128  */
129 static int hsm_find_compatible(const struct lu_env *env, struct mdt_device *mdt,
130                                struct hsm_action_list *hal)
131 {
132         struct hsm_action_item          *hai;
133         struct hsm_compat_data_cb        hcdcb;
134         int                              rc, i, ok_cnt;
135         ENTRY;
136
137         ok_cnt = 0;
138         hai = hai_first(hal);
139         for (i = 0; i < hal->hal_count; i++, hai = hai_next(hai)) {
140                 /* in a cancel request hai_cookie may be set by caller to
141                  * show the request to be canceled
142                  * if not we need to search by FID
143                  */
144                 if (hai->hai_action == HSMA_CANCEL && hai->hai_cookie != 0)
145                         ok_cnt++;
146                 else
147                         hai->hai_cookie = 0;
148         }
149
150         /* if all requests are cancel with cookie, no need to find compatible */
151         if (ok_cnt == hal->hal_count)
152                 RETURN(0);
153
154         hcdcb.cdt = &mdt->mdt_coordinator;
155         hcdcb.hal = hal;
156
157         rc = cdt_llog_process(env, mdt, hsm_find_compatible_cb, &hcdcb, READ);
158
159         RETURN(rc);
160 }
161
162 /**
163  * check if an action is really needed
164  * \param hai [IN] request description
165  * \param hal_an [IN] request archive number (not used)
166  * \param rq_flags [IN] request flags
167  * \param hsm [IN] file HSM metadata
168  * \retval boolean
169  */
170 static bool hsm_action_is_needed(struct hsm_action_item *hai, int hal_an,
171                                  __u64 rq_flags, struct md_hsm *hsm)
172 {
173         bool     is_needed = false;
174         int      hsm_flags;
175         ENTRY;
176
177         if (rq_flags & HSM_FORCE_ACTION)
178                 RETURN(true);
179
180         hsm_flags = hsm->mh_flags;
181         switch (hai->hai_action) {
182         case HSMA_ARCHIVE:
183                 if (hsm_flags & HS_DIRTY || !(hsm_flags & HS_ARCHIVED))
184                         is_needed = true;
185                 break;
186         case HSMA_RESTORE:
187                 /* if file is dirty we must return an error, this function
188                  * cannot, so we ask for an action and
189                  * mdt_hsm_is_action_compat() will return an error
190                  */
191                 if (hsm_flags & (HS_RELEASED | HS_DIRTY))
192                         is_needed = true;
193                 break;
194         case HSMA_REMOVE:
195                 if (hsm_flags & (HS_ARCHIVED | HS_EXISTS))
196                         is_needed = true;
197                 break;
198         case HSMA_CANCEL:
199                 is_needed = true;
200                 break;
201         }
202         CDEBUG(D_HSM, "fid="DFID" action=%s rq_flags=%#llx"
203                       " extent=%#llx-%#llx hsm_flags=%X %s\n",
204                       PFID(&hai->hai_fid),
205                       hsm_copytool_action2name(hai->hai_action), rq_flags,
206                       hai->hai_extent.offset, hai->hai_extent.length,
207                       hsm->mh_flags,
208                       (is_needed ? "action needed" : "no action needed"));
209
210         RETURN(is_needed);
211 }
212
213 /**
214  * test sanity of an hal
215  * FID must be valid
216  * action must be known
217  * \param hal [IN]
218  * \retval boolean
219  */
220 static bool hal_is_sane(struct hsm_action_list *hal)
221 {
222         int                      i;
223         struct hsm_action_item  *hai;
224         ENTRY;
225
226         if (hal->hal_count == 0)
227                 RETURN(false);
228
229         hai = hai_first(hal);
230         for (i = 0; i < hal->hal_count; i++, hai = hai_next(hai)) {
231                 if (!fid_is_sane(&hai->hai_fid))
232                         RETURN(false);
233                 switch (hai->hai_action) {
234                 case HSMA_NONE:
235                 case HSMA_ARCHIVE:
236                 case HSMA_RESTORE:
237                 case HSMA_REMOVE:
238                 case HSMA_CANCEL:
239                         break;
240                 default:
241                         RETURN(false);
242                 }
243         }
244         RETURN(true);
245 }
246
247 static int
248 hsm_action_permission(struct mdt_thread_info *mti,
249                       struct mdt_object *obj,
250                       enum hsm_copytool_action hsma)
251 {
252         struct coordinator *cdt = &mti->mti_mdt->mdt_coordinator;
253         struct lu_ucred *uc = mdt_ucred(mti);
254         struct md_attr *ma = &mti->mti_attr;
255         const __u64 *mask;
256         int rc;
257         ENTRY;
258
259         if (hsma != HSMA_RESTORE && mdt_rdonly(mti->mti_exp))
260                 RETURN(-EROFS);
261
262         if (md_capable(uc, CFS_CAP_SYS_ADMIN))
263                 RETURN(0);
264
265         ma->ma_need = MA_INODE;
266         rc = mdt_attr_get_complex(mti, obj, ma);
267         if (rc < 0)
268                 RETURN(rc);
269
270         if (uc->uc_fsuid == ma->ma_attr.la_uid)
271                 mask = &cdt->cdt_user_request_mask;
272         else if (lustre_in_group_p(uc, ma->ma_attr.la_gid))
273                 mask = &cdt->cdt_group_request_mask;
274         else
275                 mask = &cdt->cdt_other_request_mask;
276
277         if (!(0 <= hsma && hsma < 8 * sizeof(*mask)))
278                 RETURN(-EINVAL);
279
280         RETURN(*mask & (1UL << hsma) ? 0 : -EPERM);
281 }
282
283 /*
284  * Coordinator external API
285  */
286
287 /**
288  * register a list of requests
289  * \param mti [IN]
290  * \param hal [IN] list of requests
291  * \retval 0 success
292  * \retval -ve failure
293  * in case of restore, caller must hold layout lock
294  */
295 int mdt_hsm_add_actions(struct mdt_thread_info *mti,
296                         struct hsm_action_list *hal)
297 {
298         struct mdt_device       *mdt = mti->mti_mdt;
299         struct coordinator      *cdt = &mdt->mdt_coordinator;
300         struct hsm_action_item  *hai;
301         struct mdt_object       *obj = NULL;
302         int                      rc = 0, i;
303         struct md_hsm            mh;
304         bool                     is_restore = false;
305         __u64                    compound_id;
306         ENTRY;
307
308         /* no coordinator started, so we cannot serve requests */
309         if (cdt->cdt_state == CDT_STOPPED)
310                 RETURN(-EAGAIN);
311
312         if (!hal_is_sane(hal))
313                 RETURN(-EINVAL);
314
315         compound_id = atomic_inc_return(&cdt->cdt_compound_id);
316
317         /* search for compatible request, if found hai_cookie is set
318          * to the request cookie
319          * it is also used to set the cookie for cancel request by FID
320          */
321         rc = hsm_find_compatible(mti->mti_env, mdt, hal);
322         if (rc)
323                 GOTO(out, rc);
324
325         hai = hai_first(hal);
326         for (i = 0; i < hal->hal_count; i++, hai = hai_next(hai)) {
327                 int archive_id;
328                 __u64 flags;
329
330                 /* default archive number is the one explicitly specified */
331                 archive_id = hal->hal_archive_id;
332                 flags = hal->hal_flags;
333
334                 /* by default, data FID is same as Lustre FID */
335                 /* the volatile data FID will be created by copy tool and
336                  * send from the agent through the progress call */
337                 hai->hai_dfid = hai->hai_fid;
338
339                 /* done here to manage first and redundant requests cases */
340                 if (hai->hai_action == HSMA_RESTORE)
341                         is_restore = true;
342
343                 /* test result of hsm_find_compatible()
344                  * if request redundant or cancel of nothing
345                  * do not record
346                  */
347                 /* redundant case */
348                 if (hai->hai_action != HSMA_CANCEL && hai->hai_cookie != 0)
349                         continue;
350                 /* cancel nothing case */
351                 if (hai->hai_action == HSMA_CANCEL && hai->hai_cookie == 0)
352                         continue;
353
354                 /* new request or cancel request
355                  * we search for HSM status flags to check for compatibility
356                  * if restore, we take the layout lock
357                  */
358
359                 /* Get HSM attributes and check permissions. */
360                 obj = mdt_hsm_get_md_hsm(mti, &hai->hai_fid, &mh);
361                 if (IS_ERR(obj)) {
362                         /* In case of REMOVE and CANCEL a Lustre file
363                          * is not mandatory, but restrict this
364                          * exception to admins. */
365                         if (md_capable(mdt_ucred(mti), CFS_CAP_SYS_ADMIN) &&
366                             (hai->hai_action == HSMA_REMOVE ||
367                              hai->hai_action == HSMA_CANCEL))
368                                 goto record;
369                         else
370                                 GOTO(out, rc = PTR_ERR(obj));
371                 }
372
373                 rc = hsm_action_permission(mti, obj, hai->hai_action);
374                 mdt_object_put(mti->mti_env, obj);
375
376                 if (rc < 0)
377                         GOTO(out, rc);
378
379                 /* if action is cancel, also no need to check */
380                 if (hai->hai_action == HSMA_CANCEL)
381                         goto record;
382
383                 /* Check if an action is needed, compare request
384                  * and HSM flags status */
385                 if (!hsm_action_is_needed(hai, archive_id, flags, &mh))
386                         continue;
387
388                 /* Check if file request is compatible with HSM flags status
389                  * and stop at first incompatible
390                  */
391                 if (!mdt_hsm_is_action_compat(hai, archive_id, flags, &mh))
392                         GOTO(out, rc = -EPERM);
393
394                 /* for cancel archive number is taken from canceled request
395                  * for other request, we take from lma if not specified,
396                  * or we use the default if none found in lma
397                  * this works also for archive because the default value is 0
398                  * /!\ there is a side effect: in case of restore on multiple
399                  * files which are in different backend, the initial compound
400                  * request will be split in multiple requests because we cannot
401                  * warranty an agent can serve any combinaison of archive
402                  * backend
403                  */
404                 if (hai->hai_action != HSMA_CANCEL && archive_id == 0) {
405                         if (mh.mh_arch_id != 0)
406                                 archive_id = mh.mh_arch_id;
407                         else
408                                 archive_id = cdt->cdt_default_archive_id;
409                 }
410
411                 /* if restore, take an exclusive lock on layout */
412                 if (hai->hai_action == HSMA_RESTORE) {
413                         struct cdt_restore_handle *crh;
414
415                         /* in V1 only whole file is supported. */
416                         if (hai->hai_extent.offset != 0)
417                                 GOTO(out, rc = -EPROTO);
418
419                         OBD_SLAB_ALLOC_PTR(crh, mdt_hsm_cdt_kmem);
420                         if (crh == NULL)
421                                 GOTO(out, rc = -ENOMEM);
422
423                         crh->crh_fid = hai->hai_fid;
424                         /* in V1 only whole file is supported. However the
425                          * restore may be due to truncate. */
426                         crh->crh_extent.start = 0;
427                         crh->crh_extent.end = hai->hai_extent.length;
428
429                         mdt_lock_reg_init(&crh->crh_lh, LCK_EX);
430                         obj = mdt_object_find_lock(mti, &crh->crh_fid,
431                                                    &crh->crh_lh,
432                                                    MDS_INODELOCK_LAYOUT);
433                         if (IS_ERR(obj)) {
434                                 rc = PTR_ERR(obj);
435                                 CERROR("%s: cannot take layout lock for "
436                                        DFID": rc = %d\n", mdt_obd_name(mdt),
437                                        PFID(&crh->crh_fid), rc);
438                                 OBD_SLAB_FREE_PTR(crh, mdt_hsm_cdt_kmem);
439                                 GOTO(out, rc);
440                         }
441
442                         /* we choose to not keep a keep a reference
443                          * on the object during the restore time which can be
444                          * very long */
445                         mdt_object_put(mti->mti_env, obj);
446
447                         mutex_lock(&cdt->cdt_restore_lock);
448                         list_add_tail(&crh->crh_list, &cdt->cdt_restore_hdl);
449                         mutex_unlock(&cdt->cdt_restore_lock);
450                 }
451 record:
452                 /* record request */
453                 rc = mdt_agent_record_add(mti->mti_env, mdt, compound_id,
454                                           archive_id, flags, hai);
455                 if (rc)
456                         GOTO(out, rc);
457         }
458         if (is_restore &&
459             (cdt->cdt_policy & CDT_NONBLOCKING_RESTORE))
460                 rc = -ENODATA;
461         else
462                 rc = 0;
463
464         GOTO(out, rc);
465 out:
466         /* if work has been added, wake up coordinator */
467         if (rc == 0 || rc == -ENODATA)
468                 mdt_hsm_cdt_wakeup(mdt);
469
470         return rc;
471 }
472
473 /**
474  * check if a restore is running on a FID
475  * this is redundant with mdt_hsm_coordinator_get_running()
476  * but as it can be called frequently when getting attr
477  * we make an optimized/simpler version only for a FID
478  * \param mti [IN]
479  * \param fid [IN] file FID
480  * \retval boolean
481  */
482 bool mdt_hsm_restore_is_running(struct mdt_thread_info *mti,
483                                 const struct lu_fid *fid)
484 {
485         struct mdt_device               *mdt = mti->mti_mdt;
486         struct coordinator              *cdt = &mdt->mdt_coordinator;
487         struct cdt_restore_handle       *crh;
488         bool                             rc = false;
489         ENTRY;
490
491         if (!fid_is_sane(fid))
492                 RETURN(rc);
493
494         mutex_lock(&cdt->cdt_restore_lock);
495         list_for_each_entry(crh, &cdt->cdt_restore_hdl, crh_list) {
496                 if (lu_fid_eq(&crh->crh_fid, fid)) {
497                         rc = true;
498                         break;
499                 }
500         }
501         mutex_unlock(&cdt->cdt_restore_lock);
502         RETURN(rc);
503 }
504
505 /**
506  * get registered action on a FID list
507  * \param mti [IN]
508  * \param hal [IN/OUT] requests
509  * \retval 0 success
510  * \retval -ve failure
511  */
512 int mdt_hsm_get_actions(struct mdt_thread_info *mti,
513                         struct hsm_action_list *hal)
514 {
515         struct mdt_device       *mdt = mti->mti_mdt;
516         struct coordinator      *cdt = &mdt->mdt_coordinator;
517         struct hsm_action_item  *hai;
518         int                      i, rc;
519         ENTRY;
520
521         hai = hai_first(hal);
522         for (i = 0; i < hal->hal_count; i++, hai = hai_next(hai)) {
523                 hai->hai_action = HSMA_NONE;
524                 if (!fid_is_sane(&hai->hai_fid))
525                         RETURN(-EINVAL);
526         }
527
528         /* 1st we search in recorded requests */
529         rc = hsm_find_compatible(mti->mti_env, mdt, hal);
530         /* if llog file is not created, no action is recorded */
531         if (rc == -ENOENT)
532                 RETURN(0);
533
534         if (rc)
535                 RETURN(rc);
536
537         /* 2nd we search if the request are running
538          * cookie is cleared to tell to caller, the request is
539          * waiting
540          * we could in place use the record status, but in the future
541          * we may want do give back dynamic informations on the
542          * running request
543          */
544         hai = hai_first(hal);
545         for (i = 0; i < hal->hal_count; i++, hai = hai_next(hai)) {
546                 struct cdt_agent_req *car;
547
548                 car = mdt_cdt_find_request(cdt, hai->hai_cookie);
549                 if (car == NULL) {
550                         hai->hai_cookie = 0;
551                 } else {
552                         __u64 data_moved;
553
554                         mdt_cdt_get_work_done(car, &data_moved);
555                         /* this is just to give the volume of data moved
556                          * it means data_moved data have been moved from the
557                          * original request but we do not know which one
558                          */
559                         hai->hai_extent.length = data_moved;
560                         mdt_cdt_put_request(car);
561                 }
562         }
563
564         RETURN(0);
565 }