diff mbox

[v8,03/24] x86: refactor psr: implement main data structures.

Message ID 1487148579-7243-4-git-send-email-yi.y.sun@linux.intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Yi Sun Feb. 15, 2017, 8:49 a.m. UTC
To construct an extendible framework, we need analyze PSR features
and abstract the common things and feature specific things. Then,
encapsulate them into different data structures.

By analyzing PSR features, we can get below map.
                +------+------+------+
      --------->| Dom0 | Dom1 | ...  |
      |         +------+------+------+
      |            |
      |Dom ID      | cos_id of domain
      |            V
      |        +-----------------------------------------------------------------------------+
User --------->| PSR                                                                         |
     Socket ID |  +--------------+---------------+---------------+                           |
               |  | Socket0 Info | Socket 1 Info |    ...        |                           |
               |  +--------------+---------------+---------------+                           |
               |    |                   cos_id=0               cos_id=1          ...         |
               |    |          +-----------------------+-----------------------+-----------+ |
               |    |->Ref   : |         ref 0         |         ref 1         | ...       | |
               |    |          +-----------------------+-----------------------+-----------+ |
               |    |          +-----------------------+-----------------------+-----------+ |
               |    |->L3 CAT: |         cos 0         |         cos 1         | ...       | |
               |    |          +-----------------------+-----------------------+-----------+ |
               |    |          +-----------------------+-----------------------+-----------+ |
               |    |->L2 CAT: |         cos 0         |         cos 1         | ...       | |
               |    |          +-----------------------+-----------------------+-----------+ |
               |    |          +-----------+-----------+-----------+-----------+-----------+ |
               |    |->CDP   : | cos0 code | cos0 data | cos1 code | cos1 data | ...       | |
               |               +-----------+-----------+-----------+-----------+-----------+ |
               +-----------------------------------------------------------------------------+

So, we need define a socket info data structure, 'struct
psr_socket_info' to manage information per socket. It contains a
reference count array according to COS ID and a feature list to
manage all features enabled. Every entry of the reference count
array is used to record how many domains are using the COS registers
according to the COS ID. For example, L3 CAT and L2 CAT are enabled,
Dom1 uses COS_ID=1 registers of both features to save CBM values, like
below.
        +-------+-------+-------+-----+
        | COS 0 | COS 1 | COS 2 | ... |
        +-------+-------+-------+-----+
L3 CAT  | 0x7ff | 0x1ff | ...   | ... |
        +-------+-------+-------+-----+
L2 CAT  | 0xff  | 0xff  | ...   | ... |
        +-------+-------+-------+-----+

If Dom2 has same CBM values, it can reuse these registers which COS_ID=1.
That means, both Dom1 and Dom2 use same COS registers(ID=1) to save same
L3/L2 values. So, the value ref[1] is 2 which means 2 domains are using
COS_ID 1.

To manage a feature, we need define a feature node data structure,
'struct feat_node', to manage feature's specific HW info, its callback
functions (all feature's specific behaviors are encapsulated into these
callback functions), and an array of all COS registers values of this
feature.

CDP is a special feature which uses two entries of the array
for one COS ID. So, the number of CDP COS registers is the half of L3
CAT. E.g. L3 CAT has 16 COS registers, then CDP has 8 COS registers if
it is enabled. CDP uses the COS registers array as below.

                         +-----------+-----------+-----------+-----------+-----------+
CDP cos_reg_val[] index: |     0     |     1     |     2     |     3     |    ...    |
                         +-----------+-----------+-----------+-----------+-----------+
                  value: | cos0 code | cos0 data | cos1 code | cos1 data |    ...    |
                         +-----------+-----------+-----------+-----------+-----------+

For more details, please refer SDM and patches to implement 'get value' and
'set value'.

Signed-off-by: Yi Sun <yi.y.sun@linux.intel.com>
Reviewed-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
 xen/arch/x86/psr.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 107 insertions(+), 1 deletion(-)

Comments

