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