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