Viewing: nrs_crr.c
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2013, 2017, Intel Corporation.
*
* Copyright 2012 Xyratex Technology Limited
*/
/*
* Network Request Scheduler (NRS) CRR-N policy
*
* Request ordering in a batched Round-Robin manner over client NIDs
*
* Author: Liang Zhen <liang@whamcloud.com>
* Author: Nikitas Angelinas <nikitas_angelinas@xyratex.com>
*/
#define DEBUG_SUBSYSTEM S_RPC
#include <obd_support.h>
#include <obd_class.h>
#include <lustre_net.h>
#include <lprocfs_status.h>
#include "ptlrpc_internal.h"
/*
* CRR-N policy
* Client Round-Robin scheduling over client NIDs
*/
#define NRS_POL_NAME_CRRN "crrn"
/**
* crrn_req_compare() - Binary heap predicate.
* @e1: the first binheap node to compare
* @e2: the second binheap node to compare
*
* Uses ptlrpc_nrs_request::nr_u::crr::cr_round and
* ptlrpc_nrs_request::nr_u::crr::cr_sequence to compare two binheap nodes and
* produce a binary predicate that shows their relative priority, so that the
* binary heap can perform the necessary sorting operations.
*
* Return:
* * %0 if e1 > e2
* * %1 if e1 <= e2
*/
static int
crrn_req_compare(struct binheap_node *e1, struct binheap_node *e2)
{
struct ptlrpc_nrs_request *nrq1;
struct ptlrpc_nrs_request *nrq2;
nrq1 = container_of(e1, struct ptlrpc_nrs_request, nr_node);
nrq2 = container_of(e2, struct ptlrpc_nrs_request, nr_node);
if (nrq1->nr_u.crr.cr_round < nrq2->nr_u.crr.cr_round)
return 1;
else if (nrq1->nr_u.crr.cr_round > nrq2->nr_u.crr.cr_round)
return 0;
return nrq1->nr_u.crr.cr_sequence < nrq2->nr_u.crr.cr_sequence;
}
static struct binheap_ops nrs_crrn_heap_ops = {
.hop_enter = NULL,
.hop_exit = NULL,
.hop_compare = crrn_req_compare,
};
/**
* nrs_crrn_hashfn() - rhashtable operations for nrs_crrn_net::cn_cli_hash
* @data: pointer to key
* @len: length of key
* @seed: seed value (randomness regulator)
*
* This uses ptlrpc_request::rq_peer.nid (as nid4) as its key, in order to hash
* nrs_crrn_client objects.
*/
static u32 nrs_crrn_hashfn(const void *data, u32 len, u32 seed)
{
const struct lnet_nid *nid = data;
return hash_32(nidhash(nid) ^ seed, 32);
}
static int nrs_crrn_cmpfn(struct rhashtable_compare_arg *arg, const void *obj)
{
const struct nrs_crrn_client *cli = obj;
const struct lnet_nid *nid = arg->key;
return nid_same(nid, &cli->cc_nid) ? 0 : -ESRCH;
}
static const struct rhashtable_params nrs_crrn_hash_params = {
.key_len = sizeof(struct lnet_nid),
.key_offset = offsetof(struct nrs_crrn_client, cc_nid),
.head_offset = offsetof(struct nrs_crrn_client, cc_rhead),
.hashfn = nrs_crrn_hashfn,
.obj_cmpfn = nrs_crrn_cmpfn,
};
static void nrs_crrn_exit(void *vcli, void *data)
{
struct nrs_crrn_client *cli = vcli;
LASSERTF(atomic_read(&cli->cc_ref) == 0,
"Busy CRR-N object from client with NID %s, with %d refs\n",
libcfs_nidstr(&cli->cc_nid), atomic_read(&cli->cc_ref));
OBD_FREE_PTR(cli);
}
/**
* nrs_crrn_start() - Called when a CRR-N policy instance is started.
* @policy: the policy
* @arg: used for passing parameters and information (optional)
*
* Return:
* * %-ENOMEM OOM error
* * %0 success
*/
static int nrs_crrn_start(struct ptlrpc_nrs_policy *policy, char *arg)
{
struct nrs_crrn_net *net;
int rc = 0;
ENTRY;
OBD_CPT_ALLOC_PTR(net, nrs_pol2cptab(policy), nrs_pol2cptid(policy));
if (net == NULL)
RETURN(-ENOMEM);
net->cn_binheap = binheap_create(&nrs_crrn_heap_ops,
CBH_FLAG_ATOMIC_GROW, 4096, NULL,
nrs_pol2cptab(policy),
nrs_pol2cptid(policy));
if (net->cn_binheap == NULL)
GOTO(out_net, rc = -ENOMEM);
rc = rhashtable_init(&net->cn_cli_hash, &nrs_crrn_hash_params);
if (rc)
GOTO(out_binheap, rc);
/*
* Set default quantum value to max_rpcs_in_flight for non-MDS OSCs;
* there may be more RPCs pending from each struct nrs_crrn_client even
* with the default max_rpcs_in_flight value, as we are scheduling over
* NIDs, and there may be more than one mount point per client.
*/
net->cn_quantum = OBD_MAX_RIF_DEFAULT;
/*
* Set to 1 so that the test inside nrs_crrn_req_add() can evaluate to
* true.
*/
net->cn_sequence = 1;
policy->pol_private = net;
RETURN(rc);
out_binheap:
binheap_destroy(net->cn_binheap);
out_net:
OBD_FREE_PTR(net);
RETURN(rc);
}
/**
* nrs_crrn_stop() - Called when a CRR-N policy instance is stopped.
* @policy: the policy
*
* Called when the policy has been instructed to transition to the
* ptlrpc_nrs_pol_state::NRS_POL_STATE_STOPPED state and has no more pending
* requests to serve.
*/
static void nrs_crrn_stop(struct ptlrpc_nrs_policy *policy)
{
struct nrs_crrn_net *net = policy->pol_private;
ENTRY;
LASSERT(net != NULL);
LASSERT(net->cn_binheap != NULL);
LASSERT(binheap_is_empty(net->cn_binheap));
rhashtable_free_and_destroy(&net->cn_cli_hash, nrs_crrn_exit, NULL);
binheap_destroy(net->cn_binheap);
OBD_FREE_PTR(net);
}
/**
* nrs_crrn_ctl() - Performs a policy-specific ctl function on CRR-N policy
* instances; similar to ioctl.
* @policy: the policy instance
* @opc: the opcode
* @arg: used for passing parameters and information
*
* \pre assert_spin_locked(&policy->pol_nrs->->nrs_lock)
* \post assert_spin_locked(&policy->pol_nrs->->nrs_lock)
*
* Return:
* * %0 operation carried out successfully
* * %negative on error
*/
static int nrs_crrn_ctl(struct ptlrpc_nrs_policy *policy,
enum ptlrpc_nrs_ctl opc,
void *arg)
{
assert_spin_locked(&policy->pol_nrs->nrs_lock);
switch (opc) {
default:
RETURN(-EINVAL);
/* Read Round Robin quantum size of a policy instance. */
case NRS_CTL_CRRN_RD_QUANTUM: {
struct nrs_crrn_net *net = policy->pol_private;
*(__u16 *)arg = net->cn_quantum;
}
break;
/* Write Round Robin quantum size of a policy instance. */
case NRS_CTL_CRRN_WR_QUANTUM: {
struct nrs_crrn_net *net = policy->pol_private;
net->cn_quantum = *(__u16 *)arg;
LASSERT(net->cn_quantum != 0);
}
break;
}
RETURN(0);
}
/**
* nrs_crrn_res_get() - Obtains resources from CRR-N policy instances
* @policy: the policy for which resources are being taken for
* request @nrq
* @nrq: the request for which resources are being taken
* @parent: parent resource, embedded in nrs_crrn_net for the
* CRR-N policy
* @resp: resources references are placed in this array (out param)
* @moving_req: signifies limited caller context; used to perform
* memory allocations in an atomic context in this
* policy
*
* Obtains resources from CRR-N policy instances. The top-level resource lives
* inside @nrs_crrn_net and the second-level resource inside @nrs_crrn_client
* object instances.
*
* see @nrs_resource_get_safe()
*
* Return:
* * %0 we are returning a top-level, parent resource, one that is
* embedded in an nrs_crrn_net object
* * %1 we are returning a bottom-level resource, one that is embedded
* in an nrs_crrn_client object
*/
static int nrs_crrn_res_get(struct ptlrpc_nrs_policy *policy,
struct ptlrpc_nrs_request *nrq,
const struct ptlrpc_nrs_resource *parent,
struct ptlrpc_nrs_resource **resp, bool moving_req)
{
struct nrs_crrn_net *net;
struct nrs_crrn_client *cli;
struct nrs_crrn_client *tmp;
struct ptlrpc_request *req;
if (parent == NULL) {
*resp = &((struct nrs_crrn_net *)policy->pol_private)->cn_res;
return 0;
}
net = container_of(parent, struct nrs_crrn_net, cn_res);
req = container_of(nrq, struct ptlrpc_request, rq_nrq);
cli = rhashtable_lookup_fast(&net->cn_cli_hash, &req->rq_peer.nid,
nrs_crrn_hash_params);
if (cli)
goto out;
OBD_CPT_ALLOC_GFP(cli, nrs_pol2cptab(policy), nrs_pol2cptid(policy),
sizeof(*cli), moving_req ? GFP_ATOMIC : GFP_NOFS);
if (cli == NULL)
return -ENOMEM;
cli->cc_nid = req->rq_peer.nid;
atomic_set(&cli->cc_ref, 0);
tmp = rhashtable_lookup_get_insert_fast(&net->cn_cli_hash,
&cli->cc_rhead,
nrs_crrn_hash_params);
if (tmp) {
/* insertion failed */
OBD_FREE_PTR(cli);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
cli = tmp;
}
out:
atomic_inc(&cli->cc_ref);
*resp = &cli->cc_res;
return 1;
}
/**
* nrs_crrn_res_put() - Called when releasing references to the resource
* hierachy obtained for a request for scheduling using the CRR-N policy.
* @policy: the policy the resource belongs to
* @res: the resource to be released
*/
static void nrs_crrn_res_put(struct ptlrpc_nrs_policy *policy,
const struct ptlrpc_nrs_resource *res)
{
struct nrs_crrn_client *cli;
/* Do nothing for freeing parent, nrs_crrn_net resources */
if (res->res_parent == NULL)
return;
cli = container_of(res, struct nrs_crrn_client, cc_res);
atomic_dec(&cli->cc_ref);
}
/**
* nrs_crrn_req_get() - Called when getting a request from the CRR-N policy for
* handlingso that it can be served
*
* @policy: the policy being polled
* @peek: when set, signifies that we just want to examine the request,
* and not handle it, so the request is not removed from the policy.
* @force: force the policy to return a request; unused in this policy
*
* see @ptlrpc_nrs_req_get_nolock()
* see @nrs_request_get()
*
* Return the request to be handled or NULL no request available
*/
static
struct ptlrpc_nrs_request *nrs_crrn_req_get(struct ptlrpc_nrs_policy *policy,
bool peek, bool force)
{
struct nrs_crrn_net *net = policy->pol_private;
struct binheap_node *node = binheap_root(net->cn_binheap);
struct ptlrpc_nrs_request *nrq;
nrq = unlikely(node == NULL) ? NULL :
container_of(node, struct ptlrpc_nrs_request, nr_node);
if (likely(!peek && nrq != NULL)) {
struct nrs_crrn_client *cli;
struct ptlrpc_request *req = container_of(nrq,
struct ptlrpc_request,
rq_nrq);
cli = container_of(nrs_request_resource(nrq),
struct nrs_crrn_client, cc_res);
LASSERT(nrq->nr_u.crr.cr_round <= cli->cc_round);
binheap_remove(net->cn_binheap, &nrq->nr_node);
cli->cc_active--;
CDEBUG(D_RPCTRACE,
"NRS: starting to handle %s request from %s, with round "
"%llu\n", NRS_POL_NAME_CRRN,
libcfs_idstr(&req->rq_peer), nrq->nr_u.crr.cr_round);
/* Peek at the next request to be served */
node = binheap_root(net->cn_binheap);
/* No more requests */
if (unlikely(node == NULL)) {
net->cn_round++;
} else {
struct ptlrpc_nrs_request *next;
next = container_of(node, struct ptlrpc_nrs_request,
nr_node);
if (net->cn_round < next->nr_u.crr.cr_round)
net->cn_round = next->nr_u.crr.cr_round;
}
}
return nrq;
}
/**
* nrs_crrn_req_add() - Adds request @nrq to a CRR-N @policy instance's set of
* queued requests
* @policy: the policy
* @nrq: the request to add
*
* A scheduling round is a stream of requests that have been sorted in batches
* according to the client that they originate from (as identified by its NID);
* there can be only one batch for each client in each round. The batches are of
* maximum size nrs_crrn_net:cn_quantum. When a new request arrives for
* scheduling from a client that has exhausted its quantum in its current round,
* it will start scheduling requests on the next scheduling round. Clients are
* allowed to schedule requests against a round until all requests for the round
* are serviced, so a client might miss a round if it is not generating requests
* for a long enough period of time. Clients that miss a round will continue
* with scheduling the next request that they generate, starting at the round
* that requests are being dispatched for, at the time of arrival of this new
* request.
*
* Requests are tagged with the round number and a sequence number; the sequence
* number indicates the relative ordering amongst the batches of requests in a
* round, and is identical for all requests in a batch, as is the round number.
* The round and sequence numbers are used by crrn_req_compare() in order to
* maintain an ordered set of rounds, with each round consisting of an ordered
* set of batches of requests.
*
* Return:
* * %0 request successfully added
* * %!=0 on error
*/
static int nrs_crrn_req_add(struct ptlrpc_nrs_policy *policy,
struct ptlrpc_nrs_request *nrq)
{
struct nrs_crrn_net *net;
struct nrs_crrn_client *cli;
int rc;
cli = container_of(nrs_request_resource(nrq),
struct nrs_crrn_client, cc_res);
net = container_of(nrs_request_resource(nrq)->res_parent,
struct nrs_crrn_net, cn_res);
if (cli->cc_quantum == 0 || cli->cc_round < net->cn_round ||
(cli->cc_active == 0 && cli->cc_quantum > 0)) {
/*
* If the client has no pending requests, and still some of its
* quantum remaining unused, which implies it has not had a
* chance to schedule up to its maximum allowed batch size of
* requests in the previous round it participated, schedule this
* next request on a new round; this avoids fragmentation of
* request batches caused by client inactivity, at the expense
* of potentially slightly increased service time for the
* request batch this request will be a part of.
*/
if (cli->cc_active == 0 && cli->cc_quantum > 0)
cli->cc_round++;
/* A new scheduling round has commenced */
if (cli->cc_round < net->cn_round)
cli->cc_round = net->cn_round;
/* I was not the last client through here */
if (cli->cc_sequence < net->cn_sequence)
cli->cc_sequence = ++net->cn_sequence;
/*
* Reset the quantum if we have reached the maximum quantum
* size for this batch, or even if we have not managed to
* complete a batch size up to its maximum allowed size.
* XXX: Accessed unlocked
*/
cli->cc_quantum = net->cn_quantum;
}
nrq->nr_u.crr.cr_round = cli->cc_round;
nrq->nr_u.crr.cr_sequence = cli->cc_sequence;
rc = binheap_insert(net->cn_binheap, &nrq->nr_node);
if (rc == 0) {
cli->cc_active++;
if (--cli->cc_quantum == 0)
cli->cc_round++;
}
return rc;
}
/**
* nrs_crrn_req_del() - Removes request @nrq from a CRR-N @policy instance's set
* of queued requests.
* @policy: the policy
* @nrq: the request to remove
*/
static void nrs_crrn_req_del(struct ptlrpc_nrs_policy *policy,
struct ptlrpc_nrs_request *nrq)
{
struct nrs_crrn_net *net;
struct nrs_crrn_client *cli;
bool is_root;
cli = container_of(nrs_request_resource(nrq),
struct nrs_crrn_client, cc_res);
net = container_of(nrs_request_resource(nrq)->res_parent,
struct nrs_crrn_net, cn_res);
LASSERT(nrq->nr_u.crr.cr_round <= cli->cc_round);
is_root = &nrq->nr_node == binheap_root(net->cn_binheap);
binheap_remove(net->cn_binheap, &nrq->nr_node);
cli->cc_active--;
/*
* If we just deleted the node at the root of the binheap, we may have
* to adjust round numbers.
*/
if (unlikely(is_root)) {
/* Peek at the next request to be served */
struct binheap_node *node = binheap_root(net->cn_binheap);
/* No more requests */
if (unlikely(node == NULL)) {
net->cn_round++;
} else {
nrq = container_of(node, struct ptlrpc_nrs_request,
nr_node);
if (net->cn_round < nrq->nr_u.crr.cr_round)
net->cn_round = nrq->nr_u.crr.cr_round;
}
}
}
/**
* nrs_crrn_req_stop() - Called right after the request @nrq finishes being
* handled by CRR-N policy instance @policy.
* @policy: the policy that handled the request
* @nrq: the request that was handled
*/
static void nrs_crrn_req_stop(struct ptlrpc_nrs_policy *policy,
struct ptlrpc_nrs_request *nrq)
{
struct ptlrpc_request *req = container_of(nrq, struct ptlrpc_request,
rq_nrq);
CDEBUG(D_RPCTRACE,
"NRS: finished handling %s request from %s, with round %llu"
"\n", NRS_POL_NAME_CRRN,
libcfs_idstr(&req->rq_peer), nrq->nr_u.crr.cr_round);
}
/* debugfs interface */
/*
* Retrieves the value of the Round Robin quantum (i.e. the maximum batch size)
* for CRR-N policy instances on both the regular and high-priority NRS head
* of a service, as long as a policy instance is not in the
* ptlrpc_nrs_pol_state::NRS_POL_STATE_STOPPED state; policy instances in this
* state are skipped later by nrs_crrn_ctl().
*
* Quantum values are in # of RPCs, and output is in YAML format.
*
* For example:
* reg_quantum:8
* hp_quantum:4
*/
static int
ptlrpc_lprocfs_nrs_crrn_quantum_seq_show(struct seq_file *m, void *data)
{
struct ptlrpc_service *svc = m->private;
__u16 quantum;
int rc;
/*
* Perform two separate calls to this as only one of the NRS heads'
* policies may be in the ptlrpc_nrs_pol_state::NRS_POL_STATE_STARTED or
* ptlrpc_nrs_pol_state::NRS_POL_STATE_STOPPING state.
*/
rc = ptlrpc_nrs_policy_control(svc, PTLRPC_NRS_QUEUE_REG,
NRS_POL_NAME_CRRN,
NRS_CTL_CRRN_RD_QUANTUM,
true, &quantum);
if (rc == 0) {
seq_printf(m, NRS_LPROCFS_QUANTUM_NAME_REG
"%-5d\n", quantum);
/*
* Ignore -ENODEV as the regular NRS head's policy may be in the
* ptlrpc_nrs_pol_state::NRS_POL_STATE_STOPPED state.
*/
} else if (rc != -ENODEV) {
return rc;
}
if (!nrs_svc_has_hp(svc))
goto no_hp;
rc = ptlrpc_nrs_policy_control(svc, PTLRPC_NRS_QUEUE_HP,
NRS_POL_NAME_CRRN,
NRS_CTL_CRRN_RD_QUANTUM,
true, &quantum);
if (rc == 0) {
seq_printf(m, NRS_LPROCFS_QUANTUM_NAME_HP"%-5d\n", quantum);
/*
* Ignore -ENODEV as the high priority NRS head's policy may be
* in the ptlrpc_nrs_pol_state::NRS_POL_STATE_STOPPED state.
*/
} else if (rc != -ENODEV) {
return rc;
}
no_hp:
return rc;
}
/**
* ptlrpc_lprocfs_nrs_crrn_quantum_seq_write() - Write operation debugfs file
* @file: file pointer to debugfs file
* @buffer: buffer (user space)
* @count: number of bytes in the buffer
* @off: offset
*
* Sets the value of the Round Robin quantum (i.e. the maximum batch size)
* for CRR-N policy instances of a service. The user can set the quantum size
* for the regular or high priority NRS head individually by specifying each
* value, or both together in a single invocation.
*
* For example:
*
* lctl set_param *.*.*.nrs_crrn_quantum=reg_quantum:32, to set the regular
* request quantum size on all PTLRPC services to 32
*
* lctl set_param *.*.*.nrs_crrn_quantum=hp_quantum:16, to set the high
* priority request quantum size on all PTLRPC services to 16, and
*
* lctl set_param *.*.ost_io.nrs_crrn_quantum=16, to set both the regular and
* high priority request quantum sizes of the ost_io service to 16.
*
* policy instances in the ptlrpc_nrs_pol_state::NRS_POL_STATE_STOPPED state
* are skipped later by nrs_crrn_ctl().
*
* Return:
* * %>0 on success
* * %negative on failure
*/
static ssize_t
ptlrpc_lprocfs_nrs_crrn_quantum_seq_write(struct file *file,
const char __user *buffer,
size_t count,
loff_t *off)
{
struct seq_file *m = file->private_data;
struct ptlrpc_service *svc = m->private;
enum ptlrpc_nrs_queue_type queue = 0;
char kernbuf[LPROCFS_NRS_WR_QUANTUM_MAX_CMD];
char *val;
long quantum_reg;
long quantum_hp;
/* lprocfs_find_named_value() modifies its argument, so keep a copy */
size_t count_copy;
int rc = 0;
int rc2 = 0;
if (count > (sizeof(kernbuf) - 1))
return -EINVAL;
if (copy_from_user(kernbuf, buffer, count))
return -EFAULT;
kernbuf[count] = '\0';
count_copy = count;
/* Check if the regular quantum value has been specified */
val = lprocfs_find_named_value(kernbuf, NRS_LPROCFS_QUANTUM_NAME_REG,
&count_copy);
if (val != kernbuf) {
rc = kstrtol(val, 10, &quantum_reg);
if (rc)
return rc;
queue |= PTLRPC_NRS_QUEUE_REG;
}
count_copy = count;
/* Check if the high priority quantum value has been specified */
val = lprocfs_find_named_value(kernbuf, NRS_LPROCFS_QUANTUM_NAME_HP,
&count_copy);
if (val != kernbuf) {
if (!nrs_svc_has_hp(svc))
return -ENODEV;
rc = kstrtol(val, 10, &quantum_hp);
if (rc)
return rc;
queue |= PTLRPC_NRS_QUEUE_HP;
}
/*
* If none of the queues has been specified, look for a valid numerical
* value
*/
if (queue == 0) {
rc = kstrtol(kernbuf, 10, &quantum_reg);
if (rc)
return rc;
queue = PTLRPC_NRS_QUEUE_REG;
if (nrs_svc_has_hp(svc)) {
queue |= PTLRPC_NRS_QUEUE_HP;
quantum_hp = quantum_reg;
}
}
if ((((queue & PTLRPC_NRS_QUEUE_REG) != 0) &&
((quantum_reg > LPROCFS_NRS_QUANTUM_MAX || quantum_reg <= 0))) ||
(((queue & PTLRPC_NRS_QUEUE_HP) != 0) &&
((quantum_hp > LPROCFS_NRS_QUANTUM_MAX || quantum_hp <= 0))))
return -EINVAL;
/*
* We change the values on regular and HP NRS heads separately, so that
* we do not exit early from ptlrpc_nrs_policy_control() with an error
* returned by nrs_policy_ctl_locked(), in cases where the user has not
* started the policy on either the regular or HP NRS head; i.e. we are
* ignoring -ENODEV within nrs_policy_ctl_locked(). -ENODEV is returned
* only if the operation fails with -ENODEV on all heads that have been
* specified by the command; if at least one operation succeeds,
* success is returned.
*/
if ((queue & PTLRPC_NRS_QUEUE_REG) != 0) {
rc = ptlrpc_nrs_policy_control(svc, PTLRPC_NRS_QUEUE_REG,
NRS_POL_NAME_CRRN,
NRS_CTL_CRRN_WR_QUANTUM, false,
&quantum_reg);
if ((rc < 0 && rc != -ENODEV) ||
(rc == -ENODEV && queue == PTLRPC_NRS_QUEUE_REG))
return rc;
}
if ((queue & PTLRPC_NRS_QUEUE_HP) != 0) {
rc2 = ptlrpc_nrs_policy_control(svc, PTLRPC_NRS_QUEUE_HP,
NRS_POL_NAME_CRRN,
NRS_CTL_CRRN_WR_QUANTUM, false,
&quantum_hp);
if ((rc2 < 0 && rc2 != -ENODEV) ||
(rc2 == -ENODEV && queue == PTLRPC_NRS_QUEUE_HP))
return rc2;
}
return rc == -ENODEV && rc2 == -ENODEV ? -ENODEV : count;
}
LDEBUGFS_SEQ_FOPS(ptlrpc_lprocfs_nrs_crrn_quantum);
/**
* nrs_crrn_lprocfs_init() - Initializes a CRR-N policy's lprocfs interface for
* service @svc
* @svc: the service (PTLRPC service)
*
* Return:
* * %0 success
* * %!=0 error
*/
static int nrs_crrn_lprocfs_init(struct ptlrpc_service *svc)
{
struct ldebugfs_vars nrs_crrn_lprocfs_vars[] = {
{ .name = "nrs_crrn_quantum",
.fops = &ptlrpc_lprocfs_nrs_crrn_quantum_fops,
.data = svc },
{ NULL }
};
if (!svc->srv_debugfs_entry)
return 0;
ldebugfs_add_vars(svc->srv_debugfs_entry, nrs_crrn_lprocfs_vars, NULL);
return 0;
}
/* CRR-N policy operations */
static const struct ptlrpc_nrs_pol_ops nrs_crrn_ops = {
.op_policy_start = nrs_crrn_start,
.op_policy_stop = nrs_crrn_stop,
.op_policy_ctl = nrs_crrn_ctl,
.op_res_get = nrs_crrn_res_get,
.op_res_put = nrs_crrn_res_put,
.op_req_get = nrs_crrn_req_get,
.op_req_enqueue = nrs_crrn_req_add,
.op_req_dequeue = nrs_crrn_req_del,
.op_req_stop = nrs_crrn_req_stop,
.op_lprocfs_init = nrs_crrn_lprocfs_init,
};
/* CRR-N policy configuration */
struct ptlrpc_nrs_pol_conf nrs_conf_crrn = {
.nc_name = NRS_POL_NAME_CRRN,
.nc_ops = &nrs_crrn_ops,
.nc_compat = nrs_policy_compat_all,
};