Whamcloud - gitweb
LU-14521 flr: delete mirror without volatile file
[fs/lustre-release.git] / lustre / mdt / mdt_hsm_cdt_requests.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) 2014, 2017, Intel Corporation.
27  */
28 /*
29  * lustre/mdt/mdt_hsm_cdt_requests.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 <libcfs/libcfs.h>
40 #include <libcfs/libcfs_hash.h>
41 #include <obd_support.h>
42 #include <lprocfs_status.h>
43 #include <linux/interval_tree_generic.h>
44 #include "mdt_internal.h"
45
46 static unsigned int
47 cdt_request_cookie_hash(struct cfs_hash *hs, const void *key, unsigned int mask)
48 {
49         return cfs_hash_djb2_hash(key, sizeof(u64), mask);
50 }
51
52 static void *cdt_request_cookie_object(struct hlist_node *hnode)
53 {
54         return hlist_entry(hnode, struct cdt_agent_req, car_cookie_hash);
55 }
56
57 static void *cdt_request_cookie_key(struct hlist_node *hnode)
58 {
59         struct cdt_agent_req *car = cdt_request_cookie_object(hnode);
60
61         return &car->car_hai->hai_cookie;
62 }
63
64 static int cdt_request_cookie_keycmp(const void *key, struct hlist_node *hnode)
65 {
66         const u64 *cookie2 = cdt_request_cookie_key(hnode);
67
68         return *(u64 *)key == *cookie2;
69 }
70
71 static void
72 cdt_request_cookie_get(struct cfs_hash *hs, struct hlist_node *hnode)
73 {
74         struct cdt_agent_req *car = cdt_request_cookie_object(hnode);
75
76         mdt_cdt_get_request(car);
77 }
78
79 static void
80 cdt_request_cookie_put(struct cfs_hash *hs, struct hlist_node *hnode)
81 {
82         struct cdt_agent_req *car = cdt_request_cookie_object(hnode);
83
84         mdt_cdt_put_request(car);
85 }
86
87 struct cfs_hash_ops cdt_request_cookie_hash_ops = {
88         .hs_hash        = cdt_request_cookie_hash,
89         .hs_key         = cdt_request_cookie_key,
90         .hs_keycmp      = cdt_request_cookie_keycmp,
91         .hs_object      = cdt_request_cookie_object,
92         .hs_get         = cdt_request_cookie_get,
93         .hs_put_locked  = cdt_request_cookie_put,
94 };
95
96 /**
97  * dump requests list
98  * \param cdt [IN] coordinator
99  */
100 void dump_requests(char *prefix, struct coordinator *cdt)
101 {
102         struct cdt_agent_req    *car;
103
104         down_read(&cdt->cdt_request_lock);
105         list_for_each_entry(car, &cdt->cdt_request_list, car_request_list) {
106                 CDEBUG(D_HSM, "%s fid="DFID" dfid="DFID
107                        " cookie=%#llx"
108                        " action=%s archive#=%d flags=%#llx"
109                        " extent=%#llx-%#llx"
110                        " gid=%#llx refcount=%d canceled=%d\n",
111                        prefix, PFID(&car->car_hai->hai_fid),
112                        PFID(&car->car_hai->hai_dfid),
113                        car->car_hai->hai_cookie,
114                        hsm_copytool_action2name(car->car_hai->hai_action),
115                        car->car_archive_id, car->car_flags,
116                        car->car_hai->hai_extent.offset,
117                        car->car_hai->hai_extent.length,
118                        car->car_hai->hai_gid,
119                        atomic_read(&car->car_refcount),
120                        car->car_canceled);
121         }
122         up_read(&cdt->cdt_request_lock);
123 }
124
125 /* Interval tree to track reported progress.
126  * Intervals stored are non-overlapping and non-adjacent.
127  * When a new interval is added, all intervals that might overlap
128  * or be adjacent are first removed, with any extra length added to
129  * the new interval.
130  */
131 struct progress_node {
132         __u64           pn_offset;
133         __u64           pn_end;
134         __u64           pn_subtree_last;
135         struct rb_node  pn_rb;
136 };
137
138 #define START(node) ((node)->pn_offset)
139 #define LAST(node) ((node)->pn_end)
140
141 INTERVAL_TREE_DEFINE(struct progress_node, pn_rb, __u64, pn_subtree_last,
142                      START, LAST, static, progress)
143
144 #define progress_first(root) rb_entry_safe(interval_tree_first(root),   \
145                                            struct progress_node, pn_rb)
146
147 /*
148  * free the interval tree associated to a request
149  */
150 static void mdt_cdt_free_request_tree(struct cdt_req_progress *crp)
151 {
152         struct progress_node *node;
153         ENTRY;
154
155         while ((node = progress_first(&crp->crp_root)) != NULL) {
156                 progress_remove(node, &crp->crp_root);
157                 OBD_FREE_PTR(node);
158         }
159
160         EXIT;
161 }
162
163 /**
164  * update data moved information during a request
165  */
166 static int hsm_update_work(struct cdt_req_progress *crp,
167                            const struct hsm_extent *extent)
168 {
169         struct progress_node *node;
170         struct progress_node *overlap;
171         __u64 end;
172         __u64 total;
173         ENTRY;
174
175         end = extent->offset + extent->length - 1;
176         if (end < extent->offset)
177                 RETURN(-EINVAL);
178
179         OBD_ALLOC_PTR(node);
180         if (!node)
181                 RETURN(-ENOMEM);
182         node->pn_offset = extent->offset;
183         node->pn_end = end;
184
185         spin_lock(&crp->crp_lock);
186         total = crp->crp_total;
187         /* Search just before and just after the target interval
188          * to find intervals that would be adjacent.  Remove them
189          * too and add their extra length to 'node'.
190          */
191         while ((overlap = progress_iter_first(&crp->crp_root,
192                                               (node->pn_offset == 0 ?
193                                                0 : node->pn_offset - 1),
194                                               (node->pn_end == LUSTRE_EOF ?
195                                                LUSTRE_EOF : node->pn_end + 1)))
196                != NULL) {
197                 node->pn_offset = min(node->pn_offset, overlap->pn_offset);
198                 node->pn_end = max(node->pn_end, overlap->pn_end);
199                 progress_remove(overlap, &crp->crp_root);
200                 total -= overlap->pn_end - overlap->pn_offset + 1;
201                 OBD_FREE_PTR(overlap);
202         }
203         progress_insert(node, &crp->crp_root);
204         total += node->pn_end - node->pn_offset + 1;
205         crp->crp_total = total;
206         spin_unlock(&crp->crp_lock);
207         RETURN(0);
208 }
209
210 /**
211  * init the interval tree associated to a request
212  */
213 static void mdt_cdt_init_request_tree(struct cdt_req_progress *crp)
214 {
215         spin_lock_init(&crp->crp_lock);
216         crp->crp_root = INTERVAL_TREE_ROOT;
217         if (0)
218                 /* Silence a warning about unused function */
219                 progress_iter_next(NULL, 0, 0);
220 }
221
222 /** Allocate/init an agent request and its sub-structures.
223  *
224  * \param archive_id [IN]
225  * \param flags [IN]
226  * \param uuid [IN]
227  * \param hai [IN]
228  * \retval car [OUT] success valid structure
229  * \retval car [OUT]
230  */
231 struct cdt_agent_req *mdt_cdt_alloc_request(__u32 archive_id, __u64 flags,
232                                             struct obd_uuid *uuid,
233                                             struct hsm_action_item *hai)
234 {
235         struct cdt_agent_req *car;
236         ENTRY;
237
238         OBD_SLAB_ALLOC_PTR(car, mdt_hsm_car_kmem);
239         if (car == NULL)
240                 RETURN(ERR_PTR(-ENOMEM));
241
242         atomic_set(&car->car_refcount, 1);
243         car->car_archive_id = archive_id;
244         car->car_flags = flags;
245         car->car_canceled = 0;
246         car->car_req_start = ktime_get_real_seconds();
247         car->car_req_update = car->car_req_start;
248         car->car_uuid = *uuid;
249         OBD_ALLOC(car->car_hai, hai->hai_len);
250         if (car->car_hai == NULL) {
251                 OBD_SLAB_FREE_PTR(car, mdt_hsm_car_kmem);
252                 RETURN(ERR_PTR(-ENOMEM));
253         }
254         memcpy(car->car_hai, hai, hai->hai_len);
255         mdt_cdt_init_request_tree(&car->car_progress);
256
257         RETURN(car);
258 }
259
260 /**
261  * Free an agent request and its sub-structures.
262  *
263  * \param car [IN]  Request to be freed.
264  */
265 void mdt_cdt_free_request(struct cdt_agent_req *car)
266 {
267         mdt_cdt_free_request_tree(&car->car_progress);
268         OBD_FREE(car->car_hai, car->car_hai->hai_len);
269         OBD_SLAB_FREE_PTR(car, mdt_hsm_car_kmem);
270 }
271
272 /**
273  * inc refcount of a request
274  * \param car [IN] request
275  */
276 void mdt_cdt_get_request(struct cdt_agent_req *car)
277 {
278         atomic_inc(&car->car_refcount);
279 }
280
281 /**
282  * dec refcount of a request
283  * free if no more refcount
284  * \param car [IN] request
285  */
286 void mdt_cdt_put_request(struct cdt_agent_req *car)
287 {
288         LASSERT(atomic_read(&car->car_refcount) > 0);
289         if (atomic_dec_and_test(&car->car_refcount))
290                 mdt_cdt_free_request(car);
291 }
292
293 /**
294  * add a request to the list
295  * \param cdt [IN] coordinator
296  * \param car [IN] request
297  * \retval 0 success
298  * \retval -ve failure
299  */
300 int mdt_cdt_add_request(struct coordinator *cdt, struct cdt_agent_req *car)
301 {
302         int rc;
303         ENTRY;
304
305         /* cancel requests are not kept in memory */
306         LASSERT(car->car_hai->hai_action != HSMA_CANCEL);
307
308         down_write(&cdt->cdt_request_lock);
309
310         rc = cfs_hash_add_unique(cdt->cdt_request_cookie_hash,
311                                  &car->car_hai->hai_cookie,
312                                  &car->car_cookie_hash);
313         if (rc < 0) {
314                 up_write(&cdt->cdt_request_lock);
315                 RETURN(-EEXIST);
316         }
317
318         list_add_tail(&car->car_request_list, &cdt->cdt_request_list);
319
320         up_write(&cdt->cdt_request_lock);
321
322         mdt_hsm_agent_update_statistics(cdt, 0, 0, 1, &car->car_uuid);
323
324         switch (car->car_hai->hai_action) {
325         case HSMA_ARCHIVE:
326                 atomic_inc(&cdt->cdt_archive_count);
327                 break;
328         case HSMA_RESTORE:
329                 atomic_inc(&cdt->cdt_restore_count);
330                 break;
331         case HSMA_REMOVE:
332                 atomic_inc(&cdt->cdt_remove_count);
333                 break;
334         }
335         atomic_inc(&cdt->cdt_request_count);
336
337         RETURN(0);
338 }
339
340 /**
341  * find request in the list by cookie or by fid
342  * \param cdt [IN] coordinator
343  * \param cookie [IN] request cookie
344  * \param fid [IN] fid
345  * \retval request pointer or NULL if not found
346  */
347 struct cdt_agent_req *mdt_cdt_find_request(struct coordinator *cdt, u64 cookie)
348 {
349         struct cdt_agent_req    *car;
350         ENTRY;
351
352         down_read(&cdt->cdt_request_lock);
353         car = cfs_hash_lookup(cdt->cdt_request_cookie_hash, &cookie);
354         up_read(&cdt->cdt_request_lock);
355
356         RETURN(car);
357 }
358
359 /**
360  * remove request from the list
361  * \param cdt [IN] coordinator
362  * \param cookie [IN] request cookie
363  * \retval request pointer
364  */
365 int mdt_cdt_remove_request(struct coordinator *cdt, __u64 cookie)
366 {
367         struct cdt_agent_req *car;
368         ENTRY;
369
370         down_write(&cdt->cdt_request_lock);
371         car = cfs_hash_del_key(cdt->cdt_request_cookie_hash, &cookie);
372         if (car == NULL) {
373                 up_write(&cdt->cdt_request_lock);
374                 RETURN(-ENOENT);
375         }
376
377         list_del(&car->car_request_list);
378         up_write(&cdt->cdt_request_lock);
379
380         switch (car->car_hai->hai_action) {
381         case HSMA_ARCHIVE:
382                 atomic_dec(&cdt->cdt_archive_count);
383                 break;
384         case HSMA_RESTORE:
385                 atomic_dec(&cdt->cdt_restore_count);
386                 break;
387         case HSMA_REMOVE:
388                 atomic_dec(&cdt->cdt_remove_count);
389                 break;
390         }
391
392         /* Drop reference from cdt_request_list. */
393         mdt_cdt_put_request(car);
394
395         LASSERT(atomic_read(&cdt->cdt_request_count) >= 1);
396         if (atomic_dec_and_test(&cdt->cdt_request_count)) {
397                 /* request count is empty, nudge coordinator for more work */
398                 cdt->cdt_wakeup_coordinator = true;
399                 wake_up_interruptible(&cdt->cdt_waitq);
400         }
401
402         RETURN(0);
403 }
404
405 /**
406  * update a request in the list
407  * on success, add a ref to the request returned
408  * \param cdt [IN] coordinator
409  * \param pgs [IN] progression (cookie + extent + err)
410  * \retval request pointer
411  * \retval -ve failure
412  */
413 struct cdt_agent_req *mdt_cdt_update_request(struct coordinator *cdt,
414                                           const struct hsm_progress_kernel *pgs)
415 {
416         struct cdt_agent_req    *car;
417         int                      rc;
418         ENTRY;
419
420         car = mdt_cdt_find_request(cdt, pgs->hpk_cookie);
421         if (car == NULL)
422                 RETURN(ERR_PTR(-ENOENT));
423
424         car->car_req_update = ktime_get_real_seconds();
425
426         /* update data move progress done by copy tool */
427         if (car->car_hai->hai_action != HSMA_REMOVE && pgs->hpk_errval == 0 &&
428             pgs->hpk_extent.length != 0) {
429                 rc = hsm_update_work(&car->car_progress, &pgs->hpk_extent);
430                 if (rc) {
431                         mdt_cdt_put_request(car);
432                         RETURN(ERR_PTR(rc));
433                 }
434         }
435
436         if (pgs->hpk_flags & HP_FLAG_COMPLETED) {
437                 if (pgs->hpk_errval != 0)
438                         mdt_hsm_agent_update_statistics(cdt, 0, 1, 0,
439                                                         &car->car_uuid);
440                 else
441                         mdt_hsm_agent_update_statistics(cdt, 1, 0, 0,
442                                                         &car->car_uuid);
443         }
444         RETURN(car);
445 }
446
447 /**
448  * seq_file method called to start access to /proc file
449  */
450 static void *mdt_hsm_active_requests_proc_start(struct seq_file *s, loff_t *p)
451 {
452         struct mdt_device       *mdt = s->private;
453         struct coordinator      *cdt = &mdt->mdt_coordinator;
454         struct list_head        *pos;
455         loff_t                   i;
456         ENTRY;
457
458         down_read(&cdt->cdt_request_lock);
459
460         if (list_empty(&cdt->cdt_request_list))
461                 RETURN(NULL);
462
463         if (*p == 0)
464                 RETURN(SEQ_START_TOKEN);
465
466         i = 0;
467         list_for_each(pos, &cdt->cdt_request_list) {
468                 i++;
469                 if (i >= *p)
470                         RETURN(pos);
471         }
472         RETURN(NULL);
473 }
474
475 /**
476  * seq_file method called to get next item
477  * just returns NULL at eof
478  */
479 static void *mdt_hsm_active_requests_proc_next(struct seq_file *s, void *v,
480                                                loff_t *p)
481 {
482         struct mdt_device       *mdt = s->private;
483         struct coordinator      *cdt = &mdt->mdt_coordinator;
484         struct list_head        *pos = v;
485         ENTRY;
486
487         if (pos == SEQ_START_TOKEN)
488                 pos = cdt->cdt_request_list.next;
489         else
490                 pos = pos->next;
491
492         (*p)++;
493         if (pos != &cdt->cdt_request_list)
494                 RETURN(pos);
495         else
496                 RETURN(NULL);
497 }
498
499 /**
500  * display request data
501  */
502 static int mdt_hsm_active_requests_proc_show(struct seq_file *s, void *v)
503 {
504         struct list_head        *pos = v;
505         struct cdt_agent_req    *car;
506         char                     buf[12];
507         ENTRY;
508
509         if (pos == SEQ_START_TOKEN)
510                 RETURN(0);
511
512         car = list_entry(pos, struct cdt_agent_req, car_request_list);
513
514         seq_printf(s, "fid="DFID" dfid="DFID
515                    " compound/cookie=%#llx/%#llx"
516                    " action=%s archive#=%d flags=%#llx"
517                    " extent=%#llx-%#llx gid=%#llx"
518                    " data=[%s] canceled=%d uuid=%s done=%llu\n",
519                    PFID(&car->car_hai->hai_fid),
520                    PFID(&car->car_hai->hai_dfid),
521                    0ULL /* compound_id */, car->car_hai->hai_cookie,
522                    hsm_copytool_action2name(car->car_hai->hai_action),
523                    car->car_archive_id, car->car_flags,
524                    car->car_hai->hai_extent.offset,
525                    car->car_hai->hai_extent.length,
526                    car->car_hai->hai_gid,
527                    hai_dump_data_field(car->car_hai, buf, sizeof(buf)),
528                    car->car_canceled, obd_uuid2str(&car->car_uuid),
529                    car->car_progress.crp_total);
530         RETURN(0);
531 }
532
533 /**
534  * seq_file method called to stop access to /proc file
535  */
536 static void mdt_hsm_active_requests_proc_stop(struct seq_file *s, void *v)
537 {
538         struct mdt_device       *mdt = s->private;
539         struct coordinator      *cdt = &mdt->mdt_coordinator;
540         ENTRY;
541
542         up_read(&cdt->cdt_request_lock);
543
544         EXIT;
545 }
546
547 /* hsm agent list proc functions */
548 static const struct seq_operations mdt_hsm_active_requests_proc_ops = {
549         .start          = mdt_hsm_active_requests_proc_start,
550         .next           = mdt_hsm_active_requests_proc_next,
551         .show           = mdt_hsm_active_requests_proc_show,
552         .stop           = mdt_hsm_active_requests_proc_stop,
553 };
554
555 /**
556  * public function called at open of /proc file to get
557  * list of agents
558  */
559 static int ldebugfs_open_hsm_active_requests(struct inode *inode,
560                                              struct file *file)
561 {
562         struct seq_file *s;
563         int              rc;
564         ENTRY;
565
566         rc = seq_open(file, &mdt_hsm_active_requests_proc_ops);
567         if (rc) {
568                 RETURN(rc);
569         }
570         s = file->private_data;
571         s->private = inode->i_private;
572
573         RETURN(rc);
574 }
575
576 /* methods to access hsm request list */
577 const struct file_operations mdt_hsm_active_requests_fops = {
578         .owner          = THIS_MODULE,
579         .open           = ldebugfs_open_hsm_active_requests,
580         .read           = seq_read,
581         .llseek         = seq_lseek,
582         .release        = seq_release,
583 };
584