diff mbox series

[v2,15/17] xen/cpupool: add cpupool directories

Message ID 20201201082128.15239-16-jgross@suse.com (mailing list archive)
State Superseded
Headers show
Series xen: support per-cpupool scheduling granularity | expand

Commit Message

Jürgen Groß Dec. 1, 2020, 8:21 a.m. UTC
Add /cpupool/<cpupool-id> directories to hypfs. Those are completely
dynamic, so the related hypfs access functions need to be implemented.

Signed-off-by: Juergen Gross <jgross@suse.com>
---
V2:
- added const (Jan Beulich)
- call hypfs_add_dir() in helper (Dario Faggioli)
- switch locking to enter/exit callbacks
---
 docs/misc/hypfs-paths.pandoc |   9 +++
 xen/common/sched/cpupool.c   | 122 +++++++++++++++++++++++++++++++++++
 2 files changed, 131 insertions(+)

Comments

Jan Beulich Dec. 1, 2020, 9 a.m. UTC | #1
On 01.12.2020 09:21, Juergen Gross wrote:
> Add /cpupool/<cpupool-id> directories to hypfs. Those are completely
> dynamic, so the related hypfs access functions need to be implemented.
> 
> Signed-off-by: Juergen Gross <jgross@suse.com>
> ---
> V2:
> - added const (Jan Beulich)

Any particular reason this doesn't extend to ...

