4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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.
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
23 * (C) Copyright 2012 Commissariat a l'energie atomique et aux energies
26 * Copyright (c) 2014, 2017, Intel Corporation.
29 * lustre/mdt/mdt_hsm_cdt_requests.c
31 * Lustre HSM Coordinator
33 * Author: Jacques-Charles Lafoucriere <jacques-charles.lafoucriere@cea.fr>
34 * Author: Aurelien Degremont <aurelien.degremont@cea.fr>
37 #define DEBUG_SUBSYSTEM S_MDS
39 #include <libcfs/libcfs.h>
40 #include <libcfs/libcfs_hash.h>
41 #include <obd_support.h>
42 #include <lprocfs_status.h>
43 #include <linux/interval_tree_generic.h>
44 #include "mdt_internal.h"
47 cdt_request_cookie_hash(struct cfs_hash *hs, const void *key, unsigned int mask)
49 return cfs_hash_djb2_hash(key, sizeof(u64), mask);
52 static void *cdt_request_cookie_object(struct hlist_node *hnode)
54 return hlist_entry(hnode, struct cdt_agent_req, car_cookie_hash);
57 static void *cdt_request_cookie_key(struct hlist_node *hnode)
59 struct cdt_agent_req *car = cdt_request_cookie_object(hnode);
61 return &car->car_hai->hai_cookie;
64 static int cdt_request_cookie_keycmp(const void *key, struct hlist_node *hnode)
66 const u64 *cookie2 = cdt_request_cookie_key(hnode);
68 return *(u64 *)key == *cookie2;
72 cdt_request_cookie_get(struct cfs_hash *hs, struct hlist_node *hnode)
74 struct cdt_agent_req *car = cdt_request_cookie_object(hnode);
76 mdt_cdt_get_request(car);
80 cdt_request_cookie_put(struct cfs_hash *hs, struct hlist_node *hnode)
82 struct cdt_agent_req *car = cdt_request_cookie_object(hnode);
84 mdt_cdt_put_request(car);
87 struct cfs_hash_ops cdt_request_cookie_hash_ops = {
88 .hs_hash = cdt_request_cookie_hash,
89 .hs_key = cdt_request_cookie_key,
90 .hs_keycmp = cdt_request_cookie_keycmp,
91 .hs_object = cdt_request_cookie_object,
92 .hs_get = cdt_request_cookie_get,
93 .hs_put_locked = cdt_request_cookie_put,
98 * \param cdt [IN] coordinator
100 void dump_requests(char *prefix, struct coordinator *cdt)
102 struct cdt_agent_req *car;
104 down_read(&cdt->cdt_request_lock);
105 list_for_each_entry(car, &cdt->cdt_request_list, car_request_list) {
106 CDEBUG(D_HSM, "%s fid="DFID" dfid="DFID
108 " action=%s archive#=%d flags=%#llx"
109 " extent=%#llx-%#llx"
110 " gid=%#llx refcount=%d canceled=%d\n",
111 prefix, PFID(&car->car_hai->hai_fid),
112 PFID(&car->car_hai->hai_dfid),
113 car->car_hai->hai_cookie,
114 hsm_copytool_action2name(car->car_hai->hai_action),
115 car->car_archive_id, car->car_flags,
116 car->car_hai->hai_extent.offset,
117 car->car_hai->hai_extent.length,
118 car->car_hai->hai_gid,
119 atomic_read(&car->car_refcount),
122 up_read(&cdt->cdt_request_lock);
125 /* Interval tree to track reported progress.
126 * Intervals stored are non-overlapping and non-adjacent.
127 * When a new interval is added, all intervals that might overlap
128 * or be adjacent are first removed, with any extra length added to
131 struct progress_node {
134 __u64 pn_subtree_last;
135 struct rb_node pn_rb;
138 #define START(node) ((node)->pn_offset)
139 #define LAST(node) ((node)->pn_end)
141 INTERVAL_TREE_DEFINE(struct progress_node, pn_rb, __u64, pn_subtree_last,
142 START, LAST, static, progress)
144 #define progress_first(root) rb_entry_safe(interval_tree_first(root), \
145 struct progress_node, pn_rb)
148 * free the interval tree associated to a request
150 static void mdt_cdt_free_request_tree(struct cdt_req_progress *crp)
152 struct progress_node *node;
155 while ((node = progress_first(&crp->crp_root)) != NULL) {
156 progress_remove(node, &crp->crp_root);
164 * update data moved information during a request
166 static int hsm_update_work(struct cdt_req_progress *crp,
167 const struct hsm_extent *extent)
169 struct progress_node *node;
170 struct progress_node *overlap;
175 end = extent->offset + extent->length - 1;
176 if (end < extent->offset)
182 node->pn_offset = extent->offset;
185 spin_lock(&crp->crp_lock);
186 total = crp->crp_total;
187 /* Search just before and just after the target interval
188 * to find intervals that would be adjacent. Remove them
189 * too and add their extra length to 'node'.
191 while ((overlap = progress_iter_first(&crp->crp_root,
192 (node->pn_offset == 0 ?
193 0 : node->pn_offset - 1),
194 (node->pn_end == LUSTRE_EOF ?
195 LUSTRE_EOF : node->pn_end + 1)))
197 node->pn_offset = min(node->pn_offset, overlap->pn_offset);
198 node->pn_end = max(node->pn_end, overlap->pn_end);
199 progress_remove(overlap, &crp->crp_root);
200 total -= overlap->pn_end - overlap->pn_offset + 1;
201 OBD_FREE_PTR(overlap);
203 progress_insert(node, &crp->crp_root);
204 total += node->pn_end - node->pn_offset + 1;
205 crp->crp_total = total;
206 spin_unlock(&crp->crp_lock);
211 * init the interval tree associated to a request
213 static void mdt_cdt_init_request_tree(struct cdt_req_progress *crp)
215 spin_lock_init(&crp->crp_lock);
216 crp->crp_root = INTERVAL_TREE_ROOT;
218 /* Silence a warning about unused function */
219 progress_iter_next(NULL, 0, 0);
222 /** Allocate/init an agent request and its sub-structures.
224 * \param archive_id [IN]
228 * \retval car [OUT] success valid structure
231 struct cdt_agent_req *mdt_cdt_alloc_request(__u32 archive_id, __u64 flags,
232 struct obd_uuid *uuid,
233 struct hsm_action_item *hai)
235 struct cdt_agent_req *car;
238 OBD_SLAB_ALLOC_PTR(car, mdt_hsm_car_kmem);
240 RETURN(ERR_PTR(-ENOMEM));
242 atomic_set(&car->car_refcount, 1);
243 car->car_archive_id = archive_id;
244 car->car_flags = flags;
245 car->car_canceled = 0;
246 car->car_req_start = ktime_get_real_seconds();
247 car->car_req_update = car->car_req_start;
248 car->car_uuid = *uuid;
249 OBD_ALLOC(car->car_hai, hai->hai_len);
250 if (car->car_hai == NULL) {
251 OBD_SLAB_FREE_PTR(car, mdt_hsm_car_kmem);
252 RETURN(ERR_PTR(-ENOMEM));
254 memcpy(car->car_hai, hai, hai->hai_len);
255 mdt_cdt_init_request_tree(&car->car_progress);
261 * Free an agent request and its sub-structures.
263 * \param car [IN] Request to be freed.
265 void mdt_cdt_free_request(struct cdt_agent_req *car)
267 mdt_cdt_free_request_tree(&car->car_progress);
268 OBD_FREE(car->car_hai, car->car_hai->hai_len);
269 OBD_SLAB_FREE_PTR(car, mdt_hsm_car_kmem);
273 * inc refcount of a request
274 * \param car [IN] request
276 void mdt_cdt_get_request(struct cdt_agent_req *car)
278 atomic_inc(&car->car_refcount);
282 * dec refcount of a request
283 * free if no more refcount
284 * \param car [IN] request
286 void mdt_cdt_put_request(struct cdt_agent_req *car)
288 LASSERT(atomic_read(&car->car_refcount) > 0);
289 if (atomic_dec_and_test(&car->car_refcount))
290 mdt_cdt_free_request(car);
294 * add a request to the list
295 * \param cdt [IN] coordinator
296 * \param car [IN] request
298 * \retval -ve failure
300 int mdt_cdt_add_request(struct coordinator *cdt, struct cdt_agent_req *car)
305 /* cancel requests are not kept in memory */
306 LASSERT(car->car_hai->hai_action != HSMA_CANCEL);
308 down_write(&cdt->cdt_request_lock);
310 rc = cfs_hash_add_unique(cdt->cdt_request_cookie_hash,
311 &car->car_hai->hai_cookie,
312 &car->car_cookie_hash);
314 up_write(&cdt->cdt_request_lock);
318 list_add_tail(&car->car_request_list, &cdt->cdt_request_list);
320 up_write(&cdt->cdt_request_lock);
322 mdt_hsm_agent_update_statistics(cdt, 0, 0, 1, &car->car_uuid);
324 switch (car->car_hai->hai_action) {
326 atomic_inc(&cdt->cdt_archive_count);
329 atomic_inc(&cdt->cdt_restore_count);
332 atomic_inc(&cdt->cdt_remove_count);
335 atomic_inc(&cdt->cdt_request_count);
341 * find request in the list by cookie or by fid
342 * \param cdt [IN] coordinator
343 * \param cookie [IN] request cookie
344 * \param fid [IN] fid
345 * \retval request pointer or NULL if not found
347 struct cdt_agent_req *mdt_cdt_find_request(struct coordinator *cdt, u64 cookie)
349 struct cdt_agent_req *car;
352 down_read(&cdt->cdt_request_lock);
353 car = cfs_hash_lookup(cdt->cdt_request_cookie_hash, &cookie);
354 up_read(&cdt->cdt_request_lock);
360 * remove request from the list
361 * \param cdt [IN] coordinator
362 * \param cookie [IN] request cookie
363 * \retval request pointer
365 int mdt_cdt_remove_request(struct coordinator *cdt, __u64 cookie)
367 struct cdt_agent_req *car;
370 down_write(&cdt->cdt_request_lock);
371 car = cfs_hash_del_key(cdt->cdt_request_cookie_hash, &cookie);
373 up_write(&cdt->cdt_request_lock);
377 list_del(&car->car_request_list);
378 up_write(&cdt->cdt_request_lock);
380 switch (car->car_hai->hai_action) {
382 atomic_dec(&cdt->cdt_archive_count);
385 atomic_dec(&cdt->cdt_restore_count);
388 atomic_dec(&cdt->cdt_remove_count);
392 /* Drop reference from cdt_request_list. */
393 mdt_cdt_put_request(car);
395 LASSERT(atomic_read(&cdt->cdt_request_count) >= 1);
396 if (atomic_dec_and_test(&cdt->cdt_request_count)) {
397 /* request count is empty, nudge coordinator for more work */
398 cdt->cdt_wakeup_coordinator = true;
399 wake_up_interruptible(&cdt->cdt_waitq);
406 * update a request in the list
407 * on success, add a ref to the request returned
408 * \param cdt [IN] coordinator
409 * \param pgs [IN] progression (cookie + extent + err)
410 * \retval request pointer
411 * \retval -ve failure
413 struct cdt_agent_req *mdt_cdt_update_request(struct coordinator *cdt,
414 const struct hsm_progress_kernel *pgs)
416 struct cdt_agent_req *car;
420 car = mdt_cdt_find_request(cdt, pgs->hpk_cookie);
422 RETURN(ERR_PTR(-ENOENT));
424 car->car_req_update = ktime_get_real_seconds();
426 /* update data move progress done by copy tool */
427 if (car->car_hai->hai_action != HSMA_REMOVE && pgs->hpk_errval == 0 &&
428 pgs->hpk_extent.length != 0) {
429 rc = hsm_update_work(&car->car_progress, &pgs->hpk_extent);
431 mdt_cdt_put_request(car);
436 if (pgs->hpk_flags & HP_FLAG_COMPLETED) {
437 if (pgs->hpk_errval != 0)
438 mdt_hsm_agent_update_statistics(cdt, 0, 1, 0,
441 mdt_hsm_agent_update_statistics(cdt, 1, 0, 0,
448 * seq_file method called to start access to /proc file
450 static void *mdt_hsm_active_requests_proc_start(struct seq_file *s, loff_t *p)
452 struct mdt_device *mdt = s->private;
453 struct coordinator *cdt = &mdt->mdt_coordinator;
454 struct list_head *pos;
458 down_read(&cdt->cdt_request_lock);
460 if (list_empty(&cdt->cdt_request_list))
464 RETURN(SEQ_START_TOKEN);
467 list_for_each(pos, &cdt->cdt_request_list) {
476 * seq_file method called to get next item
477 * just returns NULL at eof
479 static void *mdt_hsm_active_requests_proc_next(struct seq_file *s, void *v,
482 struct mdt_device *mdt = s->private;
483 struct coordinator *cdt = &mdt->mdt_coordinator;
484 struct list_head *pos = v;
487 if (pos == SEQ_START_TOKEN)
488 pos = cdt->cdt_request_list.next;
493 if (pos != &cdt->cdt_request_list)
500 * display request data
502 static int mdt_hsm_active_requests_proc_show(struct seq_file *s, void *v)
504 struct list_head *pos = v;
505 struct cdt_agent_req *car;
509 if (pos == SEQ_START_TOKEN)
512 car = list_entry(pos, struct cdt_agent_req, car_request_list);
514 seq_printf(s, "fid="DFID" dfid="DFID
515 " compound/cookie=%#llx/%#llx"
516 " action=%s archive#=%d flags=%#llx"
517 " extent=%#llx-%#llx gid=%#llx"
518 " data=[%s] canceled=%d uuid=%s done=%llu\n",
519 PFID(&car->car_hai->hai_fid),
520 PFID(&car->car_hai->hai_dfid),
521 0ULL /* compound_id */, car->car_hai->hai_cookie,
522 hsm_copytool_action2name(car->car_hai->hai_action),
523 car->car_archive_id, car->car_flags,
524 car->car_hai->hai_extent.offset,
525 car->car_hai->hai_extent.length,
526 car->car_hai->hai_gid,
527 hai_dump_data_field(car->car_hai, buf, sizeof(buf)),
528 car->car_canceled, obd_uuid2str(&car->car_uuid),
529 car->car_progress.crp_total);
534 * seq_file method called to stop access to /proc file
536 static void mdt_hsm_active_requests_proc_stop(struct seq_file *s, void *v)
538 struct mdt_device *mdt = s->private;
539 struct coordinator *cdt = &mdt->mdt_coordinator;
542 up_read(&cdt->cdt_request_lock);
547 /* hsm agent list proc functions */
548 static const struct seq_operations mdt_hsm_active_requests_proc_ops = {
549 .start = mdt_hsm_active_requests_proc_start,
550 .next = mdt_hsm_active_requests_proc_next,
551 .show = mdt_hsm_active_requests_proc_show,
552 .stop = mdt_hsm_active_requests_proc_stop,
556 * public function called at open of /proc file to get
559 static int ldebugfs_open_hsm_active_requests(struct inode *inode,
566 rc = seq_open(file, &mdt_hsm_active_requests_proc_ops);
570 s = file->private_data;
571 s->private = inode->i_private;
576 /* methods to access hsm request list */
577 const struct file_operations mdt_hsm_active_requests_fops = {
578 .owner = THIS_MODULE,
579 .open = ldebugfs_open_hsm_active_requests,
582 .release = seq_release,