[xen master] x86: refactor psr: L3 CAT: implement main data structures, CPU init and free flows.

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

[xen master] x86: refactor psr: L3 CAT: implement main data structures, CPU init and free flows.

patchbot
commit 5fc1c4badbf163ec15184fa16ef08e72b59bb688
Author:     Yi Sun <[hidden email]>
AuthorDate: Tue Aug 1 11:04:00 2017 +0200
Commit:     Jan Beulich <[hidden email]>
CommitDate: Thu Aug 3 12:32:13 2017 +0200

    x86: refactor psr: L3 CAT: implement main data structures, CPU init and free flows.
   
    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 array 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 keep same
    L3/L2 values. So, the value of 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, and an array of all
    COS registers values of this feature.
   
    To manage feature properties, we need define a feature property data structure,
    'struct feat_props', to manage common properties (callback functions - all
    feature's specific behaviors are encapsulated into these callback functions,
    and generic values - e.g. the cos_max), the feature independent values.
   
    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'.
   
    This patch also implements the CPU init and free flow including L3 CAT
    initialization and some resources free. It includes below flows:
    1. presmp init:
        - parse command line parameter.
        - allocate socket info for every socket.
        - allocate feature resource.
        - initialize socket info, get feature info and add feature into feature
          array per cpuid result.
        - free resources allocated if error happens.
        - register cpu notifier to handle cpu events.
    2. cpu notifier:
        - handle cpu online events, if initialization work has been done before,
          do nothing.
        - handle cpu offline events, if it is the last cpu offline, free some
          socket resources.
   
    Signed-off-by: Yi Sun <[hidden email]>
    Reviewed-by: Jan Beulich <[hidden email]>
---
 xen/arch/x86/psr.c        | 294 +++++++++++++++++++++++++++++++++++++++++++++-
 xen/include/asm-x86/psr.h |   1 +
 2 files changed, 289 insertions(+), 6 deletions(-)

diff --git a/xen/arch/x86/psr.c b/xen/arch/x86/psr.c
index 96a8589..39d8581 100644
--- a/xen/arch/x86/psr.c
+++ b/xen/arch/x86/psr.c
@@ -13,16 +13,118 @@
  * 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/sched.h>
 #include <asm/psr.h>
 
