Whamcloud - gitweb
LU-7988 hsm: run HSM coordinator once per second at most
[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, 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 <lustre/lustre_user.h>
43 #include <lprocfs_status.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                        " compound/cookie=%#llx/%#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_compound_id, 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 struct req_interval_data {
126         struct cdt_req_progress *crp;
127         __u64                    done_sz;
128 };
129
130 /**
131  * interval tree cb, used to go through all the tree of extent done
132  */
133 static enum interval_iter req_interval_cb(struct interval_node *node,
134                                           void *args)
135 {
136         struct req_interval_data        *data;
137         ENTRY;
138
139         data = args;
140         data->done_sz += node->in_extent.end - node->in_extent.start;
141         RETURN(INTERVAL_ITER_CONT);
142 }
143
144 /**
145  * scan the interval tree associated to a request
146  * to compute the amount of work done
147  * \param car [IN] request
148  * \param done_sz [OUT] will be set to the size of work done
149  */
150 void mdt_cdt_get_work_done(struct cdt_agent_req *car, __u64 *done_sz)
151 {
152         struct req_interval_data         rid;
153         struct cdt_req_progress         *crp = &car->car_progress;
154
155         mutex_lock(&crp->crp_lock);
156
157         rid.crp = crp;
158         rid.done_sz = 0;
159         interval_iterate(crp->crp_root, req_interval_cb, &rid);
160         *done_sz = rid.done_sz;
161
162         mutex_unlock(&crp->crp_lock);
163 }
164
165 #define NODE_VECTOR_SZ 256
166 /**
167  * free the interval tree associated to a request
168  */
169 static void mdt_cdt_free_request_tree(struct cdt_req_progress *crp)
170 {
171         struct interval_node    *node, *vn;
172         int                      i;
173         ENTRY;
174
175         mutex_lock(&crp->crp_lock);
176
177         if (crp->crp_max == 0)
178                 goto out;
179
180         /* remove all nodes from tree */
181         for (i = 0 ; i < crp->crp_cnt ; i++) {
182                 vn = crp->crp_node[i / NODE_VECTOR_SZ];
183                 node = &vn[i % NODE_VECTOR_SZ];
184                 interval_erase(node, &crp->crp_root);
185         }
186         /* free all sub vectors */
187         for (i = 0 ; i <= crp->crp_max / NODE_VECTOR_SZ ; i++)
188                 OBD_FREE(crp->crp_node[i],
189                          NODE_VECTOR_SZ * sizeof(crp->crp_node[i][0]));
190
191         /* free main vector */
192         OBD_FREE(crp->crp_node,
193                  sizeof(crp->crp_node[0]) *
194                   (crp->crp_max / NODE_VECTOR_SZ + 1));
195
196         crp->crp_cnt = 0;
197         crp->crp_max = 0;
198 out:
199         mutex_unlock(&crp->crp_lock);
200         EXIT;
201 }
202
203 /**
204  * update data moved information during a request
205  */
206 static int hsm_update_work(struct cdt_req_progress *crp,
207                            const struct hsm_extent *extent)
208 {
209         int                       rc, osz, nsz;
210         struct interval_node    **new_vv;
211         struct interval_node     *v, *node;
212         __u64                     end;
213         ENTRY;
214
215         end = extent->offset + extent->length;
216         if (end <= extent->offset)
217                 RETURN(-EINVAL);
218
219         mutex_lock(&crp->crp_lock);
220         /* new node index */
221
222         if (crp->crp_cnt >= crp->crp_max) {
223                 /* no more room */
224                 /* allocate a new vector */
225                 OBD_ALLOC(v, NODE_VECTOR_SZ * sizeof(v[0]));
226                 if (v == NULL)
227                         GOTO(out, rc = -ENOMEM);
228
229                 if (crp->crp_max == 0)
230                         osz = 0;
231                 else
232                         osz = sizeof(new_vv[0]) *
233                               (crp->crp_max / NODE_VECTOR_SZ + 1);
234
235                 nsz = osz + sizeof(new_vv[0]);
236                 /* increase main vector size */
237                 OBD_ALLOC(new_vv, nsz);
238                 if (new_vv == NULL) {
239                         OBD_FREE(v, NODE_VECTOR_SZ * sizeof(v[0]));
240                         GOTO(out, rc = -ENOMEM);
241                 }
242
243                 if (osz == 0) {
244                         crp->crp_max = NODE_VECTOR_SZ - 1;
245                 } else {
246                         memcpy(new_vv, crp->crp_node, osz);
247                         OBD_FREE(crp->crp_node, osz);
248                         crp->crp_max += NODE_VECTOR_SZ;
249                 }
250
251                 crp->crp_node = new_vv;
252                 crp->crp_node[crp->crp_max / NODE_VECTOR_SZ] = v;
253         }
254
255         v = crp->crp_node[crp->crp_cnt / NODE_VECTOR_SZ];
256         node = &v[crp->crp_cnt % NODE_VECTOR_SZ];
257         rc = interval_set(node, extent->offset, end);
258         if (rc)
259                 GOTO(out, rc);
260         /* try to insert, if entry already exist ignore the new one
261          * it can happen if ct sends 2 times the same progress */
262         if (interval_insert(node, &crp->crp_root) == NULL)
263                 crp->crp_cnt++;
264
265         rc = 0;
266 out:
267         mutex_unlock(&crp->crp_lock);
268         RETURN(rc);
269 }
270
271 /**
272  * init the interval tree associated to a request
273  */
274 static void mdt_cdt_init_request_tree(struct cdt_req_progress *crp)
275 {
276         mutex_init(&crp->crp_lock);
277         crp->crp_root = NULL;
278         crp->crp_cnt = 0;
279         crp->crp_max = 0;
280 }
281
282 /** Allocate/init an agent request and its sub-structures.
283  *
284  * \param compound_id [IN]
285  * \param archive_id [IN]
286  * \param flags [IN]
287  * \param uuid [IN]
288  * \param hai [IN]
289  * \retval car [OUT] success valid structure
290  * \retval car [OUT]
291  */
292 struct cdt_agent_req *mdt_cdt_alloc_request(__u64 compound_id, __u32 archive_id,
293                                             __u64 flags, struct obd_uuid *uuid,
294                                             struct hsm_action_item *hai)
295 {
296         struct cdt_agent_req *car;
297         ENTRY;
298
299         OBD_SLAB_ALLOC_PTR(car, mdt_hsm_car_kmem);
300         if (car == NULL)
301                 RETURN(ERR_PTR(-ENOMEM));
302
303         atomic_set(&car->car_refcount, 1);
304         car->car_compound_id = compound_id;
305         car->car_archive_id = archive_id;
306         car->car_flags = flags;
307         car->car_canceled = 0;
308         car->car_req_start = cfs_time_current_sec();
309         car->car_req_update = car->car_req_start;
310         car->car_uuid = *uuid;
311         OBD_ALLOC(car->car_hai, hai->hai_len);
312         if (car->car_hai == NULL) {
313                 OBD_SLAB_FREE_PTR(car, mdt_hsm_car_kmem);
314                 RETURN(ERR_PTR(-ENOMEM));
315         }
316         memcpy(car->car_hai, hai, hai->hai_len);
317         mdt_cdt_init_request_tree(&car->car_progress);
318
319         RETURN(car);
320 }
321
322 /**
323  * Free an agent request and its sub-structures.
324  *
325  * \param car [IN]  Request to be freed.
326  */
327 void mdt_cdt_free_request(struct cdt_agent_req *car)
328 {
329         mdt_cdt_free_request_tree(&car->car_progress);
330         OBD_FREE(car->car_hai, car->car_hai->hai_len);
331         OBD_SLAB_FREE_PTR(car, mdt_hsm_car_kmem);
332 }
333
334 /**
335  * inc refcount of a request
336  * \param car [IN] request
337  */
338 void mdt_cdt_get_request(struct cdt_agent_req *car)
339 {
340         atomic_inc(&car->car_refcount);
341 }
342
343 /**
344  * dec refcount of a request
345  * free if no more refcount
346  * \param car [IN] request
347  */
348 void mdt_cdt_put_request(struct cdt_agent_req *car)
349 {
350         LASSERT(atomic_read(&car->car_refcount) > 0);
351         if (atomic_dec_and_test(&car->car_refcount))
352                 mdt_cdt_free_request(car);
353 }
354
355 /**
356  * add a request to the list
357  * \param cdt [IN] coordinator
358  * \param car [IN] request
359  * \retval 0 success
360  * \retval -ve failure
361  */
362 int mdt_cdt_add_request(struct coordinator *cdt, struct cdt_agent_req *car)
363 {
364         int rc;
365         ENTRY;
366
367         /* cancel requests are not kept in memory */
368         LASSERT(car->car_hai->hai_action != HSMA_CANCEL);
369
370         down_write(&cdt->cdt_request_lock);
371
372         rc = cfs_hash_add_unique(cdt->cdt_request_cookie_hash,
373                                  &car->car_hai->hai_cookie,
374                                  &car->car_cookie_hash);
375         if (rc < 0) {
376                 up_write(&cdt->cdt_request_lock);
377                 RETURN(-EEXIST);
378         }
379
380         list_add_tail(&car->car_request_list, &cdt->cdt_request_list);
381
382         up_write(&cdt->cdt_request_lock);
383
384         mdt_hsm_agent_update_statistics(cdt, 0, 0, 1, &car->car_uuid);
385
386         atomic_inc(&cdt->cdt_request_count);
387
388         RETURN(0);
389 }
390
391 /**
392  * find request in the list by cookie or by fid
393  * \param cdt [IN] coordinator
394  * \param cookie [IN] request cookie
395  * \param fid [IN] fid
396  * \retval request pointer or NULL if not found
397  */
398 struct cdt_agent_req *mdt_cdt_find_request(struct coordinator *cdt, u64 cookie)
399 {
400         struct cdt_agent_req    *car;
401         ENTRY;
402
403         down_read(&cdt->cdt_request_lock);
404         car = cfs_hash_lookup(cdt->cdt_request_cookie_hash, &cookie);
405         up_read(&cdt->cdt_request_lock);
406
407         RETURN(car);
408 }
409
410 /**
411  * remove request from the list
412  * \param cdt [IN] coordinator
413  * \param cookie [IN] request cookie
414  * \retval request pointer
415  */
416 int mdt_cdt_remove_request(struct coordinator *cdt, __u64 cookie)
417 {
418         struct cdt_agent_req *car;
419         ENTRY;
420
421         down_write(&cdt->cdt_request_lock);
422         car = cfs_hash_del_key(cdt->cdt_request_cookie_hash, &cookie);
423         if (car == NULL) {
424                 up_write(&cdt->cdt_request_lock);
425                 RETURN(-ENOENT);
426         }
427
428         list_del(&car->car_request_list);
429         up_write(&cdt->cdt_request_lock);
430
431         /* Drop reference from cdt_request_list. */
432         mdt_cdt_put_request(car);
433
434         LASSERT(atomic_read(&cdt->cdt_request_count) >= 1);
435         atomic_dec(&cdt->cdt_request_count);
436
437         RETURN(0);
438 }
439
440 /**
441  * update a request in the list
442  * on success, add a ref to the request returned
443  * \param cdt [IN] coordinator
444  * \param pgs [IN] progression (cookie + extent + err)
445  * \retval request pointer
446  * \retval -ve failure
447  */
448 struct cdt_agent_req *mdt_cdt_update_request(struct coordinator *cdt,
449                                           const struct hsm_progress_kernel *pgs)
450 {
451         struct cdt_agent_req    *car;
452         int                      rc;
453         ENTRY;
454
455         car = mdt_cdt_find_request(cdt, pgs->hpk_cookie);
456         if (car == NULL)
457                 RETURN(ERR_PTR(-ENOENT));
458
459         car->car_req_update = cfs_time_current_sec();
460
461         /* update data move progress done by copy tool */
462         if (car->car_hai->hai_action != HSMA_REMOVE && pgs->hpk_errval == 0 &&
463             pgs->hpk_extent.length != 0) {
464                 rc = hsm_update_work(&car->car_progress, &pgs->hpk_extent);
465                 if (rc) {
466                         mdt_cdt_put_request(car);
467                         RETURN(ERR_PTR(rc));
468                 }
469         }
470
471         if (pgs->hpk_flags & HP_FLAG_COMPLETED) {
472                 if (pgs->hpk_errval != 0)
473                         mdt_hsm_agent_update_statistics(cdt, 0, 1, 0,
474                                                         &car->car_uuid);
475                 else
476                         mdt_hsm_agent_update_statistics(cdt, 1, 0, 0,
477                                                         &car->car_uuid);
478         }
479         RETURN(car);
480 }
481
482 /**
483  * seq_file method called to start access to /proc file
484  */
485 static void *mdt_hsm_active_requests_proc_start(struct seq_file *s, loff_t *p)
486 {
487         struct mdt_device       *mdt = s->private;
488         struct coordinator      *cdt = &mdt->mdt_coordinator;
489         struct list_head        *pos;
490         loff_t                   i;
491         ENTRY;
492
493         down_read(&cdt->cdt_request_lock);
494
495         if (list_empty(&cdt->cdt_request_list))
496                 RETURN(NULL);
497
498         if (*p == 0)
499                 RETURN(SEQ_START_TOKEN);
500
501         i = 0;
502         list_for_each(pos, &cdt->cdt_request_list) {
503                 i++;
504                 if (i >= *p)
505                         RETURN(pos);
506         }
507         RETURN(NULL);
508 }
509
510 /**
511  * seq_file method called to get next item
512  * just returns NULL at eof
513  */
514 static void *mdt_hsm_active_requests_proc_next(struct seq_file *s, void *v,
515                                                loff_t *p)
516 {
517         struct mdt_device       *mdt = s->private;
518         struct coordinator      *cdt = &mdt->mdt_coordinator;
519         struct list_head        *pos = v;
520         ENTRY;
521
522         if (pos == SEQ_START_TOKEN)
523                 pos = cdt->cdt_request_list.next;
524         else
525                 pos = pos->next;
526
527         (*p)++;
528         if (pos != &cdt->cdt_request_list)
529                 RETURN(pos);
530         else
531                 RETURN(NULL);
532 }
533
534 /**
535  * display request data
536  */
537 static int mdt_hsm_active_requests_proc_show(struct seq_file *s, void *v)
538 {
539         struct list_head        *pos = v;
540         struct cdt_agent_req    *car;
541         char                     buf[12];
542         __u64                    data_moved;
543         ENTRY;
544
545         if (pos == SEQ_START_TOKEN)
546                 RETURN(0);
547
548         car = list_entry(pos, struct cdt_agent_req, car_request_list);
549         mdt_cdt_get_work_done(car, &data_moved);
550
551         seq_printf(s, "fid="DFID" dfid="DFID
552                    " compound/cookie=%#llx/%#llx"
553                    " action=%s archive#=%d flags=%#llx"
554                    " extent=%#llx-%#llx gid=%#llx"
555                    " data=[%s] canceled=%d uuid=%s done=%llu\n",
556                    PFID(&car->car_hai->hai_fid),
557                    PFID(&car->car_hai->hai_dfid),
558                    car->car_compound_id, car->car_hai->hai_cookie,
559                    hsm_copytool_action2name(car->car_hai->hai_action),
560                    car->car_archive_id, car->car_flags,
561                    car->car_hai->hai_extent.offset,
562                    car->car_hai->hai_extent.length,
563                    car->car_hai->hai_gid,
564                    hai_dump_data_field(car->car_hai, buf, sizeof(buf)),
565                    car->car_canceled, obd_uuid2str(&car->car_uuid),
566                    data_moved);
567         RETURN(0);
568 }
569
570 /**
571  * seq_file method called to stop access to /proc file
572  */
573 static void mdt_hsm_active_requests_proc_stop(struct seq_file *s, void *v)
574 {
575         struct mdt_device       *mdt = s->private;
576         struct coordinator      *cdt = &mdt->mdt_coordinator;
577         ENTRY;
578
579         up_read(&cdt->cdt_request_lock);
580
581         EXIT;
582 }
583
584 /* hsm agent list proc functions */
585 static const struct seq_operations mdt_hsm_active_requests_proc_ops = {
586         .start          = mdt_hsm_active_requests_proc_start,
587         .next           = mdt_hsm_active_requests_proc_next,
588         .show           = mdt_hsm_active_requests_proc_show,
589         .stop           = mdt_hsm_active_requests_proc_stop,
590 };
591
592 /**
593  * public function called at open of /proc file to get
594  * list of agents
595  */
596 static int lprocfs_open_hsm_active_requests(struct inode *inode,
597                                             struct file *file)
598 {
599         struct seq_file *s;
600         int              rc;
601         ENTRY;
602
603         rc = seq_open(file, &mdt_hsm_active_requests_proc_ops);
604         if (rc) {
605                 RETURN(rc);
606         }
607         s = file->private_data;
608         s->private = PDE_DATA(inode);
609
610         RETURN(rc);
611 }
612
613 /* methods to access hsm request list */
614 const struct file_operations mdt_hsm_active_requests_fops = {
615         .owner          = THIS_MODULE,
616         .open           = lprocfs_open_hsm_active_requests,
617         .read           = seq_read,
618         .llseek         = seq_lseek,
619         .release        = lprocfs_seq_release,
620 };
621