Viewing: mdt_hsm_cdt_requests.c
// SPDX-License-Identifier: GPL-2.0
/*
* (C) Copyright 2012 Commissariat a l'energie atomique et aux energies
* alternatives
*
* Copyright (c) 2014, 2017, Intel Corporation.
*/
/*
* Lustre HSM Coordinator
*
* Author: Jacques-Charles Lafoucriere <jacques-charles.lafoucriere@cea.fr>
* Author: Aurelien Degremont <aurelien.degremont@cea.fr>
*/
#define DEBUG_SUBSYSTEM S_MDS
#include <obd_support.h>
#include <lprocfs_status.h>
#include <linux/interval_tree_generic.h>
#include "mdt_internal.h"
static unsigned int
cdt_request_cookie_hash(struct cfs_hash *hs, const void *key,
const unsigned int bits)
{
return cfs_hash_djb2_hash(key, sizeof(u64), bits);
}
static void *cdt_request_cookie_object(struct hlist_node *hnode)
{
return hlist_entry(hnode, struct cdt_agent_req, car_cookie_hash);
}
static void *cdt_request_cookie_key(struct hlist_node *hnode)
{
struct cdt_agent_req *car = cdt_request_cookie_object(hnode);
return &car->car_hai.hai_cookie;
}
static int cdt_request_cookie_keycmp(const void *key, struct hlist_node *hnode)
{
const u64 *cookie2 = cdt_request_cookie_key(hnode);
return *(u64 *)key == *cookie2;
}
static void
cdt_request_cookie_get(struct cfs_hash *hs, struct hlist_node *hnode)
{
struct cdt_agent_req *car = cdt_request_cookie_object(hnode);
mdt_cdt_get_request(car);
}
static void
cdt_request_cookie_put(struct cfs_hash *hs, struct hlist_node *hnode)
{
struct cdt_agent_req *car = cdt_request_cookie_object(hnode);
mdt_cdt_put_request(car);
}
struct cfs_hash_ops cdt_request_cookie_hash_ops = {
.hs_hash = cdt_request_cookie_hash,
.hs_key = cdt_request_cookie_key,
.hs_keycmp = cdt_request_cookie_keycmp,
.hs_object = cdt_request_cookie_object,
.hs_get = cdt_request_cookie_get,
.hs_put_locked = cdt_request_cookie_put,
};
/*
* dump_requests() - dump requests list
* @prefix: Unique string to append to output
* @cdt: coordinator
*/
void __maybe_unused dump_requests(char *prefix, struct coordinator *cdt)
{
struct cdt_agent_req *car;
down_read(&cdt->cdt_request_lock);
list_for_each_entry(car, &cdt->cdt_request_list, car_request_list) {
CDEBUG(D_HSM, "%s fid="DFID" dfid="DFID
" cookie=%#llx"
" action=%s archive#=%d flags=%#llx"
" extent=%#llx-%#llx"
" gid=%#llx refcount=%d canceled=%d\n",
prefix, PFID(&car->car_hai.hai_fid),
PFID(&car->car_hai.hai_dfid),
car->car_hai.hai_cookie,
hsm_copytool_action2name(car->car_hai.hai_action),
car->car_archive_id, car->car_flags,
car->car_hai.hai_extent.offset,
car->car_hai.hai_extent.length,
car->car_hai.hai_gid,
kref_read(&car->car_refcount),
car->car_cancel ? 1 : 0);
}
up_read(&cdt->cdt_request_lock);
}
/* Interval tree to track reported progress.
* Intervals stored are non-overlapping and non-adjacent.
* When a new interval is added, all intervals that might overlap
* or be adjacent are first removed, with any extra length added to
* the new interval.
*/
struct progress_node {
__u64 pn_offset;
__u64 pn_end;
__u64 pn_subtree_last;
struct rb_node pn_rb;
};
#define START(node) ((node)->pn_offset)
#define LAST(node) ((node)->pn_end)
INTERVAL_TREE_DEFINE(struct progress_node, pn_rb, __u64, pn_subtree_last,
START, LAST, static, progress)
#define progress_first(root) rb_entry_safe(rb_first_cached(root), \
struct progress_node, pn_rb)
/*
* free the interval tree associated to a request
*/
static void mdt_cdt_free_request_tree(struct cdt_req_progress *crp)
{
struct progress_node *node;
ENTRY;
while ((node = progress_first(&crp->crp_root)) != NULL) {
progress_remove(node, &crp->crp_root);
OBD_FREE_PTR(node);
}
EXIT;
}
/**
* hsm_update_work() - update data moved information during a request
* @crp: Pointer to cdt_req_progress (request progress)
* @extent: byte range to operate on
*
* Return:
* * %0 on success
* * %negative on failure
*/
static int hsm_update_work(struct cdt_req_progress *crp,
const struct hsm_extent *extent)
{
struct progress_node *node;
struct progress_node *overlap;
__u64 end;
__u64 total;
ENTRY;
end = extent->offset + extent->length - 1;
if (end < extent->offset)
RETURN(-EINVAL);
OBD_ALLOC_PTR(node);
if (!node)
RETURN(-ENOMEM);
node->pn_offset = extent->offset;
node->pn_end = end;
spin_lock(&crp->crp_lock);
total = crp->crp_total;
/* Search just before and just after the target interval
* to find intervals that would be adjacent. Remove them
* too and add their extra length to 'node'.
*/
while ((overlap = progress_iter_first(&crp->crp_root,
(node->pn_offset == 0 ?
0 : node->pn_offset - 1),
(node->pn_end == LUSTRE_EOF ?
LUSTRE_EOF : node->pn_end + 1)))
!= NULL) {
node->pn_offset = min(node->pn_offset, overlap->pn_offset);
node->pn_end = max(node->pn_end, overlap->pn_end);
progress_remove(overlap, &crp->crp_root);
total -= overlap->pn_end - overlap->pn_offset + 1;
OBD_FREE_PTR(overlap);
}
progress_insert(node, &crp->crp_root);
total += node->pn_end - node->pn_offset + 1;
crp->crp_total = total;
spin_unlock(&crp->crp_lock);
RETURN(0);
}
/*
* mdt_cdt_init_request_tree() - init the interval tree associated to a request
* @crp: Pointer to cdt_req_progress (request progress)
*/
static void mdt_cdt_init_request_tree(struct cdt_req_progress *crp)
{
spin_lock_init(&crp->crp_lock);
crp->crp_root = RB_ROOT_CACHED;
if (0)
/* Silence a warning about unused function */
progress_iter_next(NULL, 0, 0);
}
static inline int hmmr_size(int rec_size)
{
return __ALIGN_KERNEL(offsetof(struct hsm_mem_req_rec, mr_rec) +
rec_size, 8);
}
/**
* mdt_cdt_alloc_request() - Allocate/init an agent request and sub-structures.
* @uuid: Device name (identified via uuid)
* @rec: Request record
*
* Return:
* * Pointer to struct cdt_agent_req on success
* * %negative on failure
*/
struct cdt_agent_req *mdt_cdt_alloc_request(struct obd_uuid *uuid,
struct llog_agent_req_rec *rec)
{
struct cdt_agent_req *car;
ENTRY;
OBD_SLAB_ALLOC_PTR(car, mdt_hsm_car_kmem);
if (car == NULL)
RETURN(ERR_PTR(-ENOMEM));
kref_init(&car->car_refcount);
car->car_uuid = *uuid;
OBD_ALLOC(car->car_hmm, hmmr_size(rec->arr_hdr.lrh_len));
if (car->car_hmm == NULL) {
OBD_SLAB_FREE_PTR(car, mdt_hsm_car_kmem);
RETURN(ERR_PTR(-ENOMEM));
}
memcpy(&car->car_hmm->mr_rec, rec, rec->arr_hdr.lrh_len);
mdt_cdt_init_request_tree(&car->car_progress);
car->car_cancel = NULL;
RETURN(car);
}
/**
* mdt_cdt_free_request() - Free an agent request and its sub-structures.
* @car: Request to be freed.
*/
void mdt_cdt_free_request(struct cdt_agent_req *car)
{
mdt_cdt_free_request_tree(&car->car_progress);
OBD_FREE(car->car_hmm, hmmr_size(car->car_hmm->mr_rec.arr_hdr.lrh_len));
OBD_SLAB_FREE_PTR(car, mdt_hsm_car_kmem);
}
/**
* mdt_cdt_get_request() - inc refcount of a request
* @car: request
*/
void mdt_cdt_get_request(struct cdt_agent_req *car)
{
kref_get(&car->car_refcount);
}
static void mdt_cdt_put_request_free(struct kref *kref)
{
struct cdt_agent_req *car;
car = container_of(kref, struct cdt_agent_req, car_refcount);
mdt_cdt_free_request(car);
}
/**
* mdt_cdt_put_request() - dec refcount of a request free if no more refcount
* @car: request
*/
void mdt_cdt_put_request(struct cdt_agent_req *car)
{
kref_put(&car->car_refcount, mdt_cdt_put_request_free);
}
/**
* mdt_cdt_add_request() - add a request to the list
* @cdt: coordinator
* @car: request
*
* Return:
* * %0 success
* * %negative failure
*/
int mdt_cdt_add_request(struct coordinator *cdt, struct cdt_agent_req *car)
{
int rc;
ENTRY;
/* cancel requests are not kept in memory */
LASSERT(car->car_hai.hai_action != HSMA_CANCEL);
down_write(&cdt->cdt_request_lock);
rc = cfs_hash_add_unique(cdt->cdt_request_cookie_hash,
&car->car_hai.hai_cookie,
&car->car_cookie_hash);
if (rc < 0) {
up_write(&cdt->cdt_request_lock);
RETURN(-EEXIST);
}
list_add_tail(&car->car_request_list, &cdt->cdt_request_list);
mdt_cdt_get_request(car);
up_write(&cdt->cdt_request_lock);
mdt_hsm_agent_update_statistics(cdt, 0, 0, 1, &car->car_uuid);
switch (car->car_hai.hai_action) {
case HSMA_ARCHIVE:
atomic_inc(&cdt->cdt_archive_count);
break;
case HSMA_RESTORE:
atomic_inc(&cdt->cdt_restore_count);
break;
case HSMA_REMOVE:
atomic_inc(&cdt->cdt_remove_count);
break;
}
atomic_inc(&cdt->cdt_request_count);
RETURN(0);
}
/**
* mdt_cdt_find_request() - find request in the list by cookie or by fid
* @cdt: coordinator
* @cookie: request cookie
*
* Return request pointer or %NULL if not found
*/
struct cdt_agent_req *mdt_cdt_find_request(struct coordinator *cdt, u64 cookie)
{
struct cdt_agent_req *car;
ENTRY;
down_read(&cdt->cdt_request_lock);
car = cfs_hash_lookup(cdt->cdt_request_cookie_hash, &cookie);
up_read(&cdt->cdt_request_lock);
RETURN(car);
}
/**
* mdt_cdt_remove_request() - remove request from the list
* @cdt: coordinator
* @cookie: request cookie
*
* Return:
* * %request pointer on success
* * %negative on failure
*/
int mdt_cdt_remove_request(struct coordinator *cdt, __u64 cookie)
{
struct cdt_agent_req *car;
ENTRY;
down_write(&cdt->cdt_request_lock);
car = cfs_hash_del_key(cdt->cdt_request_cookie_hash, &cookie);
if (car == NULL) {
up_write(&cdt->cdt_request_lock);
RETURN(-ENOENT);
}
list_del(&car->car_request_list);
up_write(&cdt->cdt_request_lock);
switch (car->car_hai.hai_action) {
case HSMA_ARCHIVE:
atomic_dec(&cdt->cdt_archive_count);
break;
case HSMA_RESTORE:
atomic_dec(&cdt->cdt_restore_count);
break;
case HSMA_REMOVE:
atomic_dec(&cdt->cdt_remove_count);
break;
}
if (car->car_cancel) {
mdt_cdt_put_request(car->car_cancel);
/* ref from mdt_hsm_add_hsr()->mdt_cdt_find_request() */
mdt_cdt_put_request(car);
car->car_cancel = NULL;
}
/* Drop reference from cdt_request_list. */
mdt_cdt_put_request(car);
LASSERT(atomic_read(&cdt->cdt_request_count) >= 1);
if (atomic_dec_and_test(&cdt->cdt_request_count)) {
/* request count is empty, nudge coordinator for more work */
cdt->cdt_wakeup_coordinator = true;
wake_up_interruptible(&cdt->cdt_waitq);
}
RETURN(0);
}
/**
* mdt_cdt_update_request() - update a request in the list
* @cdt: coordinator
* @pgs: progression (cookie + extent + err)
*
* update a request in the list on success, add a ref to the request returned
*
* Return:
* * %request pointer
* * %negative on failure
*/
struct cdt_agent_req *mdt_cdt_update_request(struct coordinator *cdt,
const struct hsm_progress_kernel *pgs)
{
struct cdt_agent_req *car;
int rc;
ENTRY;
car = mdt_cdt_find_request(cdt, pgs->hpk_cookie);
if (car == NULL)
RETURN(ERR_PTR(-ENOENT));
car->car_req_update = ktime_get_real_seconds();
/* update data move progress done by copy tool */
if (car->car_hai.hai_action != HSMA_REMOVE && pgs->hpk_errval == 0 &&
pgs->hpk_extent.length != 0) {
rc = hsm_update_work(&car->car_progress, &pgs->hpk_extent);
if (rc) {
mdt_cdt_put_request(car);
RETURN(ERR_PTR(rc));
}
}
if (pgs->hpk_flags & HP_FLAG_COMPLETED) {
if (pgs->hpk_errval != 0)
mdt_hsm_agent_update_statistics(cdt, 0, 1, 0,
&car->car_uuid);
else
mdt_hsm_agent_update_statistics(cdt, 1, 0, 0,
&car->car_uuid);
}
RETURN(car);
}
/*
* mdt_hsm_active_requests_proc_start() - method called to start access to /proc
* @s: pointer to the seq_file struct
* @p: A pointer to an offset
*
* seq_file method called to start access to /proc file
*/
static void *mdt_hsm_active_requests_proc_start(struct seq_file *s, loff_t *p)
{
struct mdt_device *mdt = s->private;
struct coordinator *cdt = &mdt->mdt_coordinator;
struct list_head *pos;
loff_t i;
ENTRY;
down_read(&cdt->cdt_request_lock);
if (list_empty(&cdt->cdt_request_list))
RETURN(NULL);
if (*p == 0)
RETURN(SEQ_START_TOKEN);
i = 0;
list_for_each(pos, &cdt->cdt_request_list) {
i++;
if (i >= *p)
RETURN(pos);
}
RETURN(NULL);
}
/*
* mdt_hsm_active_requests_proc_next() - seq_file method called to get next item
* @s: pointer to the seq_file struct
* @v: pointer to the current item
* @p: A pointer to an offset
*
* seq_file method called to get next item just returns NULL at eof
*/
static void *mdt_hsm_active_requests_proc_next(struct seq_file *s, void *v,
loff_t *p)
{
struct mdt_device *mdt = s->private;
struct coordinator *cdt = &mdt->mdt_coordinator;
struct list_head *pos = v;
ENTRY;
if (pos == SEQ_START_TOKEN)
pos = cdt->cdt_request_list.next;
else
pos = pos->next;
(*p)++;
if (pos != &cdt->cdt_request_list)
RETURN(pos);
else
RETURN(NULL);
}
/*
* display request data
*/
static int mdt_hsm_active_requests_proc_show(struct seq_file *s, void *v)
{
struct list_head *pos = v;
struct cdt_agent_req *car;
char buf[12];
ENTRY;
if (pos == SEQ_START_TOKEN)
RETURN(0);
car = list_entry(pos, struct cdt_agent_req, car_request_list);
seq_printf(s, "fid="DFID" dfid="DFID
" compound/cookie=%#llx/%#llx"
" action=%s archive#=%d flags=%#llx"
" extent=%#llx-%#llx gid=%#llx"
" data=[%s] canceled=%d uuid=%s done=%llu\n",
PFID(&car->car_hai.hai_fid),
PFID(&car->car_hai.hai_dfid),
0ULL /* compound_id */, car->car_hai.hai_cookie,
hsm_copytool_action2name(car->car_hai.hai_action),
car->car_archive_id, car->car_flags,
car->car_hai.hai_extent.offset,
car->car_hai.hai_extent.length,
car->car_hai.hai_gid,
hai_dump_data_field(&car->car_hai, buf, sizeof(buf)),
car->car_cancel ? 1 : 0, obd_uuid2str(&car->car_uuid),
car->car_progress.crp_total);
RETURN(0);
}
/*
* seq_file method called to stop access to /proc file
*/
static void mdt_hsm_active_requests_proc_stop(struct seq_file *s, void *v)
{
struct mdt_device *mdt = s->private;
struct coordinator *cdt = &mdt->mdt_coordinator;
ENTRY;
up_read(&cdt->cdt_request_lock);
EXIT;
}
/* hsm agent list proc functions */
static const struct seq_operations mdt_hsm_active_requests_proc_ops = {
.start = mdt_hsm_active_requests_proc_start,
.next = mdt_hsm_active_requests_proc_next,
.show = mdt_hsm_active_requests_proc_show,
.stop = mdt_hsm_active_requests_proc_stop,
};
/*
* ldebugfs_open_hsm_active_requests() - Public function called at open of /proc
* @inode: Inode representing the file being opened
* @file: Pointer to open file
*
* public function called at open of /proc file to get list of agents
*
* Return:
* * %0 on success
* * %negative on failure
*/
static int ldebugfs_open_hsm_active_requests(struct inode *inode,
struct file *file)
{
struct seq_file *s;
int rc;
ENTRY;
rc = seq_open(file, &mdt_hsm_active_requests_proc_ops);
if (rc) {
RETURN(rc);
}
s = file->private_data;
s->private = inode->i_private;
RETURN(rc);
}
/* methods to access hsm request list */
const struct file_operations mdt_hsm_active_requests_fops = {
.owner = THIS_MODULE,
.open = ldebugfs_open_hsm_active_requests,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};