+/*
+ * Terminology:
+ * - CAT         Cache Allocation Technology
+ * - CBM         Capacity BitMasks
+ * - CDP         Code and Data Prioritization
+ * - CMT         Cache Monitoring Technology
+ * - 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)
 
+#define CAT_CBM_LEN_MASK 0x1f
+#define CAT_COS_MAX_MASK 0xffff
+
+/*
+ * 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
+
+/*
+ * Every PSR feature uses some COS registers for each COS ID, e.g. CDP uses 2
+ * COS registers (DATA and CODE) for one COS ID, but CAT uses 1 COS register.
+ * We use below macro as the max number of COS registers used by all features.
+ * So far, it is 2 which means CDP's COS registers number.
+ */
+#define MAX_COS_NUM 2
+
+enum psr_feat_type {
+    FEAT_TYPE_L3_CAT,
+    FEAT_TYPE_NUM,
+};
+
+/*
+ * This structure represents one feature.
+ * cos_max     - The max COS registers number got through CPUID.
+ * cbm_len     - The length of CBM got through CPUID.
+ * 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).
+ */
+struct feat_node {
+    /* cos_max and cbm_len are common values for all features so far. */
+    unsigned int cos_max;
+    unsigned int cbm_len;
+    uint32_t cos_reg_val[MAX_COS_REG_CNT];
+};
+
+/*
+ * This structure defines feature specific values, e.g. cos_num.
+ *
+ * Array 'feat_props' is defined to save every feature's properties. We use
+ * 'enum psr_feat_type' as index.
+ */
+static const struct feat_props {
+    /*
+     * cos_num - COS registers number that feature uses for one COS ID.
+     *           It is defined in SDM.
+     */
+    unsigned int cos_num;
+
+    /*
+     * An array to save all 'enum cbm_type' values of the feature. It is
+     * used with cos_num together to get/write a feature's COS registers
+     * values one by one.
+     */
+    enum cbm_type type[MAX_COS_NUM];
+
+    /*
+     * alt_type is 'alternative type'. When this 'alt_type' is input, the
+     * feature does some special operations.
+     */
+    enum cbm_type alt_type;
+} *feat_props[FEAT_TYPE_NUM];
+
+/*
+ * PSR features are managed per socket. Below structure defines the members
+ * used to manage these features.
+ * feat_init - Indicate if features on a socket have been initialized.
+ * features  - A feature node array used to manage all features enabled.
+ * ref_lock  - A lock to protect cos_ref.
+ * 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.
+ */
+struct psr_socket_info {
+    bool feat_init;
+    /* Feature array's index is 'enum psr_feat_type' which is same as 'props' */
+    struct feat_node *features[FEAT_TYPE_NUM];
+    spinlock_t ref_lock;
+    unsigned int cos_ref[MAX_COS_REG_CNT];
+};
+
 struct psr_assoc {
     uint64_t val;
     uint64_t cos_mask;
@@ -30,11 +132,105 @@ struct psr_assoc {
 
 struct psr_cmt *__read_mostly psr_cmt;
 
+static struct psr_socket_info *__read_mostly socket_info;
+
 static unsigned int opt_psr;
 static unsigned int __initdata opt_rmid_max = 255;
+static unsigned int __read_mostly opt_cos_max = MAX_COS_REG_CNT;
 static uint64_t rmid_mask;
 static DEFINE_PER_CPU(struct psr_assoc, psr_assoc);
 
+/*
+ * Declare global feature node for every feature to facilitate the feature
+ * array creation. It is used to transiently store a spare node.
+ */
+static struct feat_node *feat_l3;
+
+/* Common functions */
+#define cat_default_val(len) (0xffffffff >> (32 - (len)))
+
+/*
+ * Use this function to check if any allocation feature has been enabled
+ * in cmdline.
+ */
+static bool psr_alloc_feat_enabled(void)
+{
+    return !!socket_info;
+}
+
+static void free_socket_resources(unsigned int socket)
+{
+    unsigned int i;
+    struct psr_socket_info *info = socket_info + socket;
+
+    if ( !info )
+        return;
+
+    /*
+     * Free resources of features. The global feature object, e.g. feat_l3,
+     * may not be freed here if it is not added into array. It is simply being
+     * kept until the next CPU online attempt.
+     */
+    for ( i = 0; i < ARRAY_SIZE(info->features); i++ )
+    {
+        xfree(info->features[i]);
+        info->features[i] = NULL;
+    }
+
+    info->feat_init = false;
+
+    memset(info->cos_ref, 0, MAX_COS_REG_CNT * sizeof(unsigned int));
+}
+
+/* CAT common functions implementation. */
+static int cat_init_feature(const struct cpuid_leaf *regs,
+                            struct feat_node *feat,
+                            struct psr_socket_info *info,
+                            enum psr_feat_type type)
+{
+    /* No valid value so do not enable feature. */
+    if ( !regs->a || !regs->d )
+        return -ENOENT;
+
+    feat->cbm_len = (regs->a & CAT_CBM_LEN_MASK) + 1;
+    feat->cos_max = min(opt_cos_max, regs->d & CAT_COS_MAX_MASK);
+
+    switch ( type )
+    {
+    case FEAT_TYPE_L3_CAT:
+        if ( feat->cos_max < 1 )
+            return -ENOENT;
+
+        /* We reserve cos=0 as default cbm (all bits within cbm_len are 1). */
+        feat->cos_reg_val[0] = cat_default_val(feat->cbm_len);
+
+        wrmsrl(MSR_IA32_PSR_L3_MASK(0), cat_default_val(feat->cbm_len));
+
+        break;
+
+    default:
+        return -ENOENT;
+    }
+
+    /* Add this feature into array. */
+    info->features[type] = feat;
+
+    if ( !opt_cpu_info )
+        return 0;
+
+    printk(XENLOG_INFO "CAT: enabled on socket %u, cos_max:%u, cbm_len:%u\n",
+           cpu_to_socket(smp_processor_id()), feat->cos_max, feat->cbm_len);
+
+    return 0;
+}
+
+/* L3 CAT props */
+static const struct feat_props l3_cat_props = {
+    .cos_num = 1,
+    .type[0] = PSR_CBM_TYPE_L3,
+    .alt_type = PSR_CBM_TYPE_UNKNOWN,
+};
+
 static void __init parse_psr_bool(char *s, char *value, char *feature,
                                   unsigned int mask)
 {
@@ -74,6 +270,9 @@ static void __init parse_psr_param(char *s)
         if ( val_str && !strcmp(s, "rmid_max") )
             opt_rmid_max = simple_strtoul(val_str, NULL, 0);
 
+        if ( val_str && !strcmp(s, "cos_max") )
+            opt_cos_max = simple_strtoul(val_str, NULL, 0);
+
         s = ss + 1;
     } while ( ss );
 }
@@ -229,19 +428,98 @@ void psr_domain_free(struct domain *d)
     psr_free_rmid(d);
 }
 
