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