Roger Pau Monné Feb. 28, 2017, 11:58 a.m. UTC | #1
On Wed, Feb 15, 2017 at 04:49:18PM +0800, Yi Sun wrote:
> To construct an extendible framework, we need analyze PSR features
> and abstract the common things and feature specific things. Then,
> encapsulate them into different data structures.
> 
> By analyzing PSR features, we can get below map.
>                 +------+------+------+
>       --------->| Dom0 | Dom1 | ...  |
>       |         +------+------+------+
>       |            |
>       |Dom ID      | cos_id of domain
>       |            V
>       |        +-----------------------------------------------------------------------------+
> User --------->| PSR                                                                         |
>      Socket ID |  +--------------+---------------+---------------+                           |
>                |  | Socket0 Info | Socket 1 Info |    ...        |                           |
>                |  +--------------+---------------+---------------+                           |
>                |    |                   cos_id=0               cos_id=1          ...         |
>                |    |          +-----------------------+-----------------------+-----------+ |
>                |    |->Ref   : |         ref 0         |         ref 1         | ...       | |
>                |    |          +-----------------------+-----------------------+-----------+ |
>                |    |          +-----------------------+-----------------------+-----------+ |
>                |    |->L3 CAT: |         cos 0         |         cos 1         | ...       | |
>                |    |          +-----------------------+-----------------------+-----------+ |
>                |    |          +-----------------------+-----------------------+-----------+ |
>                |    |->L2 CAT: |         cos 0         |         cos 1         | ...       | |
>                |    |          +-----------------------+-----------------------+-----------+ |
>                |    |          +-----------+-----------+-----------+-----------+-----------+ |
>                |    |->CDP   : | cos0 code | cos0 data | cos1 code | cos1 data | ...       | |
>                |               +-----------+-----------+-----------+-----------+-----------+ |
>                +-----------------------------------------------------------------------------+
> 
> So, we need define a socket info data structure, 'struct
> psr_socket_info' to manage information per socket. It contains a
> reference count array according to COS ID and a feature list to
> manage all features enabled. Every entry of the reference count
> array is used to record how many domains are using the COS registers
> according to the COS ID. For example, L3 CAT and L2 CAT are enabled,
> Dom1 uses COS_ID=1 registers of both features to save CBM values, like
> below.
>         +-------+-------+-------+-----+
>         | COS 0 | COS 1 | COS 2 | ... |
>         +-------+-------+-------+-----+
> L3 CAT  | 0x7ff | 0x1ff | ...   | ... |
>         +-------+-------+-------+-----+
> L2 CAT  | 0xff  | 0xff  | ...   | ... |
>         +-------+-------+-------+-----+
> 
> If Dom2 has same CBM values, it can reuse these registers which COS_ID=1.
> That means, both Dom1 and Dom2 use same COS registers(ID=1) to save same
> L3/L2 values. So, the value ref[1] is 2 which means 2 domains are using
> COS_ID 1.
> 
> To manage a feature, we need define a feature node data structure,
> 'struct feat_node', to manage feature's specific HW info, its callback
> functions (all feature's specific behaviors are encapsulated into these
> callback functions), and an array of all COS registers values of this
> feature.
> 
> CDP is a special feature which uses two entries of the array
> for one COS ID. So, the number of CDP COS registers is the half of L3
> CAT. E.g. L3 CAT has 16 COS registers, then CDP has 8 COS registers if
> it is enabled. CDP uses the COS registers array as below.
> 
>                          +-----------+-----------+-----------+-----------+-----------+
> CDP cos_reg_val[] index: |     0     |     1     |     2     |     3     |    ...    |
>                          +-----------+-----------+-----------+-----------+-----------+
>                   value: | cos0 code | cos0 data | cos1 code | cos1 data |    ...    |
>                          +-----------+-----------+-----------+-----------+-----------+
> 
> For more details, please refer SDM and patches to implement 'get value' and
> 'set value'.

I would recommend that you merge this with a patch that actually makes use of
this structures, or else it's hard to review it's usage IMHO.

> Signed-off-by: Yi Sun <yi.y.sun@linux.intel.com>
> Reviewed-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
> ---
>  xen/arch/x86/psr.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 107 insertions(+), 1 deletion(-)
> 
> diff --git a/xen/arch/x86/psr.c b/xen/arch/x86/psr.c
> index 96a8589..5acd9ca 100644
> --- a/xen/arch/x86/psr.c
> +++ b/xen/arch/x86/psr.c
> @@ -13,16 +13,122 @@
>   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>   * more details.
>   */
> -#include <xen/init.h>
>  #include <xen/cpu.h>
>  #include <xen/err.h>
> +#include <xen/init.h>
> +#include <xen/list.h>
>  #include <xen/sched.h>
>  #include <asm/psr.h>
>  
> +/*
> + * Terminology:
> + * - CAT         Cache Allocation Technology
> + * - CBM         Capacity BitMasks
> + * - CDP         Code and Data Prioritization
> + * - COS/CLOS    Class of Service. Also mean COS registers.
> + * - COS_MAX     Max number of COS for the feature (minus 1)
> + * - MSRs        Machine Specific Registers
> + * - PSR         Intel Platform Shared Resource
> + */
> +
>  #define PSR_CMT        (1<<0)
>  #define PSR_CAT        (1<<1)
>  #define PSR_CDP        (1<<2)
>  
> +/*
> + * Per SDM chapter 'Cache Allocation Technology: Cache Mask Configuration',
> + * the MSRs ranging from 0C90H through 0D0FH (inclusive), enables support for
> + * up to 128 L3 CAT Classes of Service. The COS_ID=[0,127].
> + *
> + * The MSRs ranging from 0D10H through 0D4FH (inclusive), enables support for
> + * up to 64 L2 CAT COS. The COS_ID=[0,63].
> + *
> + * So, the maximum COS register count of one feature is 128.
> + */
> +#define MAX_COS_REG_CNT  128
> +
> +/*
> + * PSR features are managed per socket. Below structure defines the members
> + * used to manage these features.
> + * feat_mask - Mask used to record features enabled on socket. There may be
> + *             some features enabled at same time.
> + * nr_feat   - Record how many features enabled.
> + * feat_list - A list used to manage all features enabled.
> + * cos_ref   - A reference count array to record how many domains are using the
> + *             COS_ID.
> + *             Every entry of cos_ref corresponds to one COS ID.
> + * ref_lock  - A lock to protect cos_ref.
> + */
> +struct psr_socket_info {
> +    /*
> +     * It maps to values defined in 'enum psr_feat_type' below. Value in 'enum
> +     * psr_feat_type' means the bit position.
> +     * bit 0:   L3 CAT
> +     * bit 1:   L3 CDP
> +     * bit 2:   L2 CAT
> +     */
> +    unsigned int feat_mask;
> +    unsigned int nr_feat;
> +    struct list_head feat_list;

Isn't it a little bit overkill to use a list when there can only be a maximum
of 3 features? (and the feat_mask is currently 32bits, so I guess you don't
really foresee having more than 32 features).

I would suggest using:

     struct feat_node *features[PSR_SOCKET_MAX_FEAT];

(PSR_SOCKET_MAX_FEAT comes from the expansion of the enum below). Then you can
simply use the enum value of each feature as the position of it's corresponding
feat_node into the array.

> +    unsigned int cos_ref[MAX_COS_REG_CNT];
> +    spinlock_t ref_lock;
> +};
> +
> +enum psr_feat_type {
> +    PSR_SOCKET_L3_CAT = 0,
> +    PSR_SOCKET_L3_CDP,
> +    PSR_SOCKET_L2_CAT,
    PSR_SOCKET_MAX_FEAT,
> +};
> +
> +/* CAT/CDP HW info data structure. */
> +struct psr_cat_hw_info {
> +    unsigned int cbm_len;
> +    unsigned int cos_max;
> +};
> +
> +/* Encapsulate feature specific HW info here. */
> +struct feat_hw_info {
> +    union {
> +        struct psr_cat_hw_info l3_cat_info;
> +    };
> +};

