Whamcloud - gitweb
9d2016457d0968e43e67a169e47eb2ed38f1e9d2
[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 mdt_cdt_update_work(struct cdt_req_progress *crp,
157                                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_ALLOC_PTR(car);
243         if (car == NULL)
244                 RETURN(ERR_PTR(-ENOMEM));
245
246         cfs_atomic_set(&car->car_refcount, 0);
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_FREE_PTR(car);
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_FREE_PTR(car);
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         if (cfs_atomic_dec_and_test(&car->car_refcount))
294                 mdt_cdt_free_request(car);
295 }
296
297 /**
298  * find request in the list by cookie or by fid
299  * lock cdt_request_lock needs to be hold by caller
300  * \param cdt [IN] coordinator
301  * \param cookie [IN] request cookie
302  * \param fid [IN] fid
303  * \retval request pointer
304  */
305 static struct cdt_agent_req *cdt_find_request_nolock(struct coordinator *cdt,
306                                                      __u64 cookie,
307                                                      const struct lu_fid *fid)
308 {
309         cfs_list_t              *pos;
310         struct cdt_agent_req    *car;
311         ENTRY;
312
313         if (cfs_list_empty(&cdt->cdt_requests))
314                 goto notfound;
315
316         cfs_list_for_each(pos, &cdt->cdt_requests) {
317                 car = cfs_list_entry(pos, struct cdt_agent_req,
318                                      car_request_list);
319                 if ((car->car_hai->hai_cookie == cookie) ||
320                     ((fid != NULL) && lu_fid_eq(fid, &car->car_hai->hai_fid))) {
321                         mdt_cdt_get_request(car);
322                         RETURN(car);
323                 }
324         }
325
326 notfound:
327         RETURN(ERR_PTR(-ENOENT));
328 }
329
330 /**
331  * add a request to the list
332  * \param cdt [IN] coordinator
333  * \param car [IN] request
334  * \retval 0 success
335  * \retval -ve failure
336  */
337 int mdt_cdt_add_request(struct coordinator *cdt, struct cdt_agent_req *new_car)
338 {
339         struct cdt_agent_req    *car;
340         ENTRY;
341
342         /* cancel requests are not kept in memory */
343         LASSERT(new_car->car_hai->hai_action != HSMA_CANCEL);
344
345         down_write(&cdt->cdt_request_lock);
346
347         car = cdt_find_request_nolock(cdt, new_car->car_hai->hai_cookie, NULL);
348         if (!IS_ERR(car)) {
349                 mdt_cdt_put_request(car);
350                 up_write(&cdt->cdt_request_lock);
351                 RETURN(-EEXIST);
352         }
353
354         mdt_cdt_get_request(new_car);
355         cfs_list_add_tail(&new_car->car_request_list, &cdt->cdt_requests);
356         up_write(&cdt->cdt_request_lock);
357
358         mdt_hsm_agent_update_statistics(cdt, 0, 0, 1, &new_car->car_uuid);
359
360         down(&cdt->cdt_counter_lock);
361         cdt->cdt_request_count++;
362         up(&cdt->cdt_counter_lock);
363
364         RETURN(0);
365 }
366
367 /**
368  * find request in the list by cookie or by fid
369  * \param cdt [IN] coordinator
370  * \param cookie [IN] request cookie
371  * \param fid [IN] fid
372  * \retval request pointer
373  */
374 struct cdt_agent_req *mdt_cdt_find_request(struct coordinator *cdt,
375                                            __u64 cookie,
376                                            const struct lu_fid *fid)
377 {
378         struct cdt_agent_req    *car;
379         ENTRY;
380
381         down_read(&cdt->cdt_request_lock);
382
383         car = cdt_find_request_nolock(cdt, cookie, fid);
384
385         up_read(&cdt->cdt_request_lock);
386
387         RETURN(car);
388 }
389
390 /**
391  * remove request from the list
392  * \param cdt [IN] coordinator
393  * \param cookie [IN] request cookie
394  * \retval request pointer
395  */
396 int mdt_cdt_remove_request(struct coordinator *cdt, __u64 cookie)
397 {
398         struct cdt_agent_req    *car;
399         ENTRY;
400
401         down_write(&cdt->cdt_request_lock);
402
403         car = cdt_find_request_nolock(cdt, cookie, NULL);
404         if (!IS_ERR(car)) {
405                 cfs_list_del(&car->car_request_list);
406                 mdt_cdt_put_request(car);
407                 up_write(&cdt->cdt_request_lock);
408
409                 down(&cdt->cdt_counter_lock);
410                 cdt->cdt_request_count--;
411                 up(&cdt->cdt_counter_lock);
412
413                 RETURN(0);
414         }
415         up_write(&cdt->cdt_request_lock);
416
417         RETURN(-ENOENT);
418 }
419
420 /**
421  * update a request in the list
422  * on success, add a ref to the request returned
423  * \param cdt [IN] coordinator
424  * \param pgs [IN] progression (cookie + extent + err)
425  * \retval request pointer
426  * \retval -ve failure
427  */
428 struct cdt_agent_req *mdt_cdt_update_request(struct coordinator *cdt,
429                                              struct hsm_progress_kernel *pgs)
430 {
431         struct cdt_agent_req    *car;
432         int                      rc;
433         ENTRY;
434
435         car = mdt_cdt_find_request(cdt, pgs->hpk_cookie, NULL);
436         if (IS_ERR(car))
437                 RETURN(car);
438
439         car->car_req_update = cfs_time_current_sec();
440
441         /* update progress done by copy tool */
442         if (pgs->hpk_errval == 0 && pgs->hpk_extent.length != 0) {
443                 rc = mdt_cdt_update_work(&car->car_progress, &pgs->hpk_extent);
444                 if (rc) {
445                         mdt_cdt_put_request(car);
446                         RETURN(ERR_PTR(rc));
447                 }
448         }
449
450         if (pgs->hpk_flags & HP_FLAG_COMPLETED) {
451                 if (pgs->hpk_errval != 0)
452                         mdt_hsm_agent_update_statistics(cdt, 0, 1, 0,
453                                                         &car->car_uuid);
454                 else
455                         mdt_hsm_agent_update_statistics(cdt, 1, 0, 0,
456                                                         &car->car_uuid);
457         }
458         RETURN(car);
459 }
460
461 /**
462  * seq_file method called to start access to /proc file
463  */
464 static void *mdt_hsm_request_proc_start(struct seq_file *s, loff_t *p)
465 {
466         struct mdt_device       *mdt = s->private;
467         struct coordinator      *cdt = &mdt->mdt_coordinator;
468         cfs_list_t              *pos;
469         loff_t                   i;
470         ENTRY;
471
472         down_read(&cdt->cdt_request_lock);
473
474         if (cfs_list_empty(&cdt->cdt_requests))
475                 RETURN(NULL);
476
477         if (*p == 0)
478                 RETURN(SEQ_START_TOKEN);
479
480         i = 0;
481         cfs_list_for_each(pos, &cdt->cdt_requests) {
482                 i++;
483                 if (i >= *p)
484                         RETURN(pos);
485         }
486         RETURN(NULL);
487 }
488
489 /**
490  * seq_file method called to get next item
491  * just returns NULL at eof
492  */
493 static void *mdt_hsm_request_proc_next(struct seq_file *s, void *v, loff_t *p)
494 {
495         struct mdt_device       *mdt = s->private;
496         struct coordinator      *cdt = &mdt->mdt_coordinator;
497         cfs_list_t              *pos = v;
498         ENTRY;
499
500         if (pos == SEQ_START_TOKEN)
501                 pos = cdt->cdt_requests.next;
502         else
503                 pos = pos->next;
504
505         (*p)++;
506         if (pos != &cdt->cdt_requests)
507                 RETURN(pos);
508         else
509                 RETURN(NULL);
510 }
511
512 /**
513  * display request data
514  */
515 static int mdt_hsm_request_proc_show(struct seq_file *s, void *v)
516 {
517         cfs_list_t              *pos = v;
518         struct cdt_agent_req    *car;
519         char                     buf[12];
520         __u64                    data_moved;
521         ENTRY;
522
523         if (pos == SEQ_START_TOKEN)
524                 RETURN(0);
525
526         car = cfs_list_entry(pos, struct cdt_agent_req, car_request_list);
527         mdt_cdt_get_work_done(car, &data_moved);
528
529         seq_printf(s, "fid="DFID" dfid="DFID
530                    " compound/cookie="LPX64"/"LPX64
531                    " action=%s archive#=%d flags="LPX64
532                    " extent="LPX64"-"LPX64" gid="LPX64
533                    " data=[%s] canceled=%d uuid=%s done="LPU64"%%\n",
534                    PFID(&car->car_hai->hai_fid),
535                    PFID(&car->car_hai->hai_dfid),
536                    car->car_compound_id, car->car_hai->hai_cookie,
537                    hsm_copytool_action2name(car->car_hai->hai_action),
538                    car->car_archive_id, car->car_flags,
539                    car->car_hai->hai_extent.offset,
540                    car->car_hai->hai_extent.length,
541                    car->car_hai->hai_gid,
542                    hai_dump_data_field(car->car_hai, buf, sizeof(buf)),
543                    car->car_canceled, obd_uuid2str(&car->car_uuid),
544                    data_moved);
545         RETURN(0);
546 }
547
548 /**
549  * seq_file method called to stop access to /proc file
550  */
551 static void mdt_hsm_request_proc_stop(struct seq_file *s, void *v)
552 {
553         struct mdt_device       *mdt = s->private;
554         struct coordinator      *cdt = &mdt->mdt_coordinator;
555         ENTRY;
556
557         up_read(&cdt->cdt_request_lock);
558
559         EXIT;
560 }
561
562 /* hsm agent list proc functions */
563 static const struct seq_operations mdt_hsm_request_proc_ops = {
564         .start          = mdt_hsm_request_proc_start,
565         .next           = mdt_hsm_request_proc_next,
566         .show           = mdt_hsm_request_proc_show,
567         .stop           = mdt_hsm_request_proc_stop,
568 };
569
570 /**
571  * public function called at open of /proc file to get
572  * list of agents
573  */
574 static int lprocfs_open_hsm_request(struct inode *inode, struct file *file)
575 {
576         struct seq_file *s;
577         int              rc;
578         ENTRY;
579
580         if (LPROCFS_ENTRY_AND_CHECK(PDE(inode)))
581                 RETURN(-ENOENT);
582
583         rc = seq_open(file, &mdt_hsm_request_proc_ops);
584         if (rc) {
585                 LPROCFS_EXIT();
586                 RETURN(rc);
587         }
588         s = file->private_data;
589         s->private = PDE(inode)->data;
590
591         RETURN(rc);
592 }
593
594 /* methods to access hsm request list */
595 const struct file_operations mdt_hsm_request_fops = {
596         .owner          = THIS_MODULE,
597         .open           = lprocfs_open_hsm_request,
598         .read           = seq_read,
599         .llseek         = seq_lseek,
600         .release        = lprocfs_seq_release,
601 };
602