diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown
index 9797c8d..dbf5d4c 100644
--- a/docs/misc/xen-command-line.markdown
+++ b/docs/misc/xen-command-line.markdown
@@ -567,7 +567,7 @@ also slow in responding to load changes.
 The default value of `1 sec` is rather long.
 
 ### credit2\_runqueue
-> `= cpu | core | socket | node | all`
+> `= cpu | core | socket | node | all | <custom>`
 
 > Default: `socket`
 
@@ -585,6 +585,16 @@ Available alternatives, with their meaning, are:
 * `node`: one runqueue per each NUMA node of the host;
 * `all`: just one runqueue shared by all the logical pCPUs of
          the host
+* `<custom>`: one runqueue per each specified subset of logical
+              pCPUs of the host. Subsets are defined either as:
+              `[[0,1,][2,6][3,5][4,7]]`, or as:
+              `'0,1;2,6;3,5;4,7'`. That means
+               - pCPUs 0 and 1 belong to runqueue 0
+               - pCPUs 2 and 6 belong to runqueue 1
+               - pCPUs 3 and 5 belong to runqueue 2
+               - pCPUs 4 and 7 belong to runqueue 3
+              pCPUs that are present on the host, but are not
+              part of any subset, are assigned to runqueue 0.
 
 ### dbgp
 > `= ehci[ <integer> | @pci<bus>:<slot>.<func> ]`
diff --git a/xen/common/sched_credit2.c b/xen/common/sched_credit2.c
index 0f93ad5..10da084 100644
--- a/xen/common/sched_credit2.c
+++ b/xen/common/sched_credit2.c
@@ -12,6 +12,7 @@
 
 #include <xen/init.h>
 #include <xen/lib.h>
+#include <xen/ctype.h>
 #include <xen/sched.h>
 #include <xen/domain.h>
 #include <xen/delay.h>
@@ -323,6 +324,17 @@ integer_param("credit2_balance_over", opt_overload_balance_tolerance);
  *           (logical) processors of the host belong. This will happen if
  *           the opt_runqueue parameter is set to 'all'.
  *
+ * - custom: meaning that there will be one runqueue per each specified
+ *           subset, as shown in the following examples:
+ *           - credit2_runqueue=[[0,1][3][4,5]]
+ *           - credit2_runqueue='0,1;3;4,5'
+ *           These (both) mean the following:
+ *           - CPU 0 and CPU 1 belong to runqueue 0
+ *           - CPU 3 belongs to runqueue 1
+ *           - CPU 4 and CPU 5 belong to runqueue 2
+ *           CPUs that are present on the host, but are not part of any
+ *           defined subset, will be assigned to runqueue 0.
+ *
  * Depending on the value of opt_runqueue, therefore, cpus that are part of
  * either the same physical core, the same physical socket, the same NUMA
  * node, or just all of them, will be put together to form runqueues.
@@ -332,6 +344,7 @@ integer_param("credit2_balance_over", opt_overload_balance_tolerance);
 #define OPT_RUNQUEUE_SOCKET 2
 #define OPT_RUNQUEUE_NODE   3
 #define OPT_RUNQUEUE_ALL    4