Why don't you use an union here directly, instead of encapsulating an union
inside of a struct?

union feat_hw_info {
    struct psr_cat_hw_info l3_cat_info;
};

> +
> +struct feat_node;
> +
> +/*
> + * This structure defines feature operation callback functions. Every feature
> + * enabled MUST implement such callback functions and register them to ops.
> + *
> + * Feature specific behaviors will be encapsulated into these callback
> + * functions. Then, the main flows will not be changed when introducing a new
> + * feature.
> + */
> +struct feat_ops {
> +    /* get_cos_max is used to get feature's cos_max. */
> +    unsigned int (*get_cos_max)(const struct feat_node *feat);
> +};
> +
> +/*
> + * This structure represents one feature.
> + * feature     - Which feature it is.
> + * feat_ops    - Feature operation callback functions.
> + * info        - Feature HW info.
> + * cos_reg_val - Array to store the values of COS registers. One entry stores
> + *               the value of one COS register.
> + *               For L3 CAT and L2 CAT, one entry corresponds to one COS_ID.
> + *               For CDP, two entries correspond to one COS_ID. E.g.
> + *               COS_ID=0 corresponds to cos_reg_val[0] (Data) and
> + *               cos_reg_val[1] (Code).
> + * list        - Feature list.
> + */
> +struct feat_node {
> +    enum psr_feat_type feature;

If you index them in an array with the key being psr_feat_type I don't think
you need that field.

> +    struct feat_ops ops;

I would place the function hooks in this struct directly, instead of nesting
them inside of another struct. The hooks AFAICT are shared between all the
different PSR features.

> +    struct feat_hw_info info;

Same with this, you can place the actual union for storage here directly,
instead of nesting it inside of feat_hw_info, so:

    union {
        struct psr_cat_hw_info l3_cat_info;
    } hw_info;

Roger.
Yi Sun March 1, 2017, 5:10 a.m. UTC | #2
On 17-02-28 11:58:59, Roger Pau Monn� wrote:
> On Wed, Feb 15, 2017 at 04:49:18PM +0800, Yi Sun wrote:
> > To construct an extendible framework, we need analyze PSR features
> > and abstract the common things and feature specific things. Then,
> > encapsulate them into different data structures.
> > 
> > By analyzing PSR features, we can get below map.
> >                 +------+------+------+
> >       --------->| Dom0 | Dom1 | ...  |
> >       |         +------+------+------+
> >       |            |
> >       |Dom ID      | cos_id of domain
> >       |            V
> >       |        +-----------------------------------------------------------------------------+
> > User --------->| PSR                                                                         |
> >      Socket ID |  +--------------+---------------+---------------+                           |
> >                |  | Socket0 Info | Socket 1 Info |    ...        |                           |
> >                |  +--------------+---------------+---------------+                           |
> >                |    |                   cos_id=0               cos_id=1          ...         |
> >                |    |          +-----------------------+-----------------------+-----------+ |
> >                |    |->Ref   : |         ref 0         |         ref 1         | ...       | |
> >                |    |          +-----------------------+-----------------------+-----------+ |
> >                |    |          +-----------------------+-----------------------+-----------+ |
> >                |    |->L3 CAT: |         cos 0         |         cos 1         | ...       | |
> >                |    |          +-----------------------+-----------------------+-----------+ |
> >                |    |          +-----------------------+-----------------------+-----------+ |
> >                |    |->L2 CAT: |         cos 0         |         cos 1         | ...       | |
> >                |    |          +-----------------------+-----------------------+-----------+ |
> >                |    |          +-----------+-----------+-----------+-----------+-----------+ |
> >                |    |->CDP   : | cos0 code | cos0 data | cos1 code | cos1 data | ...       | |
> >                |               +-----------+-----------+-----------+-----------+-----------+ |
> >                +-----------------------------------------------------------------------------+
> > 
> > So, we need define a socket info data structure, 'struct
> > psr_socket_info' to manage information per socket. It contains a
> > reference count array according to COS ID and a feature list to
> > manage all features enabled. Every entry of the reference count
> > array is used to record how many domains are using the COS registers
> > according to the COS ID. For example, L3 CAT and L2 CAT are enabled,
> > Dom1 uses COS_ID=1 registers of both features to save CBM values, like
> > below.
> >         +-------+-------+-------+-----+
> >         | COS 0 | COS 1 | COS 2 | ... |
> >         +-------+-------+-------+-----+
> > L3 CAT  | 0x7ff | 0x1ff | ...   | ... |
> >         +-------+-------+-------+-----+
> > L2 CAT  | 0xff  | 0xff  | ...   | ... |
> >         +-------+-------+-------+-----+
> > 
> > If Dom2 has same CBM values, it can reuse these registers which COS_ID=1.
> > That means, both Dom1 and Dom2 use same COS registers(ID=1) to save same
> > L3/L2 values. So, the value ref[1] is 2 which means 2 domains are using
> > COS_ID 1.
> > 
> > To manage a feature, we need define a feature node data structure,
> > 'struct feat_node', to manage feature's specific HW info, its callback
> > functions (all feature's specific behaviors are encapsulated into these
> > callback functions), and an array of all COS registers values of this
> > feature.
> > 
> > CDP is a special feature which uses two entries of the array
> > for one COS ID. So, the number of CDP COS registers is the half of L3
> > CAT. E.g. L3 CAT has 16 COS registers, then CDP has 8 COS registers if
> > it is enabled. CDP uses the COS registers array as below.
> > 
> >                          +-----------+-----------+-----------+-----------+-----------+
> > CDP cos_reg_val[] index: |     0     |     1     |     2     |     3     |    ...    |
> >                          +-----------+-----------+-----------+-----------+-----------+
> >                   value: | cos0 code | cos0 data | cos1 code | cos1 data |    ...    |
> >                          +-----------+-----------+-----------+-----------+-----------+
> > 
> > For more details, please refer SDM and patches to implement 'get value' and
> > 'set value'.
> 
> I would recommend that you merge this with a patch that actually makes use of
> this structures, or else it's hard to review it's usage IMHO.
> 
Sorry for this. To make codes less and simpler in a patch, I split this patch
out to only show the data structures. I think I can merge it with next patch:
[PATCH v8 04/24] x86: refactor psr: implement CPU init and free flow.

How do you think?

> > +struct psr_socket_info {
> > +    /*
> > +     * It maps to values defined in 'enum psr_feat_type' below. Value in 'enum
> > +     * psr_feat_type' means the bit position.
> > +     * bit 0:   L3 CAT
> > +     * bit 1:   L3 CDP
> > +     * bit 2:   L2 CAT
> > +     */
> > +    unsigned int feat_mask;
> > +    unsigned int nr_feat;
> > +    struct list_head feat_list;
> 
> Isn't it a little bit overkill to use a list when there can only be a maximum
> of 3 features? (and the feat_mask is currently 32bits, so I guess you don't
> really foresee having more than 32 features).
> 
> I would suggest using:
> 
>      struct feat_node *features[PSR_SOCKET_MAX_FEAT];
> 
> (PSR_SOCKET_MAX_FEAT comes from the expansion of the enum below). Then you can
> simply use the enum value of each feature as the position of it's corresponding
> feat_node into the array.
> 
I really thought this before. But there may be different features enabled on
different sockets. For example, socket 0 enables L3 CAT and L2 CAT but socket 1
only supports L3 CAT. So, the feature array may be different for different
sockets. I think it is more complex to use array to handle all things than list.

> > +    unsigned int cos_ref[MAX_COS_REG_CNT];
> > +    spinlock_t ref_lock;
> > +};
> > +
> > +enum psr_feat_type {
> > +    PSR_SOCKET_L3_CAT = 0,
> > +    PSR_SOCKET_L3_CDP,
> > +    PSR_SOCKET_L2_CAT,
>     PSR_SOCKET_MAX_FEAT,
> > +};
> > +
> > +/* CAT/CDP HW info data structure. */
> > +struct psr_cat_hw_info {
> > +    unsigned int cbm_len;
> > +    unsigned int cos_max;
> > +};
> > +
> > +/* Encapsulate feature specific HW info here. */
> > +struct feat_hw_info {
> > +    union {
> > +        struct psr_cat_hw_info l3_cat_info;
> > +    };
> > +};
> 
> Why don't you use an union here directly, instead of encapsulating an union
> inside of a struct?
> 
> union feat_hw_info {
>     struct psr_cat_hw_info l3_cat_info;
> };
> 
> > +
> > +struct feat_node;
> > +
> > +/*
> > + * This structure defines feature operation callback functions. Every feature
> > + * enabled MUST implement such callback functions and register them to ops.
> > + *
> > + * Feature specific behaviors will be encapsulated into these callback
> > + * functions. Then, the main flows will not be changed when introducing a new
> > + * feature.
> > + */
> > +struct feat_ops {
> > +    /* get_cos_max is used to get feature's cos_max. */
> > +    unsigned int (*get_cos_max)(const struct feat_node *feat);
> > +};
> > +
> > +/*
> > + * This structure represents one feature.
> > + * feature     - Which feature it is.
> > + * feat_ops    - Feature operation callback functions.
> > + * info        - Feature HW info.
> > + * cos_reg_val - Array to store the values of COS registers. One entry stores
> > + *               the value of one COS register.
> > + *               For L3 CAT and L2 CAT, one entry corresponds to one COS_ID.
> > + *               For CDP, two entries correspond to one COS_ID. E.g.
> > + *               COS_ID=0 corresponds to cos_reg_val[0] (Data) and
> > + *               cos_reg_val[1] (Code).
> > + * list        - Feature list.
> > + */
> > +struct feat_node {
> > +    enum psr_feat_type feature;
> 
> If you index them in an array with the key being psr_feat_type I don't think
> you need that field.
> 
I need this to check if input type can match this feature, you can refer get
val or set val flow. Thanks!

> > +    struct feat_ops ops;
> 
> I would place the function hooks in this struct directly, instead of nesting
> them inside of another struct. The hooks AFAICT are shared between all the
> different PSR features.
> 
Jan suggested this before in v4 patch. We have discussed this and Jan accepts
current implementation. The reason is below:

"To facilitate the callback functions assignment for a feature, I defined
feature specific callback function ops like below.

struct feat_ops l3_cat_ops = {
    .init_feature = l3_cat_init_feature,
    .get_max_cos_max = l3_cat_get_max_cos_max,
    ......
};

And directly assign it to global feature node in initialization process like
below.

static void cpu_init_work(void)
{
......
            feat_tmp = feat_l3_cat;
            feat_l3_cat = NULL;
            feat_tmp->ops = l3_cat_ops;
......
}

I think this can make codes be clear."

> > +    struct feat_hw_info info;
> 
> Same with this, you can place the actual union for storage here directly,
> instead of nesting it inside of feat_hw_info, so:
> 
>     union {
>         struct psr_cat_hw_info l3_cat_info;
>     } hw_info;
> 
Thanks for the suggestion! Will do this.

> Roger.
Jan Beulich March 1, 2017, 8:17 a.m. UTC | #3
>>> On 01.03.17 at 06:10, <yi.y.sun@linux.intel.com> wrote:
> On 17-02-28 11:58:59, Roger Pau Monn wrote:
>> On Wed, Feb 15, 2017 at 04:49:18PM +0800, Yi Sun wrote:
>> > +    struct feat_ops ops;
>> 
>> I would place the function hooks in this struct directly, instead of nesting
>> them inside of another struct. The hooks AFAICT are shared between all the
>> different PSR features.
>> 
> Jan suggested this before in v4 patch. We have discussed this and Jan 
> accepts
> current implementation. The reason is below:

I'm pretty sure I didn't (in this specific form). Instead you want this
to be a pointer (to a const struct instance), i.e. ...

> "To facilitate the callback functions assignment for a feature, I defined
> feature specific callback function ops like below.
> 
> struct feat_ops l3_cat_ops = {
>     .init_feature = l3_cat_init_feature,
>     .get_max_cos_max = l3_cat_get_max_cos_max,
>     ......
> };
> 
> And directly assign it to global feature node in initialization process like
> below.
> 
> static void cpu_init_work(void)
> {
> ......
>             feat_tmp = feat_l3_cat;
>             feat_l3_cat = NULL;
>             feat_tmp->ops = l3_cat_ops;

            feat_tmp->ops = &l3_cat_ops;

Jan
Yi Sun March 1, 2017, 8:28 a.m. UTC | #4
On 17-03-01 01:17:22, Jan Beulich wrote:
> >>> On 01.03.17 at 06:10, <yi.y.sun@linux.intel.com> wrote:
> > On 17-02-28 11:58:59, Roger Pau Monn wrote:
> >> On Wed, Feb 15, 2017 at 04:49:18PM +0800, Yi Sun wrote:
> >> > +    struct feat_ops ops;
> >> 
> >> I would place the function hooks in this struct directly, instead of nesting
> >> them inside of another struct. The hooks AFAICT are shared between all the
> >> different PSR features.
> >> 
> > Jan suggested this before in v4 patch. We have discussed this and Jan 
> > accepts
> > current implementation. The reason is below:
> 
> I'm pretty sure I didn't (in this specific form). Instead you want this
> to be a pointer (to a const struct instance), i.e. ...
> 
Sorry for confusion. I think you suggested same thing (maybe I misunderstood?).

Quoted from v4:
"What is the reason to have a separate structure for this, when you
don't store a pointer in struct feat_node? If this was inlined there,
the odd forward declaration of struct feat_node wouldn't be needed
either. (The same question may apply to struct feat_hw_info.)"

So I explained as below and asked if you can accept such implementation. And
got your positive ack. Of course, I should declare l3_cat_ops to const.

> > "To facilitate the callback functions assignment for a feature, I defined
> > feature specific callback function ops like below.
> > 
> > struct feat_ops l3_cat_ops = {
> >     .init_feature = l3_cat_init_feature,
> >     .get_max_cos_max = l3_cat_get_max_cos_max,
> >     ......
> > };
> > 
> > And directly assign it to global feature node in initialization process like
> > below.
> > 
> > static void cpu_init_work(void)
> > {
> > ......
> >             feat_tmp = feat_l3_cat;
> >             feat_l3_cat = NULL;
> >             feat_tmp->ops = l3_cat_ops;
> 
>             feat_tmp->ops = &l3_cat_ops;
> 
> Jan
Jan Beulich March 1, 2017, 8:39 a.m. UTC | #5
>>> On 01.03.17 at 09:28, <yi.y.sun@linux.intel.com> wrote:
> On 17-03-01 01:17:22, Jan Beulich wrote:
>> >>> On 01.03.17 at 06:10, <yi.y.sun@linux.intel.com> wrote:
>> > On 17-02-28 11:58:59, Roger Pau Monn wrote:
>> >> On Wed, Feb 15, 2017 at 04:49:18PM +0800, Yi Sun wrote:
>> >> > +    struct feat_ops ops;
>> >> 
>> >> I would place the function hooks in this struct directly, instead of 
> nesting
>> >> them inside of another struct. The hooks AFAICT are shared between all the
>> >> different PSR features.
>> >> 
>> > Jan suggested this before in v4 patch. We have discussed this and Jan 
>> > accepts
>> > current implementation. The reason is below:
>> 
>> I'm pretty sure I didn't (in this specific form). Instead you want this
>> to be a pointer (to a const struct instance), i.e. ...
>> 
> Sorry for confusion. I think you suggested same thing (maybe I 
> misunderstood?).
> 
> Quoted from v4:
> "What is the reason to have a separate structure for this, when you
> don't store a pointer in struct feat_node? If this was inlined there,
> the odd forward declaration of struct feat_node wouldn't be needed
> either. (The same question may apply to struct feat_hw_info.)"
> 
> So I explained as below and asked if you can accept such implementation. And
> got your positive ack. Of course, I should declare l3_cat_ops to const.

In which case an earlier question to answer is: Why can it not be a
pointer that gets stored?

Jan
Roger Pau Monné March 1, 2017, 8:49 a.m. UTC | #6
On Wed, Mar 01, 2017 at 01:10:02PM +0800, Yi Sun wrote:
> On 17-02-28 11:58:59, Roger Pau Monn� wrote:
> > On Wed, Feb 15, 2017 at 04:49:18PM +0800, Yi Sun wrote:
> > > To construct an extendible framework, we need analyze PSR features
> > > and abstract the common things and feature specific things. Then,
> > > encapsulate them into different data structures.
> > > 
> > > By analyzing PSR features, we can get below map.
> > >                 +------+------+------+
> > >       --------->| Dom0 | Dom1 | ...  |
> > >       |         +------+------+------+
> > >       |            |
> > >       |Dom ID      | cos_id of domain
> > >       |            V
> > >       |        +-----------------------------------------------------------------------------+
> > > User --------->| PSR                                                                         |
> > >      Socket ID |  +--------------+---------------+---------------+                           |
> > >                |  | Socket0 Info | Socket 1 Info |    ...        |                           |
> > >                |  +--------------+---------------+---------------+                           |
> > >                |    |                   cos_id=0               cos_id=1          ...         |
> > >                |    |          +-----------------------+-----------------------+-----------+ |
> > >                |    |->Ref   : |         ref 0         |         ref 1         | ...       | |
> > >                |    |          +-----------------------+-----------------------+-----------+ |
> > >                |    |          +-----------------------+-----------------------+-----------+ |
> > >                |    |->L3 CAT: |         cos 0         |         cos 1         | ...       | |
> > >                |    |          +-----------------------+-----------------------+-----------+ |
> > >                |    |          +-----------------------+-----------------------+-----------+ |
> > >                |    |->L2 CAT: |         cos 0         |         cos 1         | ...       | |
> > >                |    |          +-----------------------+-----------------------+-----------+ |
> > >                |    |          +-----------+-----------+-----------+-----------+-----------+ |
> > >                |    |->CDP   : | cos0 code | cos0 data | cos1 code | cos1 data | ...       | |
> > >                |               +-----------+-----------+-----------+-----------+-----------+ |
> > >                +-----------------------------------------------------------------------------+
> > > 
> > > So, we need define a socket info data structure, 'struct
> > > psr_socket_info' to manage information per socket. It contains a
> > > reference count array according to COS ID and a feature list to
> > > manage all features enabled. Every entry of the reference count
> > > array is used to record how many domains are using the COS registers
> > > according to the COS ID. For example, L3 CAT and L2 CAT are enabled,
> > > Dom1 uses COS_ID=1 registers of both features to save CBM values, like
> > > below.
> > >         +-------+-------+-------+-----+
> > >         | COS 0 | COS 1 | COS 2 | ... |
> > >         +-------+-------+-------+-----+
> > > L3 CAT  | 0x7ff | 0x1ff | ...   | ... |
> > >         +-------+-------+-------+-----+
> > > L2 CAT  | 0xff  | 0xff  | ...   | ... |
> > >         +-------+-------+-------+-----+
> > > 
> > > If Dom2 has same CBM values, it can reuse these registers which COS_ID=1.
> > > That means, both Dom1 and Dom2 use same COS registers(ID=1) to save same
> > > L3/L2 values. So, the value ref[1] is 2 which means 2 domains are using
> > > COS_ID 1.
> > > 
> > > To manage a feature, we need define a feature node data structure,
> > > 'struct feat_node', to manage feature's specific HW info, its callback
> > > functions (all feature's specific behaviors are encapsulated into these
> > > callback functions), and an array of all COS registers values of this
> > > feature.
> > > 
> > > CDP is a special feature which uses two entries of the array
> > > for one COS ID. So, the number of CDP COS registers is the half of L3
> > > CAT. E.g. L3 CAT has 16 COS registers, then CDP has 8 COS registers if
> > > it is enabled. CDP uses the COS registers array as below.
> > > 
> > >                          +-----------+-----------+-----------+-----------+-----------+
> > > CDP cos_reg_val[] index: |     0     |     1     |     2     |     3     |    ...    |
> > >                          +-----------+-----------+-----------+-----------+-----------+
> > >                   value: | cos0 code | cos0 data | cos1 code | cos1 data |    ...    |
> > >                          +-----------+-----------+-----------+-----------+-----------+
> > > 
> > > For more details, please refer SDM and patches to implement 'get value' and
> > > 'set value'.
> > 
> > I would recommend that you merge this with a patch that actually makes use of
> > this structures, or else it's hard to review it's usage IMHO.
> > 
> Sorry for this. To make codes less and simpler in a patch, I split this patch
> out to only show the data structures. I think I can merge it with next patch:
> [PATCH v8 04/24] x86: refactor psr: implement CPU init and free flow.
> 
> How do you think?

Now that I've looked at the other patches this doesn't seem so abstract to me,
I will leave that to the x86 maintainers, and whether they want to join it with
some actual code or not.

> > > +struct psr_socket_info {
> > > +    /*
> > > +     * It maps to values defined in 'enum psr_feat_type' below. Value in 'enum
> > > +     * psr_feat_type' means the bit position.
> > > +     * bit 0:   L3 CAT
> > > +     * bit 1:   L3 CDP
> > > +     * bit 2:   L2 CAT
> > > +     */
> > > +    unsigned int feat_mask;
> > > +    unsigned int nr_feat;
> > > +    struct list_head feat_list;
> > 
> > Isn't it a little bit overkill to use a list when there can only be a maximum
> > of 3 features? (and the feat_mask is currently 32bits, so I guess you don't
> > really foresee having more than 32 features).
> > 
> > I would suggest using:
> > 
> >      struct feat_node *features[PSR_SOCKET_MAX_FEAT];
> > 
> > (PSR_SOCKET_MAX_FEAT comes from the expansion of the enum below). Then you can
> > simply use the enum value of each feature as the position of it's corresponding
> > feat_node into the array.
> > 
> I really thought this before. But there may be different features enabled on
> different sockets. For example, socket 0 enables L3 CAT and L2 CAT but socket 1
> only supports L3 CAT. So, the feature array may be different for different
> sockets. I think it is more complex to use array to handle all things than list.

Different sockets with different features enabled should still be fine. Each
socket has a feat_mask, and you can use that to know whether a certain feature
is enabled or not.

What I was proposing is to make feat_node an array of pointers, so if a feature
is not supported that specific pointer would be NULL (ie: if feat_mask &
PSR_SOCKET_L3_CDP == 0, features[PSR_SOCKET_L3_CDP] == NULL). And then you can
avoid all the list traversing code.

> > > +    unsigned int cos_ref[MAX_COS_REG_CNT];
> > > +    spinlock_t ref_lock;
> > > +};
> > > +
> > > +enum psr_feat_type {
> > > +    PSR_SOCKET_L3_CAT = 0,
> > > +    PSR_SOCKET_L3_CDP,
> > > +    PSR_SOCKET_L2_CAT,
> >     PSR_SOCKET_MAX_FEAT,
> > > +};
> > > +
> > > +/* CAT/CDP HW info data structure. */
> > > +struct psr_cat_hw_info {
> > > +    unsigned int cbm_len;
> > > +    unsigned int cos_max;
> > > +};
> > > +
> > > +/* Encapsulate feature specific HW info here. */
> > > +struct feat_hw_info {
> > > +    union {
> > > +        struct psr_cat_hw_info l3_cat_info;
> > > +    };
> > > +};
> > 
> > Why don't you use an union here directly, instead of encapsulating an union
> > inside of a struct?
> > 
> > union feat_hw_info {
> >     struct psr_cat_hw_info l3_cat_info;
> > };
> > 
> > > +
> > > +struct feat_node;
> > > +
> > > +/*
> > > + * This structure defines feature operation callback functions. Every feature
> > > + * enabled MUST implement such callback functions and register them to ops.
> > > + *
> > > + * Feature specific behaviors will be encapsulated into these callback
> > > + * functions. Then, the main flows will not be changed when introducing a new
> > > + * feature.
> > > + */
> > > +struct feat_ops {
> > > +    /* get_cos_max is used to get feature's cos_max. */
> > > +    unsigned int (*get_cos_max)(const struct feat_node *feat);
> > > +};
> > > +
> > > +/*
> > > + * This structure represents one feature.
> > > + * feature     - Which feature it is.
> > > + * feat_ops    - Feature operation callback functions.
> > > + * info        - Feature HW info.
> > > + * cos_reg_val - Array to store the values of COS registers. One entry stores
> > > + *               the value of one COS register.
> > > + *               For L3 CAT and L2 CAT, one entry corresponds to one COS_ID.
> > > + *               For CDP, two entries correspond to one COS_ID. E.g.
> > > + *               COS_ID=0 corresponds to cos_reg_val[0] (Data) and
> > > + *               cos_reg_val[1] (Code).
> > > + * list        - Feature list.
> > > + */
> > > +struct feat_node {
> > > +    enum psr_feat_type feature;
> > 
> > If you index them in an array with the key being psr_feat_type I don't think
> > you need that field.
> > 
> I need this to check if input type can match this feature, you can refer get
> val or set val flow. Thanks!

If you move to the array of pointers proposed above you no longer need this
since you can just do features[PSR_SOCKET_L3_CAT] and get the pointer to the
feat_node struct, the index into the array is going to be the feature type
already.

> > > +    struct feat_ops ops;
> > 
> > I would place the function hooks in this struct directly, instead of nesting
> > them inside of another struct. The hooks AFAICT are shared between all the
> > different PSR features.
> > 
> Jan suggested this before in v4 patch. We have discussed this and Jan accepts
> current implementation. The reason is below:
> 
> "To facilitate the callback functions assignment for a feature, I defined
> feature specific callback function ops like below.
> 
> struct feat_ops l3_cat_ops = {
>     .init_feature = l3_cat_init_feature,
>     .get_max_cos_max = l3_cat_get_max_cos_max,
>     ......
> };

This should be constified as Jan already pointed out.

I see that what I said before doesn't make sense, since you have both constant
functions pointers (that can be shared across nodes), plus local node storage
in this structure.

Roger.
Jan Beulich March 1, 2017, 8:54 a.m. UTC | #7
>>> On 01.03.17 at 09:49, <roger.pau@citrix.com> wrote:
> What I was proposing is to make feat_node an array of pointers, so if a feature
> is not supported that specific pointer would be NULL (ie: if feat_mask &
> PSR_SOCKET_L3_CDP == 0, features[PSR_SOCKET_L3_CDP] == NULL). And then you can
> avoid all the list traversing code.

In which case the feature flags would look to become redundant too.

Jan
Roger Pau Monné March 1, 2017, 9 a.m. UTC | #8
On Wed, Mar 01, 2017 at 01:54:00AM -0700, Jan Beulich wrote:
> >>> On 01.03.17 at 09:49, <roger.pau@citrix.com> wrote:
> > What I was proposing is to make feat_node an array of pointers, so if a feature
> > is not supported that specific pointer would be NULL (ie: if feat_mask &
> > PSR_SOCKET_L3_CDP == 0, features[PSR_SOCKET_L3_CDP] == NULL). And then you can
> > avoid all the list traversing code.
> 
> In which case the feature flags would look to become redundant too.

I guess so, I'm just not sure if you can have a feature available (so
feat_mask bit set) but not enabled (so features[...] == NULL). Not sure if that
makes sense or is desirable at all.

Roger.
diff mbox

Patch

diff --git a/xen/arch/x86/psr.c b/xen/arch/x86/psr.c
index 96a8589..5acd9ca 100644
--- a/xen/arch/x86/psr.c
+++ b/xen/arch/x86/psr.c
@@ -13,16 +13,122 @@ 
  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  * more details.
  */
-#include <xen/init.h>
 #include <xen/cpu.h>
 #include <xen/err.h>
+#include <xen/init.h>
+#include <xen/list.h>
 #include <xen/sched.h>
 #include <asm/psr.h>
 
+/*
+ * Terminology:
+ * - CAT         Cache Allocation Technology
+ * - CBM         Capacity BitMasks
+ * - CDP         Code and Data Prioritization
+ * - COS/CLOS    Class of Service. Also mean COS registers.
+ * - COS_MAX     Max number of COS for the feature (minus 1)
+ * - MSRs        Machine Specific Registers
+ * - PSR         Intel Platform Shared Resource
+ */
+
 #define PSR_CMT        (1<<0)
 #define PSR_CAT        (1<<1)
 #define PSR_CDP        (1<<2)
 
+/*
+ * Per SDM chapter 'Cache Allocation Technology: Cache Mask Configuration',
+ * the MSRs ranging from 0C90H through 0D0FH (inclusive), enables support for
+ * up to 128 L3 CAT Classes of Service. The COS_ID=[0,127].
+ *
+ * The MSRs ranging from 0D10H through 0D4FH (inclusive), enables support for
+ * up to 64 L2 CAT COS. The COS_ID=[0,63].
+ *
+ * So, the maximum COS register count of one feature is 128.
+ */
+#define MAX_COS_REG_CNT  128
+
+/*
+ * PSR features are managed per socket. Below structure defines the members
+ * used to manage these features.
+ * feat_mask - Mask used to record features enabled on socket. There may be
+ *             some features enabled at same time.
+ * nr_feat   - Record how many features enabled.
+ * feat_list - A list used to manage all features enabled.
+ * cos_ref   - A reference count array to record how many domains are using the
+ *             COS_ID.
+ *             Every entry of cos_ref corresponds to one COS ID.
+ * ref_lock  - A lock to protect cos_ref.
+ */
+struct psr_socket_info {
+    /*
+     * It maps to values defined in 'enum psr_feat_type' below. Value in 'enum
+     * psr_feat_type' means the bit position.
+     * bit 0:   L3 CAT
+     * bit 1:   L3 CDP
+     * bit 2:   L2 CAT
+     */
+    unsigned int feat_mask;
+    unsigned int nr_feat;
+    struct list_head feat_list;
+    unsigned int cos_ref[MAX_COS_REG_CNT];
+    spinlock_t ref_lock;
+};
+
+enum psr_feat_type {
+    PSR_SOCKET_L3_CAT = 0,
+    PSR_SOCKET_L3_CDP,
+    PSR_SOCKET_L2_CAT,
+};
+
+/* CAT/CDP HW info data structure. */
+struct psr_cat_hw_info {
+    unsigned int cbm_len;
+    unsigned int cos_max;
+};
+
+/* Encapsulate feature specific HW info here. */
+struct feat_hw_info {
+    union {
+        struct psr_cat_hw_info l3_cat_info;
+    };
+};
+
+struct feat_node;
+
+/*
+ * This structure defines feature operation callback functions. Every feature
+ * enabled MUST implement such callback functions and register them to ops.
+ *
+ * Feature specific behaviors will be encapsulated into these callback
+ * functions. Then, the main flows will not be changed when introducing a new
+ * feature.
+ */
+struct feat_ops {
+    /* get_cos_max is used to get feature's cos_max. */
+    unsigned int (*get_cos_max)(const struct feat_node *feat);
+};
+
+/*
+ * This structure represents one feature.
+ * feature     - Which feature it is.
+ * feat_ops    - Feature operation callback functions.
+ * info        - Feature HW info.
+ * cos_reg_val - Array to store the values of COS registers. One entry stores
+ *               the value of one COS register.
+ *               For L3 CAT and L2 CAT, one entry corresponds to one COS_ID.
+ *               For CDP, two entries correspond to one COS_ID. E.g.
+ *               COS_ID=0 corresponds to cos_reg_val[0] (Data) and
+ *               cos_reg_val[1] (Code).
+ * list        - Feature list.
+ */
+struct feat_node {
+    enum psr_feat_type feature;
+    struct feat_ops ops;
+    struct feat_hw_info info;
+    uint64_t cos_reg_val[MAX_COS_REG_CNT];
+    struct list_head list;
+};
+
 struct psr_assoc {
     uint64_t val;
     uint64_t cos_mask;