-static int psr_cpu_prepare(unsigned int cpu)
+static void __init init_psr(void)
 {
+    if ( opt_cos_max < 1 )
+    {
+        printk(XENLOG_INFO "CAT: disabled, cos_max is too small\n");
+        return;
+    }
+
+    socket_info = xzalloc_array(struct psr_socket_info, nr_sockets);
+
+    if ( !socket_info )
+    {
+        printk(XENLOG_WARNING "Failed to alloc socket_info!\n");
+        return;
+    }
+}
+
+static void __init psr_free(void)
+{
+    xfree(socket_info);
+    socket_info = NULL;
+}
+
+static int psr_cpu_prepare(void)
+{
+    if ( !psr_alloc_feat_enabled() )
+        return 0;
+
+    /* Malloc memory for the global feature node here. */
+    if ( feat_l3 == NULL &&
+         (feat_l3 = xzalloc(struct feat_node)) == NULL )
+        return -ENOMEM;
+
     return 0;
 }
 
 static void psr_cpu_init(void)
 {
+    struct psr_socket_info *info;
+    unsigned int socket, cpu = smp_processor_id();
+    struct feat_node *feat;
+    struct cpuid_leaf regs;
+
+    if ( !psr_alloc_feat_enabled() || !boot_cpu_has(X86_FEATURE_PQE) )
+        goto assoc_init;
+
+    if ( boot_cpu_data.cpuid_level < PSR_CPUID_LEVEL_CAT )
+    {
+        setup_clear_cpu_cap(X86_FEATURE_PQE);
+        goto assoc_init;
+    }
+
+    socket = cpu_to_socket(cpu);
+    info = socket_info + socket;
+    if ( info->feat_init )
+        goto assoc_init;
+
+    spin_lock_init(&info->ref_lock);
+
+    cpuid_count_leaf(PSR_CPUID_LEVEL_CAT, 0, &regs);
+    if ( regs.b & PSR_RESOURCE_TYPE_L3 )
+    {
+        cpuid_count_leaf(PSR_CPUID_LEVEL_CAT, 1, &regs);
+
+        feat = feat_l3;
+        feat_l3 = NULL;
+
+        if ( !cat_init_feature(&regs, feat, info, FEAT_TYPE_L3_CAT) )
+            feat_props[FEAT_TYPE_L3_CAT] = &l3_cat_props;
+        else
+            feat_l3 = feat;
+    }
+
+    info->feat_init = true;
+
+ assoc_init:
     psr_assoc_init();
 }
 
 static void psr_cpu_fini(unsigned int cpu)
 {
-    return;
+    unsigned int socket = cpu_to_socket(cpu);
+
+    if ( !psr_alloc_feat_enabled() )
+        return;
+
+    /*
+     * We only free when we are the last CPU in the socket. The socket_cpumask
+     * is cleared prior to this notification code by remove_siblinginfo().
+     */
+    if ( socket_cpumask[socket] && cpumask_empty(socket_cpumask[socket]) )
+        free_socket_resources(socket);
 }
 
 static int cpu_callback(
@@ -253,7 +531,7 @@ static int cpu_callback(
     switch ( action )
     {
     case CPU_UP_PREPARE:
-        rc = psr_cpu_prepare(cpu);
+        rc = psr_cpu_prepare();
         break;
     case CPU_STARTING:
         psr_cpu_init();
@@ -282,10 +560,14 @@ static int __init psr_presmp_init(void)
     if ( (opt_psr & PSR_CMT) && opt_rmid_max )
         init_psr_cmt(opt_rmid_max);
 
-    psr_cpu_prepare(0);
+    if ( opt_psr & PSR_CAT )
+        init_psr();
+
+    if ( psr_cpu_prepare() )
+        psr_free();
 
     psr_cpu_init();
-    if ( psr_cmt_enabled() )
+    if ( psr_cmt_enabled() || psr_alloc_feat_enabled() )
         register_cpu_notifier(&cpu_nfb);
 
     return 0;
diff --git a/xen/include/asm-x86/psr.h b/xen/include/asm-x86/psr.h
index 57f47e9..8141336 100644
--- a/xen/include/asm-x86/psr.h
+++ b/xen/include/asm-x86/psr.h
@@ -50,6 +50,7 @@ enum cbm_type {
     PSR_CBM_TYPE_L3,
     PSR_CBM_TYPE_L3_CODE,
     PSR_CBM_TYPE_L3_DATA,
+    PSR_CBM_TYPE_UNKNOWN,
 };
 
 extern struct psr_cmt *psr_cmt;
--
generated by git-patchbot for /home/xen/git/xen.git#master

_______________________________________________
Xen-changelog mailing list
[hidden email]
https://lists.xenproject.org/xen-changelog
Loading...