summaryrefslogtreecommitdiff
path: root/xen/common/sched/boot-cpupool.c
blob: 5955e6f9a98bb7a6c93c12e77d433ee50f7c0a7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * xen/common/boot_cpupools.c
 *
 * Code to create cpupools at boot time.
 *
 * Copyright (C) 2022 Arm Ltd.
 */

#include <xen/sched.h>
#include <asm/acpi.h>

/*
 * pool_cpu_map:   Index is logical cpu number, content is cpupool id, (-1) for
 *                 unassigned.
 * pool_sched_map: Index is cpupool id, content is scheduler id, (-1) for
 *                 unassigned.
 */
static int __initdata pool_cpu_map[NR_CPUS]   = { [0 ... NR_CPUS-1] = -1 };
static int __initdata pool_sched_map[NR_CPUS] = { [0 ... NR_CPUS-1] = -1 };
static unsigned int __initdata next_pool_id;

#define BTCPUPOOLS_DT_NODE_NO_REG     (-1)
#define BTCPUPOOLS_DT_NODE_NO_LOG_CPU (-2)
#define BTCPUPOOLS_DT_WRONG_NODE      (-3)
#define BTCPUPOOLS_DT_CORRUPTED_NODE  (-4)

static int __init get_logical_cpu_from_hw_id(unsigned int hwid)
{
    unsigned int i;

    for ( i = 0; i < nr_cpu_ids; i++ )
    {
        if ( cpu_physical_id(i) == hwid )
            return i;
    }

    return -1;
}

static int __init
get_logical_cpu_from_cpu_node(const struct dt_device_node *cpu_node)
{
    int cpu_num;
    const __be32 *prop;
    unsigned int cpu_reg;

    prop = dt_get_property(cpu_node, "reg", NULL);
    if ( !prop )
        return BTCPUPOOLS_DT_NODE_NO_REG;

    cpu_reg = dt_read_number(prop, dt_n_addr_cells(cpu_node));

    cpu_num = get_logical_cpu_from_hw_id(cpu_reg);
    if ( cpu_num < 0 )
        return BTCPUPOOLS_DT_NODE_NO_LOG_CPU;

    return cpu_num;
}

int __init btcpupools_get_domain_pool_id(const struct dt_device_node *node)
{
    const struct dt_device_node *phandle_node;
    int cpu_num;

    if ( !dt_device_is_compatible(node, "xen,cpupool") )
        return BTCPUPOOLS_DT_WRONG_NODE;
    /*
     * Get first cpu listed in the cpupool, from its reg it's possible to
     * retrieve the cpupool id.
     */
    phandle_node = dt_parse_phandle(node, "cpupool-cpus", 0);
    if ( !phandle_node )
        return BTCPUPOOLS_DT_CORRUPTED_NODE;

    cpu_num = get_logical_cpu_from_cpu_node(phandle_node);
    if ( cpu_num < 0 )
        return cpu_num;

    return pool_cpu_map[cpu_num];
}

static int __init check_and_get_sched_id(const char* scheduler_name)
{
    int sched_id = sched_get_id_by_name(scheduler_name);

    if ( sched_id < 0 )
        panic("Scheduler %s does not exists!\n", scheduler_name);

    return sched_id;
}

void __init btcpupools_dtb_parse(void)
{
    const struct dt_device_node *chosen, *node;

    if ( !acpi_disabled )
        return;

    chosen = dt_find_node_by_path("/chosen");
    if ( !chosen )
        panic("/chosen missing. Boot time cpupools can't be parsed from DT.\n");

    dt_for_each_child_node(chosen, node)
    {
        const struct dt_device_node *phandle_node;
        int sched_id = -1;
        const char* scheduler_name;
        unsigned int i = 0;

        if ( !dt_device_is_compatible(node, "xen,cpupool") )
            continue;

        if ( !dt_property_read_string(node, "cpupool-sched", &scheduler_name) )
            sched_id = check_and_get_sched_id(scheduler_name);

        phandle_node = dt_parse_phandle(node, "cpupool-cpus", i++);
        if ( !phandle_node )
            panic("Missing or empty cpupool-cpus property!\n");

        while ( phandle_node )
        {
            int cpu_num;

            cpu_num = get_logical_cpu_from_cpu_node(phandle_node);

            if ( cpu_num < 0 )
                panic("Error retrieving logical cpu from node %s (%d)\n",
                      dt_node_name(node), cpu_num);

            if ( pool_cpu_map[cpu_num] != -1 )
                panic("Logical cpu %d already added to a cpupool!\n", cpu_num);

            pool_cpu_map[cpu_num] = next_pool_id;

            phandle_node = dt_parse_phandle(node, "cpupool-cpus", i++);
        }

        /* Save scheduler choice for this cpupool id */
        pool_sched_map[next_pool_id] = sched_id;

        /* Let Xen generate pool ids */
        next_pool_id++;
    }
}

void __init btcpupools_allocate_pools(void)
{
    unsigned int i;
    bool add_extra_cpupool = false;
    int swap_id = -1;

    /*
     * If there are no cpupools, the value of next_pool_id is zero, so the code
     * below will assign every cpu to cpupool0 as the default behavior.
     * When there are cpupools, the code below is assigning all the not
     * assigned cpu to a new pool (next_pool_id value is the last id + 1).
     * In the same loop we check if there is any assigned cpu that is not
     * online.
     */
    for ( i = 0; i < nr_cpu_ids; i++ )
    {
        if ( cpumask_test_cpu(i, &cpu_online_map) )
        {
            /* Unassigned cpu gets next_pool_id pool id value */
            if ( pool_cpu_map[i] < 0 )
            {
                pool_cpu_map[i] = next_pool_id;
                add_extra_cpupool = true;
            }

            /*
             * Cpu0 must be in cpupool0, otherwise some operations like moving
             * cpus between cpupools, cpu hotplug, destroying cpupools, shutdown
             * of the host, might not work in a sane way.
             */
            if ( !i && (pool_cpu_map[0] != 0) )
                swap_id = pool_cpu_map[0];

            if ( swap_id != -1 )
            {
                if ( pool_cpu_map[i] == swap_id )
                    pool_cpu_map[i] = 0;
                else if ( pool_cpu_map[i] == 0 )
                    pool_cpu_map[i] = swap_id;
            }
        }
        else
        {
            if ( pool_cpu_map[i] >= 0 )
                panic("Pool-%d contains cpu%u that is not online!\n",
                      pool_cpu_map[i], i);
        }
    }

    /* A swap happened, swap schedulers between cpupool id 0 and the other */
    if ( swap_id != -1 )
    {
        int swap_sched = pool_sched_map[swap_id];

        pool_sched_map[swap_id] = pool_sched_map[0];
        pool_sched_map[0] = swap_sched;
    }

    if ( add_extra_cpupool )
        next_pool_id++;

    /* Keep track of cpupool id 0 with the global cpupool0 */
    cpupool0 = cpupool_create_pool(0, pool_sched_map[0]);

    /* Create cpupools with selected schedulers */
    for ( i = 1; i < next_pool_id; i++ )
        cpupool_create_pool(i, pool_sched_map[i]);
}

unsigned int __init btcpupools_get_cpupool_id(unsigned int cpu)
{
    ASSERT((cpu < NR_CPUS) && (pool_cpu_map[cpu] >= 0));

    printk(XENLOG_INFO "Logical CPU %u in Pool-%d (Scheduler id: %d).\n",
           cpu, pool_cpu_map[cpu], pool_sched_map[pool_cpu_map[cpu]]);

    return pool_cpu_map[cpu];
}

/*
 * Local variables:
 * mode: C
 * c-file-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */