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