+#define OPT_RUNQUEUE_CUSTOM 5
 static const char *const opt_runqueue_str[] = {
     [OPT_RUNQUEUE_CPU] = "cpu",
     [OPT_RUNQUEUE_CORE] = "core",
@@ -341,6 +354,115 @@ static const char *const opt_runqueue_str[] = {
 };
 static int __read_mostly opt_runqueue = OPT_RUNQUEUE_SOCKET;
 
+static unsigned int __read_mostly custom_cpu_runqueue[NR_CPUS];
+
+static int parse_custom_runqueue(const char *s)
+{
+    unsigned int cpu, rqi = 0;
+    bool in_subset = false, ret = true;
+    cpumask_t cpus;
+
+    /*
+     * If we are dealing with format 1 (i.e., [[0,1][3,5]]), first
+     * and last character must be '[' and ']', respectively.
+     *
+     * If we are dealing with format 2 (i.e., 0,1;3,5), first and
+     * last character must be numbers.
+     */
+    if ( *s == '[' && *(s+strlen(s)-1) == ']' )
+        s++;
+    else if ( !(isdigit(*s) && isdigit(s[strlen(s)-1])) )
+        return false;
+
+    cpumask_clear(&cpus);
+    while ( !(*s == ']' && *(s+1) == '\0') && *s != '\0' )
+    {
+        /*
+         * We tolerate only the allowed characters (depending on the
+         * format). Also, we don't accept empty subsets.
+         */
+        if ( *(s+strlen(s)-1) == ']' )
+        {
+            /* Format 1 */
+            if ( !(*s == '[' || *s == ']' || *s == ',' || isdigit(*s)) ||
+                 (*s == '[' && *(s+1) == ']') )
+                return false;
+        }
+        else
+        {
+            /* Format 2 */
+            if ( !(*s == ';' || *s == ',' || isdigit(*s)) ||
+                 (*s == ';' && *(s+1) == ';') )
+                return false;
+        }
+
+        /* Are we at the beginning of a subset, in format 1? */
+        if ( *s == '[' )
+        {
+            /*
+             * If we are opening a subset, we must have closed all the
+             * previously defined ones, or the string is malformed.
+             */
+            if ( in_subset )
+                return false;
+
+            s++;
+            in_subset = true;
+            continue;
+        }
+        /* Are we at the end of a subset? */
+        if ( *s == ']' || *s == ';' )
+        {
+            /*
+             * If we are closing a subset, in format 1, we must have
+             * opened it before. If not, the string is malformed.
+             */
+            if ( *s == ']' && !in_subset )
+                return false;
+
+            s++;
+            rqi++;
+            in_subset = false;
+            continue;
+        }
+
+        /*
+         * At this point, we must be dealing with either a CPU ID (i.e.,
+         * a number) or CPU separator, within a subset (i.e., ',').
+         */
+        if ( *s == ',' )
+        {
+            s++;
+            continue;
+        }
+        cpu = simple_strtoul(s, &s, 10);
+
+        /* Is the cpu ID we found valid? */
+        if ( cpu >= nr_cpu_ids )
+            return false;
+
+        /*
+         * CPU IDs are considered only the first time they're found in the
+         * string considere. Multiple subsequent occurrences are ignored.
+         */
+        if ( cpumask_test_cpu(cpu, &cpus) )
+            continue;
+
+        cpumask_set_cpu(cpu, &cpus);
+        custom_cpu_runqueue[cpu] = rqi;
+    }
+
+    /*
+     * If in_subset is true, it means we are in format 1, and we
+     * found a subset that was not closed with its ']', which
+     * means the string is malformed.
+     */
+    if ( in_subset )
+        return false;
+
+    return ret;
+}
+
 static int parse_credit2_runqueue(const char *s)
 {
     unsigned int i;
@@ -354,6 +476,13 @@ static int parse_credit2_runqueue(const char *s)
         }
     }
 
+    if ( parse_custom_runqueue(s) )
+    {
+        opt_runqueue = OPT_RUNQUEUE_CUSTOM;
+        return 0;
+    }
+
+    printk("WARNING, unrecognized value of credit2_runqueue option!\n");
     return -EINVAL;
 }
 custom_param("credit2_runqueue", parse_credit2_runqueue);
@@ -728,6 +857,12 @@ cpu_to_runqueue(struct csched2_private *prv, unsigned int cpu)
     struct csched2_runqueue_data *rqd;
     unsigned int rqi;
 
+    if ( opt_runqueue == OPT_RUNQUEUE_CUSTOM )
+    {
+        ASSERT(custom_cpu_runqueue[cpu] < nr_cpu_ids);
+        return custom_cpu_runqueue[cpu];
+    }
+
     for ( rqi = 0; rqi < nr_cpu_ids; rqi++ )
     {
         unsigned int peer_cpu;