> @@ -1003,12 +1006,131 @@ static struct notifier_block cpu_nfb = {
>      .notifier_call = cpu_callback
>  };
>  
> +#ifdef CONFIG_HYPFS
> +static const struct hypfs_entry *cpupool_pooldir_enter(
> +    const struct hypfs_entry *entry);
> +
> +static struct hypfs_funcs cpupool_pooldir_funcs = {

... this (similarly in the next patch)? Granted I didn't look at
the hypfs patches yet, but I don't suppose these struct instances
need to be writable.

Jan
Jürgen Groß Dec. 1, 2020, 9:03 a.m. UTC | #2
On 01.12.20 10:00, Jan Beulich wrote:
> On 01.12.2020 09:21, Juergen Gross wrote:
>> Add /cpupool/<cpupool-id> directories to hypfs. Those are completely
>> dynamic, so the related hypfs access functions need to be implemented.
>>
>> Signed-off-by: Juergen Gross <jgross@suse.com>
>> ---
>> V2:
>> - added const (Jan Beulich)
> 
> Any particular reason this doesn't extend to ...
> 
>> @@ -1003,12 +1006,131 @@ static struct notifier_block cpu_nfb = {
>>       .notifier_call = cpu_callback
>>   };
>>   
>> +#ifdef CONFIG_HYPFS
>> +static const struct hypfs_entry *cpupool_pooldir_enter(
>> +    const struct hypfs_entry *entry);
>> +
>> +static struct hypfs_funcs cpupool_pooldir_funcs = {
> 
> ... this (similarly in the next patch)? Granted I didn't look at
> the hypfs patches yet, but I don't suppose these struct instances
> need to be writable.

No reason. I'll add const.


Juergen
Jürgen Groß Dec. 2, 2020, 3:46 p.m. UTC | #3
On 01.12.20 09:21, Juergen Gross wrote:
> Add /cpupool/<cpupool-id> directories to hypfs. Those are completely
> dynamic, so the related hypfs access functions need to be implemented.
> 
> Signed-off-by: Juergen Gross <jgross@suse.com>
> ---
> V2:
> - added const (Jan Beulich)
> - call hypfs_add_dir() in helper (Dario Faggioli)
> - switch locking to enter/exit callbacks
> ---
>   docs/misc/hypfs-paths.pandoc |   9 +++
>   xen/common/sched/cpupool.c   | 122 +++++++++++++++++++++++++++++++++++
>   2 files changed, 131 insertions(+)
> 
> diff --git a/docs/misc/hypfs-paths.pandoc b/docs/misc/hypfs-paths.pandoc
> index 6c7b2f7ee3..aaca1cdf92 100644
> --- a/docs/misc/hypfs-paths.pandoc
> +++ b/docs/misc/hypfs-paths.pandoc
> @@ -175,6 +175,15 @@ The major version of Xen.
>   
>   The minor version of Xen.
>   
> +#### /cpupool/
> +
> +A directory of all current cpupools.
> +
> +#### /cpupool/*/
> +
> +The individual cpupools. Each entry is a directory with the name being the
> +cpupool-id (e.g. /cpupool/0/).
> +
>   #### /params/
>   
>   A directory of runtime parameters.
> diff --git a/xen/common/sched/cpupool.c b/xen/common/sched/cpupool.c
> index 0db7d77219..3e17fdf95b 100644
> --- a/xen/common/sched/cpupool.c
> +++ b/xen/common/sched/cpupool.c
> @@ -13,6 +13,8 @@
>   
>   #include <xen/cpu.h>
>   #include <xen/cpumask.h>
> +#include <xen/guest_access.h>
> +#include <xen/hypfs.h>
>   #include <xen/init.h>
>   #include <xen/keyhandler.h>
>   #include <xen/lib.h>
> @@ -33,6 +35,7 @@ static int cpupool_moving_cpu = -1;
>   static struct cpupool *cpupool_cpu_moving = NULL;
>   static cpumask_t cpupool_locked_cpus;
>   
> +/* This lock nests inside sysctl or hypfs lock. */
>   static DEFINE_SPINLOCK(cpupool_lock);
>   
>   static enum sched_gran __read_mostly opt_sched_granularity = SCHED_GRAN_cpu;
> @@ -1003,12 +1006,131 @@ static struct notifier_block cpu_nfb = {
>       .notifier_call = cpu_callback
>   };
>   
> +#ifdef CONFIG_HYPFS
> +static const struct hypfs_entry *cpupool_pooldir_enter(
> +    const struct hypfs_entry *entry);
> +
> +static struct hypfs_funcs cpupool_pooldir_funcs = {
> +    .enter = cpupool_pooldir_enter,
> +    .exit = hypfs_node_exit,
> +    .read = hypfs_read_dir,
> +    .write = hypfs_write_deny,
> +    .getsize = hypfs_getsize,
> +    .findentry = hypfs_dir_findentry,
> +};
> +
> +static HYPFS_VARDIR_INIT(cpupool_pooldir, "%u", &cpupool_pooldir_funcs);
> +
> +static const struct hypfs_entry *cpupool_pooldir_enter(
> +    const struct hypfs_entry *entry)
> +{
> +    return &cpupool_pooldir.e;
> +}
I have found a more generic way to handle entering a dyndir node,
resulting in no need to have cpupool_pooldir_enter() and
cpupool_pooldir_funcs.

This will add some more lines to the previous patch, but less than
saved here.


Juergen
Jan Beulich Dec. 3, 2020, 2:46 p.m. UTC | #4
On 02.12.2020 16:46, Jürgen Groß wrote:
> On 01.12.20 09:21, Juergen Gross wrote:
>> @@ -1003,12 +1006,131 @@ static struct notifier_block cpu_nfb = {
>>       .notifier_call = cpu_callback
>>   };
>>   
>> +#ifdef CONFIG_HYPFS
>> +static const struct hypfs_entry *cpupool_pooldir_enter(
>> +    const struct hypfs_entry *entry);
>> +
>> +static struct hypfs_funcs cpupool_pooldir_funcs = {
>> +    .enter = cpupool_pooldir_enter,
>> +    .exit = hypfs_node_exit,
>> +    .read = hypfs_read_dir,
>> +    .write = hypfs_write_deny,
>> +    .getsize = hypfs_getsize,
>> +    .findentry = hypfs_dir_findentry,
>> +};
>> +
>> +static HYPFS_VARDIR_INIT(cpupool_pooldir, "%u", &cpupool_pooldir_funcs);
>> +
>> +static const struct hypfs_entry *cpupool_pooldir_enter(
>> +    const struct hypfs_entry *entry)
>> +{
>> +    return &cpupool_pooldir.e;
>> +}
> I have found a more generic way to handle entering a dyndir node,
> resulting in no need to have cpupool_pooldir_enter() and
> cpupool_pooldir_funcs.
> 
> This will add some more lines to the previous patch, but less than
> saved here.

Which may then mean it's not a good use of time to look at v2 patch
14, considering there's a lot of other stuff in need of looking at?

Jan
Jürgen Groß Dec. 3, 2020, 3:11 p.m. UTC | #5
On 03.12.20 15:46, Jan Beulich wrote:
> On 02.12.2020 16:46, Jürgen Groß wrote:
>> On 01.12.20 09:21, Juergen Gross wrote:
>>> @@ -1003,12 +1006,131 @@ static struct notifier_block cpu_nfb = {
>>>        .notifier_call = cpu_callback
>>>    };
>>>    
>>> +#ifdef CONFIG_HYPFS
>>> +static const struct hypfs_entry *cpupool_pooldir_enter(
>>> +    const struct hypfs_entry *entry);
>>> +
>>> +static struct hypfs_funcs cpupool_pooldir_funcs = {
>>> +    .enter = cpupool_pooldir_enter,
>>> +    .exit = hypfs_node_exit,
>>> +    .read = hypfs_read_dir,
>>> +    .write = hypfs_write_deny,
>>> +    .getsize = hypfs_getsize,
>>> +    .findentry = hypfs_dir_findentry,
>>> +};
>>> +
>>> +static HYPFS_VARDIR_INIT(cpupool_pooldir, "%u", &cpupool_pooldir_funcs);
>>> +
>>> +static const struct hypfs_entry *cpupool_pooldir_enter(
>>> +    const struct hypfs_entry *entry)
>>> +{
>>> +    return &cpupool_pooldir.e;
>>> +}
>> I have found a more generic way to handle entering a dyndir node,
>> resulting in no need to have cpupool_pooldir_enter() and
>> cpupool_pooldir_funcs.
>>
>> This will add some more lines to the previous patch, but less than
>> saved here.
> 
> Which may then mean it's not a good use of time to look at v2 patch
> 14, considering there's a lot of other stuff in need of looking at?

All of V2 patch 14 remains valid, there is just a generic enter function
added in V3.


Juergen
Jan Beulich Dec. 4, 2020, 9:10 a.m. UTC | #6
On 01.12.2020 09:21, Juergen Gross wrote:
> @@ -1003,12 +1006,131 @@ static struct notifier_block cpu_nfb = {
>      .notifier_call = cpu_callback
>  };
>  
> +#ifdef CONFIG_HYPFS
> +static const struct hypfs_entry *cpupool_pooldir_enter(
> +    const struct hypfs_entry *entry);
> +
> +static struct hypfs_funcs cpupool_pooldir_funcs = {

Yet one more const missing?

> +    .enter = cpupool_pooldir_enter,
> +    .exit = hypfs_node_exit,
> +    .read = hypfs_read_dir,
> +    .write = hypfs_write_deny,
> +    .getsize = hypfs_getsize,
> +    .findentry = hypfs_dir_findentry,
> +};
> +
> +static HYPFS_VARDIR_INIT(cpupool_pooldir, "%u", &cpupool_pooldir_funcs);
> +
> +static const struct hypfs_entry *cpupool_pooldir_enter(
> +    const struct hypfs_entry *entry)
> +{
> +    return &cpupool_pooldir.e;
> +}
> +
> +static int cpupool_dir_read(const struct hypfs_entry *entry,
> +                            XEN_GUEST_HANDLE_PARAM(void) uaddr)
> +{
> +    int ret = 0;
> +    const struct cpupool *c;
> +    unsigned int size = 0;
> +
> +    list_for_each_entry(c, &cpupool_list, list)
> +    {
> +        size += hypfs_dynid_entry_size(entry, c->cpupool_id);

Why do you maintain size here? I can't spot any use.

With this dropped the function then no longer depends on its
"entry" parameter, which makes me wonder ...

> +        ret = hypfs_read_dyndir_id_entry(&cpupool_pooldir, c->cpupool_id,
> +                                         list_is_last(&c->list, &cpupool_list),
> +                                         &uaddr);
> +        if ( ret )
> +            break;
> +    }
> +
> +    return ret;
> +}
> +
> +static unsigned int cpupool_dir_getsize(const struct hypfs_entry *entry)
> +{
> +    const struct cpupool *c;
> +    unsigned int size = 0;
> +
> +    list_for_each_entry(c, &cpupool_list, list)
> +        size += hypfs_dynid_entry_size(entry, c->cpupool_id);

... why this one does. To be certain their results are consistent
with one another, I think both should produce their results from
the same data.

> +    return size;
> +}
> +
> +static const struct hypfs_entry *cpupool_dir_enter(
> +    const struct hypfs_entry *entry)
> +{
> +    struct hypfs_dyndir_id *data;
> +
> +    data = hypfs_alloc_dyndata(sizeof(*data));

I generally like the added type safety of the macro wrappers
around _xmalloc(). I wonder if it wouldn't be a good idea to have
such here as well, to avoid random mistakes like

    data = hypfs_alloc_dyndata(sizeof(data));

However I further notice that the struct allocated isn't cpupool
specific at all. It would seem to me that such an allocation
therefore doesn't belong here. Therefore I wonder whether ...

> +    if ( !data )
> +        return ERR_PTR(-ENOMEM);
> +    data->id = CPUPOOLID_NONE;
> +
> +    spin_lock(&cpupool_lock);

... these two properties (initial ID and lock) shouldn't e.g. be
communicated via the template, allowing the enter/exit hooks to
become generic for all ID templates.

Yet in turn I notice that the "id" field only ever gets set, both
in patch 14 and here. But yes, I've now spotted the consumers in
patch 16.

> +    return entry;
> +}
> +
> +static void cpupool_dir_exit(const struct hypfs_entry *entry)
> +{
> +    spin_unlock(&cpupool_lock);
> +
> +    hypfs_free_dyndata();
> +}
> +
> +static struct hypfs_entry *cpupool_dir_findentry(
> +    const struct hypfs_entry_dir *dir, const char *name, unsigned int name_len)
> +{
> +    unsigned long id;
> +    const char *end;
> +    const struct cpupool *cpupool;
> +
> +    id = simple_strtoul(name, &end, 10);
> +    if ( end != name + name_len )
> +        return ERR_PTR(-ENOENT);
> +
> +    cpupool = __cpupool_find_by_id(id, true);

Silent truncation from unsigned long to unsigned int?

> +    if ( !cpupool )
> +        return ERR_PTR(-ENOENT);
> +
> +    return hypfs_gen_dyndir_entry_id(&cpupool_pooldir, id);
> +}
> +
> +static struct hypfs_funcs cpupool_dir_funcs = {

Yet another missing const?

> +    .enter = cpupool_dir_enter,
> +    .exit = cpupool_dir_exit,
> +    .read = cpupool_dir_read,
> +    .write = hypfs_write_deny,
> +    .getsize = cpupool_dir_getsize,
> +    .findentry = cpupool_dir_findentry,
> +};
> +
> +static HYPFS_VARDIR_INIT(cpupool_dir, "cpupool", &cpupool_dir_funcs);

Why VARDIR? This isn't a template, is it? Or does VARDIR really
serve multiple purposes?

> +static void cpupool_hypfs_init(void)
> +{
> +    hypfs_add_dir(&hypfs_root, &cpupool_dir, true);
> +    hypfs_add_dyndir(&cpupool_dir, &cpupool_pooldir);
> +}
> +#else
> +
> +static void cpupool_hypfs_init(void)
> +{
> +}
> +#endif

I think you want to be consistent with the use of blank lines next
to #if / #else / #endif. In cases when they enclose multiple entities,
I think it's generally better to have intervening blank lines
everywhere. I also think in such cases commenting #else and #endif is
helpful. But you're the maintainer of this code ...

Jan
Jürgen Groß Dec. 4, 2020, 11:08 a.m. UTC | #7
On 04.12.20 10:10, Jan Beulich wrote:
> On 01.12.2020 09:21, Juergen Gross wrote:
>> @@ -1003,12 +1006,131 @@ static struct notifier_block cpu_nfb = {
>>       .notifier_call = cpu_callback
>>   };
>>   
>> +#ifdef CONFIG_HYPFS
>> +static const struct hypfs_entry *cpupool_pooldir_enter(
>> +    const struct hypfs_entry *entry);
>> +
>> +static struct hypfs_funcs cpupool_pooldir_funcs = {
> 
> Yet one more const missing?

Already fixed locally.

> 
>> +    .enter = cpupool_pooldir_enter,
>> +    .exit = hypfs_node_exit,
>> +    .read = hypfs_read_dir,
>> +    .write = hypfs_write_deny,
>> +    .getsize = hypfs_getsize,
>> +    .findentry = hypfs_dir_findentry,
>> +};
>> +
>> +static HYPFS_VARDIR_INIT(cpupool_pooldir, "%u", &cpupool_pooldir_funcs);
>> +
>> +static const struct hypfs_entry *cpupool_pooldir_enter(
>> +    const struct hypfs_entry *entry)
>> +{
>> +    return &cpupool_pooldir.e;
>> +}
>> +
>> +static int cpupool_dir_read(const struct hypfs_entry *entry,
>> +                            XEN_GUEST_HANDLE_PARAM(void) uaddr)
>> +{
>> +    int ret = 0;
>> +    const struct cpupool *c;
>> +    unsigned int size = 0;
>> +
>> +    list_for_each_entry(c, &cpupool_list, list)
>> +    {
>> +        size += hypfs_dynid_entry_size(entry, c->cpupool_id);
> 
> Why do you maintain size here? I can't spot any use.

Oh, indeed.

This is a remnant of an earlier variant.

> 
> With this dropped the function then no longer depends on its
> "entry" parameter, which makes me wonder ...
> 
>> +        ret = hypfs_read_dyndir_id_entry(&cpupool_pooldir, c->cpupool_id,
>> +                                         list_is_last(&c->list, &cpupool_list),
>> +                                         &uaddr);
>> +        if ( ret )
>> +            break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static unsigned int cpupool_dir_getsize(const struct hypfs_entry *entry)
>> +{
>> +    const struct cpupool *c;
>> +    unsigned int size = 0;
>> +
>> +    list_for_each_entry(c, &cpupool_list, list)
>> +        size += hypfs_dynid_entry_size(entry, c->cpupool_id);
> 
> ... why this one does. To be certain their results are consistent
> with one another, I think both should produce their results from
> the same data.

In the end they do. Creating a complete direntry just for obtaining its
size is overkill, especially as hypfs_read_dyndir_id_entry() is not
directly calculating the size, but copying the fixed and the variable
parts in two portions.

> 
>> +    return size;
>> +}
>> +
>> +static const struct hypfs_entry *cpupool_dir_enter(
>> +    const struct hypfs_entry *entry)
>> +{
>> +    struct hypfs_dyndir_id *data;
>> +
>> +    data = hypfs_alloc_dyndata(sizeof(*data));
> 
> I generally like the added type safety of the macro wrappers
> around _xmalloc(). I wonder if it wouldn't be a good idea to have
> such here as well, to avoid random mistakes like
> 
>      data = hypfs_alloc_dyndata(sizeof(data));

Fine with me.

> 
> However I further notice that the struct allocated isn't cpupool
> specific at all. It would seem to me that such an allocation
> therefore doesn't belong here. Therefore I wonder whether ...
> 
>> +    if ( !data )
>> +        return ERR_PTR(-ENOMEM);
>> +    data->id = CPUPOOLID_NONE;
>> +
>> +    spin_lock(&cpupool_lock);
> 
> ... these two properties (initial ID and lock) shouldn't e.g. be
> communicated via the template, allowing the enter/exit hooks to
> become generic for all ID templates.

The problem with the lock is that it is rather user specific. For
domains this will be split (rcu_read_lock(&domlist_read_lock) for
the /domain directory, and get_domain() for the per-domain level).

And memory allocation might need other data as well, so this won't
be the same structure for all cases. A two level dynamic directory
(e.g. domain/vcpu) might want to allocate the needed dyndata for
both levels already when entering /domain.

> 
> Yet in turn I notice that the "id" field only ever gets set, both
> in patch 14 and here. But yes, I've now spotted the consumers in
> patch 16.
> 
>> +    return entry;
>> +}
>> +
>> +static void cpupool_dir_exit(const struct hypfs_entry *entry)
>> +{
>> +    spin_unlock(&cpupool_lock);
>> +
>> +    hypfs_free_dyndata();
>> +}
>> +
>> +static struct hypfs_entry *cpupool_dir_findentry(
>> +    const struct hypfs_entry_dir *dir, const char *name, unsigned int name_len)
>> +{
>> +    unsigned long id;
>> +    const char *end;
>> +    const struct cpupool *cpupool;
>> +
>> +    id = simple_strtoul(name, &end, 10);
>> +    if ( end != name + name_len )
>> +        return ERR_PTR(-ENOENT);
>> +
>> +    cpupool = __cpupool_find_by_id(id, true);
> 
> Silent truncation from unsigned long to unsigned int?

Oh, indeed. Need to check against UINT_MAX.

> 
>> +    if ( !cpupool )
>> +        return ERR_PTR(-ENOENT);
>> +
>> +    return hypfs_gen_dyndir_entry_id(&cpupool_pooldir, id);
>> +}
>> +
>> +static struct hypfs_funcs cpupool_dir_funcs = {
> 
> Yet another missing const?

Already fixed.

> 
>> +    .enter = cpupool_dir_enter,
>> +    .exit = cpupool_dir_exit,
>> +    .read = cpupool_dir_read,
>> +    .write = hypfs_write_deny,
>> +    .getsize = cpupool_dir_getsize,
>> +    .findentry = cpupool_dir_findentry,
>> +};
>> +
>> +static HYPFS_VARDIR_INIT(cpupool_dir, "cpupool", &cpupool_dir_funcs);
> 
> Why VARDIR? This isn't a template, is it? Or does VARDIR really
> serve multiple purposes?

Basically it just takes an additional parameter for the function vector.
Maybe I should rename it to HYPFS_DIR_INIT_FUNC()?

> 
>> +static void cpupool_hypfs_init(void)
>> +{
>> +    hypfs_add_dir(&hypfs_root, &cpupool_dir, true);
>> +    hypfs_add_dyndir(&cpupool_dir, &cpupool_pooldir);
>> +}
>> +#else
>> +
>> +static void cpupool_hypfs_init(void)
>> +{
>> +}
>> +#endif
> 
> I think you want to be consistent with the use of blank lines next
> to #if / #else / #endif. In cases when they enclose multiple entities,
> I think it's generally better to have intervening blank lines
> everywhere. I also think in such cases commenting #else and #endif is
> helpful. But you're the maintainer of this code ...

I think I'll change it.


Juergen
Jan Beulich Dec. 4, 2020, 11:54 a.m. UTC | #8
On 04.12.2020 12:08, Jürgen Groß wrote:
> On 04.12.20 10:10, Jan Beulich wrote:
>> On 01.12.2020 09:21, Juergen Gross wrote:
>>> +static struct hypfs_funcs cpupool_dir_funcs = {
>>
>> Yet another missing const?
> 
> Already fixed.
> 
>>
>>> +    .enter = cpupool_dir_enter,
>>> +    .exit = cpupool_dir_exit,
>>> +    .read = cpupool_dir_read,
>>> +    .write = hypfs_write_deny,
>>> +    .getsize = cpupool_dir_getsize,
>>> +    .findentry = cpupool_dir_findentry,
>>> +};
>>> +
>>> +static HYPFS_VARDIR_INIT(cpupool_dir, "cpupool", &cpupool_dir_funcs);
>>
>> Why VARDIR? This isn't a template, is it? Or does VARDIR really
>> serve multiple purposes?
> 
> Basically it just takes an additional parameter for the function vector.
> Maybe I should rename it to HYPFS_DIR_INIT_FUNC()?

Maybe. Depends on what exactly the VAR is meant to stand for.

Jan
diff mbox series

Patch

diff --git a/docs/misc/hypfs-paths.pandoc b/docs/misc/hypfs-paths.pandoc
index 6c7b2f7ee3..aaca1cdf92 100644
--- a/docs/misc/hypfs-paths.pandoc
+++ b/docs/misc/hypfs-paths.pandoc
@@ -175,6 +175,15 @@  The major version of Xen.
 
 The minor version of Xen.
 
+#### /cpupool/
+
+A directory of all current cpupools.
+
+#### /cpupool/*/
+
+The individual cpupools. Each entry is a directory with the name being the
+cpupool-id (e.g. /cpupool/0/).
+
 #### /params/
 
 A directory of runtime parameters.
diff --git a/xen/common/sched/cpupool.c b/xen/common/sched/cpupool.c
index 0db7d77219..3e17fdf95b 100644
--- a/xen/common/sched/cpupool.c
+++ b/xen/common/sched/cpupool.c
@@ -13,6 +13,8 @@ 
 
 #include <xen/cpu.h>
 #include <xen/cpumask.h>
+#include <xen/guest_access.h>
+#include <xen/hypfs.h>
 #include <xen/init.h>
 #include <xen/keyhandler.h>
 #include <xen/lib.h>
@@ -33,6 +35,7 @@  static int cpupool_moving_cpu = -1;
 static struct cpupool *cpupool_cpu_moving = NULL;
 static cpumask_t cpupool_locked_cpus;
 
+/* This lock nests inside sysctl or hypfs lock. */
 static DEFINE_SPINLOCK(cpupool_lock);
 
 static enum sched_gran __read_mostly opt_sched_granularity = SCHED_GRAN_cpu;
@@ -1003,12 +1006,131 @@  static struct notifier_block cpu_nfb = {
     .notifier_call = cpu_callback
 };
 
+#ifdef CONFIG_HYPFS
+static const struct hypfs_entry *cpupool_pooldir_enter(
+    const struct hypfs_entry *entry);
+
+static struct hypfs_funcs cpupool_pooldir_funcs = {
+    .enter = cpupool_pooldir_enter,
+    .exit = hypfs_node_exit,
+    .read = hypfs_read_dir,
+    .write = hypfs_write_deny,
+    .getsize = hypfs_getsize,
+    .findentry = hypfs_dir_findentry,
+};
+
+static HYPFS_VARDIR_INIT(cpupool_pooldir, "%u", &cpupool_pooldir_funcs);
+
+static const struct hypfs_entry *cpupool_pooldir_enter(
+    const struct hypfs_entry *entry)
+{
+    return &cpupool_pooldir.e;
+}
+
+static int cpupool_dir_read(const struct hypfs_entry *entry,
+                            XEN_GUEST_HANDLE_PARAM(void) uaddr)
+{
+    int ret = 0;
+    const struct cpupool *c;
+    unsigned int size = 0;
+
+    list_for_each_entry(c, &cpupool_list, list)
+    {
+        size += hypfs_dynid_entry_size(entry, c->cpupool_id);
+
+        ret = hypfs_read_dyndir_id_entry(&cpupool_pooldir, c->cpupool_id,
+                                         list_is_last(&c->list, &cpupool_list),
+                                         &uaddr);
+        if ( ret )
+            break;
+    }
+
+    return ret;
+}
+
+static unsigned int cpupool_dir_getsize(const struct hypfs_entry *entry)
+{
+    const struct cpupool *c;
+    unsigned int size = 0;
+
+    list_for_each_entry(c, &cpupool_list, list)
+        size += hypfs_dynid_entry_size(entry, c->cpupool_id);
+
+    return size;
+}
+
+static const struct hypfs_entry *cpupool_dir_enter(
+    const struct hypfs_entry *entry)
+{
+    struct hypfs_dyndir_id *data;
+
+    data = hypfs_alloc_dyndata(sizeof(*data));
+    if ( !data )
+        return ERR_PTR(-ENOMEM);
+    data->id = CPUPOOLID_NONE;
+
+    spin_lock(&cpupool_lock);
+
+    return entry;
+}
+
+static void cpupool_dir_exit(const struct hypfs_entry *entry)
+{
+    spin_unlock(&cpupool_lock);
+
+    hypfs_free_dyndata();
+}
+
+static struct hypfs_entry *cpupool_dir_findentry(
+    const struct hypfs_entry_dir *dir, const char *name, unsigned int name_len)
+{
+    unsigned long id;
+    const char *end;
+    const struct cpupool *cpupool;
+
+    id = simple_strtoul(name, &end, 10);
+    if ( end != name + name_len )
+        return ERR_PTR(-ENOENT);
+
+    cpupool = __cpupool_find_by_id(id, true);
+
+    if ( !cpupool )
+        return ERR_PTR(-ENOENT);
+
+    return hypfs_gen_dyndir_entry_id(&cpupool_pooldir, id);
+}
+
+static struct hypfs_funcs cpupool_dir_funcs = {
+    .enter = cpupool_dir_enter,
+    .exit = cpupool_dir_exit,
+    .read = cpupool_dir_read,
+    .write = hypfs_write_deny,
+    .getsize = cpupool_dir_getsize,
+    .findentry = cpupool_dir_findentry,
+};
+
+static HYPFS_VARDIR_INIT(cpupool_dir, "cpupool", &cpupool_dir_funcs);
+
+static void cpupool_hypfs_init(void)
+{
+    hypfs_add_dir(&hypfs_root, &cpupool_dir, true);
+    hypfs_add_dyndir(&cpupool_dir, &cpupool_pooldir);
+}
+#else
+
+static void cpupool_hypfs_init(void)
+{
+}
+#endif
+
 static int __init cpupool_init(void)
 {
     unsigned int cpu;
 
     cpupool_gran_init();
 
+    cpupool_hypfs_init();
+
     cpupool0 = cpupool_create(0, 0);
     BUG_ON(IS_ERR(cpupool0));
     cpupool_put(cpupool0);