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