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,
48 const unsigned int bits)
50 return cfs_hash_djb2_hash(key, sizeof(u64), bits);
53 static void *cdt_request_cookie_object(struct hlist_node *hnode)
55 return hlist_entry(hnode, struct cdt_agent_req, car_cookie_hash);
58 static void *cdt_request_cookie_key(struct hlist_node *hnode)
60 struct cdt_agent_req *car = cdt_request_cookie_object(hnode);
62 return &car->car_hai->hai_cookie;
65 static int cdt_request_cookie_keycmp(const void *key, struct hlist_node *hnode)
67 const u64 *cookie2 = cdt_request_cookie_key(hnode);
69 return *(u64 *)key == *cookie2;
73 cdt_request_cookie_get(struct cfs_hash *hs, struct hlist_node *hnode)
75 struct cdt_agent_req *car = cdt_request_cookie_object(hnode);
77 mdt_cdt_get_request(car);
81 cdt_request_cookie_put(struct cfs_hash *hs, struct hlist_node *hnode)
83 struct cdt_agent_req *car = cdt_request_cookie_object(hnode);
85 mdt_cdt_put_request(car);
88 struct cfs_hash_ops cdt_request_cookie_hash_ops = {
89 .hs_hash = cdt_request_cookie_hash,
90 .hs_key = cdt_request_cookie_key,
91 .hs_keycmp = cdt_request_cookie_keycmp,
92 .hs_object = cdt_request_cookie_object,
93 .hs_get = cdt_request_cookie_get,
94 .hs_put_locked = cdt_request_cookie_put,
99 * \param cdt [IN] coordinator
101 void dump_requests(char *prefix, struct coordinator *cdt)
103 struct cdt_agent_req *car;
105 down_read(&cdt->cdt_request_lock);
106 list_for_each_entry(car, &cdt->cdt_request_list, car_request_list) {
107 CDEBUG(D_HSM, "%s fid="DFID" dfid="DFID
109 " action=%s archive#=%d flags=%#llx"
110 " extent=%#llx-%#llx"
111 " gid=%#llx refcount=%d canceled=%d\n",
112 prefix, PFID(&car->car_hai->hai_fid),
113 PFID(&car->car_hai->hai_dfid),
114 car->car_hai->hai_cookie,
115 hsm_copytool_action2name(car->car_hai->hai_action),
116 car->car_archive_id, car->car_flags,
117 car->car_hai->hai_extent.offset,
118 car->car_hai->hai_extent.length,
119 car->car_hai->hai_gid,
120 kref_read(&car->car_refcount),
123 up_read(&cdt->cdt_request_lock);
126 /* Interval tree to track reported progress.
127 * Intervals stored are non-overlapping and non-adjacent.
128 * When a new interval is added, all intervals that might overlap
129 * or be adjacent are first removed, with any extra length added to
132 struct progress_node {
135 __u64 pn_subtree_last;
136 struct rb_node pn_rb;
139 #define START(node) ((node)->pn_offset)
140 #define LAST(node) ((node)->pn_end)
142 INTERVAL_TREE_DEFINE(struct progress_node, pn_rb, __u64, pn_subtree_last,
143 START, LAST, static, progress)
145 #define progress_first(root) rb_entry_safe(interval_tree_first(root), \
146 struct progress_node, pn_rb)
149 * free the interval tree associated to a request
151 static void mdt_cdt_free_request_tree(struct cdt_req_progress *crp)
153 struct progress_node *node;
156 while ((node = progress_first(&crp->crp_root)) != NULL) {
157 progress_remove(node, &crp->crp_root);
165 * update data moved information during a request
167 static int hsm_update_work(struct cdt_req_progress *crp,
168 const struct hsm_extent *extent)
170 struct progress_node *node;
171 struct progress_node *overlap;
176 end = extent->offset + extent->length - 1;
177 if (end < extent->offset)
183 node->pn_offset = extent->offset;
186 spin_lock(&crp->crp_lock);
187 total = crp->crp_total;
188 /* Search just before and just after the target interval
189 * to find intervals that would be adjacent. Remove them
190 * too and add their extra length to 'node'.
192 while ((overlap = progress_iter_first(&crp->crp_root,
193 (node->pn_offset == 0 ?
194 0 : node->pn_offset - 1),
195 (node->pn_end == LUSTRE_EOF ?
196 LUSTRE_EOF : node->pn_end + 1)))
198 node->pn_offset = min(node->pn_offset, overlap->pn_offset);
199 node->pn_end = max(node->pn_end, overlap->pn_end);
200 progress_remove(overlap, &crp->crp_root);
201 total -= overlap->pn_end - overlap->pn_offset + 1;
202 OBD_FREE_PTR(overlap);
204 progress_insert(node, &crp->crp_root);
205 total += node->pn_end - node->pn_offset + 1;
206 crp->crp_total = total;
207 spin_unlock(&crp->crp_lock);
212 * init the interval tree associated to a request
214 static void mdt_cdt_init_request_tree(struct cdt_req_progress *crp)
216 spin_lock_init(&crp->crp_lock);
217 crp->crp_root = INTERVAL_TREE_ROOT;
219 /* Silence a warning about unused function */
220 progress_iter_next(NULL, 0, 0);
223 /** Allocate/init an agent request and its sub-structures.
225 * \param archive_id [IN]
229 * \retval car [OUT] success valid structure
232 struct cdt_agent_req *mdt_cdt_alloc_request(__u32 archive_id, __u64 flags,
233 struct obd_uuid *uuid,
234 struct hsm_action_item *hai)
236 struct cdt_agent_req *car;
239 OBD_SLAB_ALLOC_PTR(car, mdt_hsm_car_kmem);
241 RETURN(ERR_PTR(-ENOMEM));
243 kref_init(&car->car_refcount);
244 car->car_archive_id = archive_id;
245 car->car_flags = flags;
246 car->car_canceled = 0;
247 car->car_req_start = ktime_get_real_seconds();
248 car->car_req_update = car->car_req_start;
249 car->car_uuid = *uuid;
250 OBD_ALLOC(car->car_hai, hai->hai_len);
251 if (car->car_hai == NULL) {
252 OBD_SLAB_FREE_PTR(car, mdt_hsm_car_kmem);
253 RETURN(ERR_PTR(-ENOMEM));
255 memcpy(car->car_hai, hai, hai->hai_len);
256 mdt_cdt_init_request_tree(&car->car_progress);
262 * Free an agent request and its sub-structures.
264 * \param car [IN] Request to be freed.
266 void mdt_cdt_free_request(struct cdt_agent_req *car)
268 mdt_cdt_free_request_tree(&car->car_progress);
269 OBD_FREE(car->car_hai, car->car_hai->hai_len);
270 OBD_SLAB_FREE_PTR(car, mdt_hsm_car_kmem);
274 * inc refcount of a request
275 * \param car [IN] request
277 void mdt_cdt_get_request(struct cdt_agent_req *car)
279 kref_get(&car->car_refcount);
282 void mdt_cdt_put_request_free(struct kref *kref)
284 struct cdt_agent_req *car;
286 car = container_of(kref, struct cdt_agent_req, car_refcount);
287 mdt_cdt_free_request(car);
291 * dec refcount of a request
292 * free if no more refcount
293 * \param car [IN] request
295 void mdt_cdt_put_request(struct cdt_agent_req *car)
297 kref_put(&car->car_refcount, mdt_cdt_put_request_free);
301 * add a request to the list
302 * \param cdt [IN] coordinator
303 * \param car [IN] request
305 * \retval -ve failure
307 int mdt_cdt_add_request(struct coordinator *cdt, struct cdt_agent_req *car)
312 /* cancel requests are not kept in memory */
313 LASSERT(car->car_hai->hai_action != HSMA_CANCEL);
315 down_write(&cdt->cdt_request_lock);
317 rc = cfs_hash_add_unique(cdt->cdt_request_cookie_hash,
318 &car->car_hai->hai_cookie,
319 &car->car_cookie_hash);
321 up_write(&cdt->cdt_request_lock);
325 list_add_tail(&car->car_request_list, &cdt->cdt_request_list);
327 up_write(&cdt->cdt_request_lock);
329 mdt_hsm_agent_update_statistics(cdt, 0, 0, 1, &car->car_uuid);
331 switch (car->car_hai->hai_action) {
333 atomic_inc(&cdt->cdt_archive_count);
336 atomic_inc(&cdt->cdt_restore_count);
339 atomic_inc(&cdt->cdt_remove_count);
342 atomic_inc(&cdt->cdt_request_count);
348 * find request in the list by cookie or by fid
349 * \param cdt [IN] coordinator
350 * \param cookie [IN] request cookie
351 * \param fid [IN] fid
352 * \retval request pointer or NULL if not found
354 struct cdt_agent_req *mdt_cdt_find_request(struct coordinator *cdt, u64 cookie)
356 struct cdt_agent_req *car;
359 down_read(&cdt->cdt_request_lock);
360 car = cfs_hash_lookup(cdt->cdt_request_cookie_hash, &cookie);
361 up_read(&cdt->cdt_request_lock);
367 * remove request from the list
368 * \param cdt [IN] coordinator
369 * \param cookie [IN] request cookie
370 * \retval request pointer
372 int mdt_cdt_remove_request(struct coordinator *cdt, __u64 cookie)
374 struct cdt_agent_req *car;
377 down_write(&cdt->cdt_request_lock);
378 car = cfs_hash_del_key(cdt->cdt_request_cookie_hash, &cookie);
380 up_write(&cdt->cdt_request_lock);
384 list_del(&car->car_request_list);
385 up_write(&cdt->cdt_request_lock);
387 switch (car->car_hai->hai_action) {
389 atomic_dec(&cdt->cdt_archive_count);
392 atomic_dec(&cdt->cdt_restore_count);
395 atomic_dec(&cdt->cdt_remove_count);
399 /* Drop reference from cdt_request_list. */
400 mdt_cdt_put_request(car);
402 LASSERT(atomic_read(&cdt->cdt_request_count) >= 1);
403 if (atomic_dec_and_test(&cdt->cdt_request_count)) {
404 /* request count is empty, nudge coordinator for more work */
405 cdt->cdt_wakeup_coordinator = true;
406 wake_up_interruptible(&cdt->cdt_waitq);
413 * update a request in the list
414 * on success, add a ref to the request returned
415 * \param cdt [IN] coordinator
416 * \param pgs [IN] progression (cookie + extent + err)
417 * \retval request pointer
418 * \retval -ve failure
420 struct cdt_agent_req *mdt_cdt_update_request(struct coordinator *cdt,
421 const struct hsm_progress_kernel *pgs)
423 struct cdt_agent_req *car;
427 car = mdt_cdt_find_request(cdt, pgs->hpk_cookie);
429 RETURN(ERR_PTR(-ENOENT));
431 car->car_req_update = ktime_get_real_seconds();
433 /* update data move progress done by copy tool */
434 if (car->car_hai->hai_action != HSMA_REMOVE && pgs->hpk_errval == 0 &&
435 pgs->hpk_extent.length != 0) {
436 rc = hsm_update_work(&car->car_progress, &pgs->hpk_extent);
438 mdt_cdt_put_request(car);
443 if (pgs->hpk_flags & HP_FLAG_COMPLETED) {
444 if (pgs->hpk_errval != 0)
445 mdt_hsm_agent_update_statistics(cdt, 0, 1, 0,
448 mdt_hsm_agent_update_statistics(cdt, 1, 0, 0,
455 * seq_file method called to start access to /proc file
457 static void *mdt_hsm_active_requests_proc_start(struct seq_file *s, loff_t *p)
459 struct mdt_device *mdt = s->private;
460 struct coordinator *cdt = &mdt->mdt_coordinator;
461 struct list_head *pos;
465 down_read(&cdt->cdt_request_lock);
467 if (list_empty(&cdt->cdt_request_list))
471 RETURN(SEQ_START_TOKEN);
474 list_for_each(pos, &cdt->cdt_request_list) {
483 * seq_file method called to get next item
484 * just returns NULL at eof
486 static void *mdt_hsm_active_requests_proc_next(struct seq_file *s, void *v,
489 struct mdt_device *mdt = s->private;
490 struct coordinator *cdt = &mdt->mdt_coordinator;
491 struct list_head *pos = v;
494 if (pos == SEQ_START_TOKEN)
495 pos = cdt->cdt_request_list.next;
500 if (pos != &cdt->cdt_request_list)
507 * display request data
509 static int mdt_hsm_active_requests_proc_show(struct seq_file *s, void *v)
511 struct list_head *pos = v;
512 struct cdt_agent_req *car;
516 if (pos == SEQ_START_TOKEN)
519 car = list_entry(pos, struct cdt_agent_req, car_request_list);
521 seq_printf(s, "fid="DFID" dfid="DFID
522 " compound/cookie=%#llx/%#llx"
523 " action=%s archive#=%d flags=%#llx"
524 " extent=%#llx-%#llx gid=%#llx"
525 " data=[%s] canceled=%d uuid=%s done=%llu\n",
526 PFID(&car->car_hai->hai_fid),
527 PFID(&car->car_hai->hai_dfid),
528 0ULL /* compound_id */, car->car_hai->hai_cookie,
529 hsm_copytool_action2name(car->car_hai->hai_action),
530 car->car_archive_id, car->car_flags,
531 car->car_hai->hai_extent.offset,
532 car->car_hai->hai_extent.length,
533 car->car_hai->hai_gid,
534 hai_dump_data_field(car->car_hai, buf, sizeof(buf)),
535 car->car_canceled, obd_uuid2str(&car->car_uuid),
536 car->car_progress.crp_total);
541 * seq_file method called to stop access to /proc file
543 static void mdt_hsm_active_requests_proc_stop(struct seq_file *s, void *v)
545 struct mdt_device *mdt = s->private;
546 struct coordinator *cdt = &mdt->mdt_coordinator;
549 up_read(&cdt->cdt_request_lock);
554 /* hsm agent list proc functions */
555 static const struct seq_operations mdt_hsm_active_requests_proc_ops = {
556 .start = mdt_hsm_active_requests_proc_start,
557 .next = mdt_hsm_active_requests_proc_next,
558 .show = mdt_hsm_active_requests_proc_show,
559 .stop = mdt_hsm_active_requests_proc_stop,
563 * public function called at open of /proc file to get
566 static int ldebugfs_open_hsm_active_requests(struct inode *inode,
573 rc = seq_open(file, &mdt_hsm_active_requests_proc_ops);
577 s = file->private_data;
578 s->private = inode->i_private;
583 /* methods to access hsm request list */
584 const struct file_operations mdt_hsm_active_requests_fops = {
585 .owner = THIS_MODULE,
586 .open = ldebugfs_open_hsm_active_requests,
589 .release = seq_release,