1*f9de6cdfSSumanth Korikkar // SPDX-License-Identifier: GPL-2.0
2*f9de6cdfSSumanth Korikkar /*
3*f9de6cdfSSumanth Korikkar * Memory hotplug support via sclp
4*f9de6cdfSSumanth Korikkar *
5*f9de6cdfSSumanth Korikkar * Copyright IBM Corp. 2025
6*f9de6cdfSSumanth Korikkar */
7*f9de6cdfSSumanth Korikkar
8*f9de6cdfSSumanth Korikkar #define KMSG_COMPONENT "sclp_mem"
9*f9de6cdfSSumanth Korikkar #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
10*f9de6cdfSSumanth Korikkar
11*f9de6cdfSSumanth Korikkar #include <linux/cpufeature.h>
12*f9de6cdfSSumanth Korikkar #include <linux/err.h>
13*f9de6cdfSSumanth Korikkar #include <linux/errno.h>
14*f9de6cdfSSumanth Korikkar #include <linux/init.h>
15*f9de6cdfSSumanth Korikkar #include <linux/memory.h>
16*f9de6cdfSSumanth Korikkar #include <linux/memory_hotplug.h>
17*f9de6cdfSSumanth Korikkar #include <linux/mm.h>
18*f9de6cdfSSumanth Korikkar #include <linux/mmzone.h>
19*f9de6cdfSSumanth Korikkar #include <linux/slab.h>
20*f9de6cdfSSumanth Korikkar #include <asm/facility.h>
21*f9de6cdfSSumanth Korikkar #include <asm/page.h>
22*f9de6cdfSSumanth Korikkar #include <asm/page-states.h>
23*f9de6cdfSSumanth Korikkar #include <asm/sclp.h>
24*f9de6cdfSSumanth Korikkar
25*f9de6cdfSSumanth Korikkar #include "sclp.h"
26*f9de6cdfSSumanth Korikkar
27*f9de6cdfSSumanth Korikkar #define SCLP_CMDW_ASSIGN_STORAGE 0x000d0001
28*f9de6cdfSSumanth Korikkar #define SCLP_CMDW_UNASSIGN_STORAGE 0x000c0001
29*f9de6cdfSSumanth Korikkar
30*f9de6cdfSSumanth Korikkar static DEFINE_MUTEX(sclp_mem_mutex);
31*f9de6cdfSSumanth Korikkar static LIST_HEAD(sclp_mem_list);
32*f9de6cdfSSumanth Korikkar static u8 sclp_max_storage_id;
33*f9de6cdfSSumanth Korikkar static DECLARE_BITMAP(sclp_storage_ids, 256);
34*f9de6cdfSSumanth Korikkar
35*f9de6cdfSSumanth Korikkar struct memory_increment {
36*f9de6cdfSSumanth Korikkar struct list_head list;
37*f9de6cdfSSumanth Korikkar u16 rn;
38*f9de6cdfSSumanth Korikkar int standby;
39*f9de6cdfSSumanth Korikkar };
40*f9de6cdfSSumanth Korikkar
41*f9de6cdfSSumanth Korikkar struct assign_storage_sccb {
42*f9de6cdfSSumanth Korikkar struct sccb_header header;
43*f9de6cdfSSumanth Korikkar u16 rn;
44*f9de6cdfSSumanth Korikkar } __packed;
45*f9de6cdfSSumanth Korikkar
46*f9de6cdfSSumanth Korikkar struct attach_storage_sccb {
47*f9de6cdfSSumanth Korikkar struct sccb_header header;
48*f9de6cdfSSumanth Korikkar u16 :16;
49*f9de6cdfSSumanth Korikkar u16 assigned;
50*f9de6cdfSSumanth Korikkar u32 :32;
51*f9de6cdfSSumanth Korikkar u32 entries[];
52*f9de6cdfSSumanth Korikkar } __packed;
53*f9de6cdfSSumanth Korikkar
arch_get_memory_phys_device(unsigned long start_pfn)54*f9de6cdfSSumanth Korikkar int arch_get_memory_phys_device(unsigned long start_pfn)
55*f9de6cdfSSumanth Korikkar {
56*f9de6cdfSSumanth Korikkar if (!sclp.rzm)
57*f9de6cdfSSumanth Korikkar return 0;
58*f9de6cdfSSumanth Korikkar return PFN_PHYS(start_pfn) >> ilog2(sclp.rzm);
59*f9de6cdfSSumanth Korikkar }
60*f9de6cdfSSumanth Korikkar
rn2addr(u16 rn)61*f9de6cdfSSumanth Korikkar static unsigned long rn2addr(u16 rn)
62*f9de6cdfSSumanth Korikkar {
63*f9de6cdfSSumanth Korikkar return (unsigned long)(rn - 1) * sclp.rzm;
64*f9de6cdfSSumanth Korikkar }
65*f9de6cdfSSumanth Korikkar
do_assign_storage(sclp_cmdw_t cmd,u16 rn)66*f9de6cdfSSumanth Korikkar static int do_assign_storage(sclp_cmdw_t cmd, u16 rn)
67*f9de6cdfSSumanth Korikkar {
68*f9de6cdfSSumanth Korikkar struct assign_storage_sccb *sccb;
69*f9de6cdfSSumanth Korikkar int rc;
70*f9de6cdfSSumanth Korikkar
71*f9de6cdfSSumanth Korikkar sccb = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
72*f9de6cdfSSumanth Korikkar if (!sccb)
73*f9de6cdfSSumanth Korikkar return -ENOMEM;
74*f9de6cdfSSumanth Korikkar sccb->header.length = PAGE_SIZE;
75*f9de6cdfSSumanth Korikkar sccb->rn = rn;
76*f9de6cdfSSumanth Korikkar rc = sclp_sync_request_timeout(cmd, sccb, SCLP_QUEUE_INTERVAL);
77*f9de6cdfSSumanth Korikkar if (rc)
78*f9de6cdfSSumanth Korikkar goto out;
79*f9de6cdfSSumanth Korikkar switch (sccb->header.response_code) {
80*f9de6cdfSSumanth Korikkar case 0x0020:
81*f9de6cdfSSumanth Korikkar case 0x0120:
82*f9de6cdfSSumanth Korikkar break;
83*f9de6cdfSSumanth Korikkar default:
84*f9de6cdfSSumanth Korikkar pr_warn("assign storage failed (cmd=0x%08x, response=0x%04x, rn=0x%04x)\n",
85*f9de6cdfSSumanth Korikkar cmd, sccb->header.response_code, rn);
86*f9de6cdfSSumanth Korikkar rc = -EIO;
87*f9de6cdfSSumanth Korikkar break;
88*f9de6cdfSSumanth Korikkar }
89*f9de6cdfSSumanth Korikkar out:
90*f9de6cdfSSumanth Korikkar free_page((unsigned long)sccb);
91*f9de6cdfSSumanth Korikkar return rc;
92*f9de6cdfSSumanth Korikkar }
93*f9de6cdfSSumanth Korikkar
sclp_assign_storage(u16 rn)94*f9de6cdfSSumanth Korikkar static int sclp_assign_storage(u16 rn)
95*f9de6cdfSSumanth Korikkar {
96*f9de6cdfSSumanth Korikkar unsigned long start;
97*f9de6cdfSSumanth Korikkar int rc;
98*f9de6cdfSSumanth Korikkar
99*f9de6cdfSSumanth Korikkar rc = do_assign_storage(SCLP_CMDW_ASSIGN_STORAGE, rn);
100*f9de6cdfSSumanth Korikkar if (rc)
101*f9de6cdfSSumanth Korikkar return rc;
102*f9de6cdfSSumanth Korikkar start = rn2addr(rn);
103*f9de6cdfSSumanth Korikkar storage_key_init_range(start, start + sclp.rzm);
104*f9de6cdfSSumanth Korikkar return 0;
105*f9de6cdfSSumanth Korikkar }
106*f9de6cdfSSumanth Korikkar
sclp_unassign_storage(u16 rn)107*f9de6cdfSSumanth Korikkar static int sclp_unassign_storage(u16 rn)
108*f9de6cdfSSumanth Korikkar {
109*f9de6cdfSSumanth Korikkar return do_assign_storage(SCLP_CMDW_UNASSIGN_STORAGE, rn);
110*f9de6cdfSSumanth Korikkar }
111*f9de6cdfSSumanth Korikkar
sclp_attach_storage(u8 id)112*f9de6cdfSSumanth Korikkar static int sclp_attach_storage(u8 id)
113*f9de6cdfSSumanth Korikkar {
114*f9de6cdfSSumanth Korikkar struct attach_storage_sccb *sccb;
115*f9de6cdfSSumanth Korikkar int rc, i;
116*f9de6cdfSSumanth Korikkar
117*f9de6cdfSSumanth Korikkar sccb = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
118*f9de6cdfSSumanth Korikkar if (!sccb)
119*f9de6cdfSSumanth Korikkar return -ENOMEM;
120*f9de6cdfSSumanth Korikkar sccb->header.length = PAGE_SIZE;
121*f9de6cdfSSumanth Korikkar sccb->header.function_code = 0x40;
122*f9de6cdfSSumanth Korikkar rc = sclp_sync_request_timeout(0x00080001 | id << 8, sccb,
123*f9de6cdfSSumanth Korikkar SCLP_QUEUE_INTERVAL);
124*f9de6cdfSSumanth Korikkar if (rc)
125*f9de6cdfSSumanth Korikkar goto out;
126*f9de6cdfSSumanth Korikkar switch (sccb->header.response_code) {
127*f9de6cdfSSumanth Korikkar case 0x0020:
128*f9de6cdfSSumanth Korikkar set_bit(id, sclp_storage_ids);
129*f9de6cdfSSumanth Korikkar for (i = 0; i < sccb->assigned; i++) {
130*f9de6cdfSSumanth Korikkar if (sccb->entries[i])
131*f9de6cdfSSumanth Korikkar sclp_unassign_storage(sccb->entries[i] >> 16);
132*f9de6cdfSSumanth Korikkar }
133*f9de6cdfSSumanth Korikkar break;
134*f9de6cdfSSumanth Korikkar default:
135*f9de6cdfSSumanth Korikkar rc = -EIO;
136*f9de6cdfSSumanth Korikkar break;
137*f9de6cdfSSumanth Korikkar }
138*f9de6cdfSSumanth Korikkar out:
139*f9de6cdfSSumanth Korikkar free_page((unsigned long)sccb);
140*f9de6cdfSSumanth Korikkar return rc;
141*f9de6cdfSSumanth Korikkar }
142*f9de6cdfSSumanth Korikkar
sclp_mem_change_state(unsigned long start,unsigned long size,int online)143*f9de6cdfSSumanth Korikkar static int sclp_mem_change_state(unsigned long start, unsigned long size,
144*f9de6cdfSSumanth Korikkar int online)
145*f9de6cdfSSumanth Korikkar {
146*f9de6cdfSSumanth Korikkar struct memory_increment *incr;
147*f9de6cdfSSumanth Korikkar unsigned long istart;
148*f9de6cdfSSumanth Korikkar int rc = 0;
149*f9de6cdfSSumanth Korikkar
150*f9de6cdfSSumanth Korikkar list_for_each_entry(incr, &sclp_mem_list, list) {
151*f9de6cdfSSumanth Korikkar istart = rn2addr(incr->rn);
152*f9de6cdfSSumanth Korikkar if (start + size - 1 < istart)
153*f9de6cdfSSumanth Korikkar break;
154*f9de6cdfSSumanth Korikkar if (start > istart + sclp.rzm - 1)
155*f9de6cdfSSumanth Korikkar continue;
156*f9de6cdfSSumanth Korikkar if (online)
157*f9de6cdfSSumanth Korikkar rc |= sclp_assign_storage(incr->rn);
158*f9de6cdfSSumanth Korikkar else
159*f9de6cdfSSumanth Korikkar sclp_unassign_storage(incr->rn);
160*f9de6cdfSSumanth Korikkar if (rc == 0)
161*f9de6cdfSSumanth Korikkar incr->standby = online ? 0 : 1;
162*f9de6cdfSSumanth Korikkar }
163*f9de6cdfSSumanth Korikkar return rc ? -EIO : 0;
164*f9de6cdfSSumanth Korikkar }
165*f9de6cdfSSumanth Korikkar
contains_standby_increment(unsigned long start,unsigned long end)166*f9de6cdfSSumanth Korikkar static bool contains_standby_increment(unsigned long start, unsigned long end)
167*f9de6cdfSSumanth Korikkar {
168*f9de6cdfSSumanth Korikkar struct memory_increment *incr;
169*f9de6cdfSSumanth Korikkar unsigned long istart;
170*f9de6cdfSSumanth Korikkar
171*f9de6cdfSSumanth Korikkar list_for_each_entry(incr, &sclp_mem_list, list) {
172*f9de6cdfSSumanth Korikkar istart = rn2addr(incr->rn);
173*f9de6cdfSSumanth Korikkar if (end - 1 < istart)
174*f9de6cdfSSumanth Korikkar continue;
175*f9de6cdfSSumanth Korikkar if (start > istart + sclp.rzm - 1)
176*f9de6cdfSSumanth Korikkar continue;
177*f9de6cdfSSumanth Korikkar if (incr->standby)
178*f9de6cdfSSumanth Korikkar return true;
179*f9de6cdfSSumanth Korikkar }
180*f9de6cdfSSumanth Korikkar return false;
181*f9de6cdfSSumanth Korikkar }
182*f9de6cdfSSumanth Korikkar
sclp_mem_notifier(struct notifier_block * nb,unsigned long action,void * data)183*f9de6cdfSSumanth Korikkar static int sclp_mem_notifier(struct notifier_block *nb,
184*f9de6cdfSSumanth Korikkar unsigned long action, void *data)
185*f9de6cdfSSumanth Korikkar {
186*f9de6cdfSSumanth Korikkar unsigned long start, size;
187*f9de6cdfSSumanth Korikkar struct memory_notify *arg;
188*f9de6cdfSSumanth Korikkar unsigned char id;
189*f9de6cdfSSumanth Korikkar int rc = 0;
190*f9de6cdfSSumanth Korikkar
191*f9de6cdfSSumanth Korikkar arg = data;
192*f9de6cdfSSumanth Korikkar start = arg->start_pfn << PAGE_SHIFT;
193*f9de6cdfSSumanth Korikkar size = arg->nr_pages << PAGE_SHIFT;
194*f9de6cdfSSumanth Korikkar mutex_lock(&sclp_mem_mutex);
195*f9de6cdfSSumanth Korikkar for_each_clear_bit(id, sclp_storage_ids, sclp_max_storage_id + 1)
196*f9de6cdfSSumanth Korikkar sclp_attach_storage(id);
197*f9de6cdfSSumanth Korikkar switch (action) {
198*f9de6cdfSSumanth Korikkar case MEM_GOING_OFFLINE:
199*f9de6cdfSSumanth Korikkar /*
200*f9de6cdfSSumanth Korikkar * Do not allow to set memory blocks offline that contain
201*f9de6cdfSSumanth Korikkar * standby memory. This is done to simplify the "memory online"
202*f9de6cdfSSumanth Korikkar * case.
203*f9de6cdfSSumanth Korikkar */
204*f9de6cdfSSumanth Korikkar if (contains_standby_increment(start, start + size))
205*f9de6cdfSSumanth Korikkar rc = -EPERM;
206*f9de6cdfSSumanth Korikkar break;
207*f9de6cdfSSumanth Korikkar case MEM_PREPARE_ONLINE:
208*f9de6cdfSSumanth Korikkar /*
209*f9de6cdfSSumanth Korikkar * Access the altmap_start_pfn and altmap_nr_pages fields
210*f9de6cdfSSumanth Korikkar * within the struct memory_notify specifically when dealing
211*f9de6cdfSSumanth Korikkar * with only MEM_PREPARE_ONLINE/MEM_FINISH_OFFLINE notifiers.
212*f9de6cdfSSumanth Korikkar *
213*f9de6cdfSSumanth Korikkar * When altmap is in use, take the specified memory range
214*f9de6cdfSSumanth Korikkar * online, which includes the altmap.
215*f9de6cdfSSumanth Korikkar */
216*f9de6cdfSSumanth Korikkar if (arg->altmap_nr_pages) {
217*f9de6cdfSSumanth Korikkar start = PFN_PHYS(arg->altmap_start_pfn);
218*f9de6cdfSSumanth Korikkar size += PFN_PHYS(arg->altmap_nr_pages);
219*f9de6cdfSSumanth Korikkar }
220*f9de6cdfSSumanth Korikkar rc = sclp_mem_change_state(start, size, 1);
221*f9de6cdfSSumanth Korikkar if (rc || !arg->altmap_nr_pages)
222*f9de6cdfSSumanth Korikkar break;
223*f9de6cdfSSumanth Korikkar /*
224*f9de6cdfSSumanth Korikkar * Set CMMA state to nodat here, since the struct page memory
225*f9de6cdfSSumanth Korikkar * at the beginning of the memory block will not go through the
226*f9de6cdfSSumanth Korikkar * buddy allocator later.
227*f9de6cdfSSumanth Korikkar */
228*f9de6cdfSSumanth Korikkar __arch_set_page_nodat((void *)__va(start), arg->altmap_nr_pages);
229*f9de6cdfSSumanth Korikkar break;
230*f9de6cdfSSumanth Korikkar case MEM_FINISH_OFFLINE:
231*f9de6cdfSSumanth Korikkar /*
232*f9de6cdfSSumanth Korikkar * When altmap is in use, take the specified memory range
233*f9de6cdfSSumanth Korikkar * offline, which includes the altmap.
234*f9de6cdfSSumanth Korikkar */
235*f9de6cdfSSumanth Korikkar if (arg->altmap_nr_pages) {
236*f9de6cdfSSumanth Korikkar start = PFN_PHYS(arg->altmap_start_pfn);
237*f9de6cdfSSumanth Korikkar size += PFN_PHYS(arg->altmap_nr_pages);
238*f9de6cdfSSumanth Korikkar }
239*f9de6cdfSSumanth Korikkar sclp_mem_change_state(start, size, 0);
240*f9de6cdfSSumanth Korikkar break;
241*f9de6cdfSSumanth Korikkar default:
242*f9de6cdfSSumanth Korikkar break;
243*f9de6cdfSSumanth Korikkar }
244*f9de6cdfSSumanth Korikkar mutex_unlock(&sclp_mem_mutex);
245*f9de6cdfSSumanth Korikkar return rc ? NOTIFY_BAD : NOTIFY_OK;
246*f9de6cdfSSumanth Korikkar }
247*f9de6cdfSSumanth Korikkar
248*f9de6cdfSSumanth Korikkar static struct notifier_block sclp_mem_nb = {
249*f9de6cdfSSumanth Korikkar .notifier_call = sclp_mem_notifier,
250*f9de6cdfSSumanth Korikkar };
251*f9de6cdfSSumanth Korikkar
align_to_block_size(unsigned long * start,unsigned long * size,unsigned long alignment)252*f9de6cdfSSumanth Korikkar static void __init align_to_block_size(unsigned long *start,
253*f9de6cdfSSumanth Korikkar unsigned long *size,
254*f9de6cdfSSumanth Korikkar unsigned long alignment)
255*f9de6cdfSSumanth Korikkar {
256*f9de6cdfSSumanth Korikkar unsigned long start_align, size_align;
257*f9de6cdfSSumanth Korikkar
258*f9de6cdfSSumanth Korikkar start_align = roundup(*start, alignment);
259*f9de6cdfSSumanth Korikkar size_align = rounddown(*start + *size, alignment) - start_align;
260*f9de6cdfSSumanth Korikkar
261*f9de6cdfSSumanth Korikkar pr_info("Standby memory at 0x%lx (%luM of %luM usable)\n",
262*f9de6cdfSSumanth Korikkar *start, size_align >> 20, *size >> 20);
263*f9de6cdfSSumanth Korikkar *start = start_align;
264*f9de6cdfSSumanth Korikkar *size = size_align;
265*f9de6cdfSSumanth Korikkar }
266*f9de6cdfSSumanth Korikkar
add_memory_merged(u16 rn)267*f9de6cdfSSumanth Korikkar static void __init add_memory_merged(u16 rn)
268*f9de6cdfSSumanth Korikkar {
269*f9de6cdfSSumanth Korikkar unsigned long start, size, addr, block_size;
270*f9de6cdfSSumanth Korikkar static u16 first_rn, num;
271*f9de6cdfSSumanth Korikkar
272*f9de6cdfSSumanth Korikkar if (rn && first_rn && (first_rn + num == rn)) {
273*f9de6cdfSSumanth Korikkar num++;
274*f9de6cdfSSumanth Korikkar return;
275*f9de6cdfSSumanth Korikkar }
276*f9de6cdfSSumanth Korikkar if (!first_rn)
277*f9de6cdfSSumanth Korikkar goto skip_add;
278*f9de6cdfSSumanth Korikkar start = rn2addr(first_rn);
279*f9de6cdfSSumanth Korikkar size = (unsigned long)num * sclp.rzm;
280*f9de6cdfSSumanth Korikkar if (start >= ident_map_size)
281*f9de6cdfSSumanth Korikkar goto skip_add;
282*f9de6cdfSSumanth Korikkar if (start + size > ident_map_size)
283*f9de6cdfSSumanth Korikkar size = ident_map_size - start;
284*f9de6cdfSSumanth Korikkar block_size = memory_block_size_bytes();
285*f9de6cdfSSumanth Korikkar align_to_block_size(&start, &size, block_size);
286*f9de6cdfSSumanth Korikkar if (!size)
287*f9de6cdfSSumanth Korikkar goto skip_add;
288*f9de6cdfSSumanth Korikkar for (addr = start; addr < start + size; addr += block_size) {
289*f9de6cdfSSumanth Korikkar add_memory(0, addr, block_size,
290*f9de6cdfSSumanth Korikkar cpu_has_edat1() ?
291*f9de6cdfSSumanth Korikkar MHP_MEMMAP_ON_MEMORY | MHP_OFFLINE_INACCESSIBLE : MHP_NONE);
292*f9de6cdfSSumanth Korikkar }
293*f9de6cdfSSumanth Korikkar skip_add:
294*f9de6cdfSSumanth Korikkar first_rn = rn;
295*f9de6cdfSSumanth Korikkar num = 1;
296*f9de6cdfSSumanth Korikkar }
297*f9de6cdfSSumanth Korikkar
sclp_add_standby_memory(void)298*f9de6cdfSSumanth Korikkar static void __init sclp_add_standby_memory(void)
299*f9de6cdfSSumanth Korikkar {
300*f9de6cdfSSumanth Korikkar struct memory_increment *incr;
301*f9de6cdfSSumanth Korikkar
302*f9de6cdfSSumanth Korikkar list_for_each_entry(incr, &sclp_mem_list, list) {
303*f9de6cdfSSumanth Korikkar if (incr->standby)
304*f9de6cdfSSumanth Korikkar add_memory_merged(incr->rn);
305*f9de6cdfSSumanth Korikkar }
306*f9de6cdfSSumanth Korikkar add_memory_merged(0);
307*f9de6cdfSSumanth Korikkar }
308*f9de6cdfSSumanth Korikkar
insert_increment(u16 rn,int standby,int assigned)309*f9de6cdfSSumanth Korikkar static void __init insert_increment(u16 rn, int standby, int assigned)
310*f9de6cdfSSumanth Korikkar {
311*f9de6cdfSSumanth Korikkar struct memory_increment *incr, *new_incr;
312*f9de6cdfSSumanth Korikkar struct list_head *prev;
313*f9de6cdfSSumanth Korikkar u16 last_rn;
314*f9de6cdfSSumanth Korikkar
315*f9de6cdfSSumanth Korikkar new_incr = kzalloc(sizeof(*new_incr), GFP_KERNEL);
316*f9de6cdfSSumanth Korikkar if (!new_incr)
317*f9de6cdfSSumanth Korikkar return;
318*f9de6cdfSSumanth Korikkar new_incr->rn = rn;
319*f9de6cdfSSumanth Korikkar new_incr->standby = standby;
320*f9de6cdfSSumanth Korikkar last_rn = 0;
321*f9de6cdfSSumanth Korikkar prev = &sclp_mem_list;
322*f9de6cdfSSumanth Korikkar list_for_each_entry(incr, &sclp_mem_list, list) {
323*f9de6cdfSSumanth Korikkar if (assigned && incr->rn > rn)
324*f9de6cdfSSumanth Korikkar break;
325*f9de6cdfSSumanth Korikkar if (!assigned && incr->rn - last_rn > 1)
326*f9de6cdfSSumanth Korikkar break;
327*f9de6cdfSSumanth Korikkar last_rn = incr->rn;
328*f9de6cdfSSumanth Korikkar prev = &incr->list;
329*f9de6cdfSSumanth Korikkar }
330*f9de6cdfSSumanth Korikkar if (!assigned)
331*f9de6cdfSSumanth Korikkar new_incr->rn = last_rn + 1;
332*f9de6cdfSSumanth Korikkar if (new_incr->rn > sclp.rnmax) {
333*f9de6cdfSSumanth Korikkar kfree(new_incr);
334*f9de6cdfSSumanth Korikkar return;
335*f9de6cdfSSumanth Korikkar }
336*f9de6cdfSSumanth Korikkar list_add(&new_incr->list, prev);
337*f9de6cdfSSumanth Korikkar }
338*f9de6cdfSSumanth Korikkar
sclp_detect_standby_memory(void)339*f9de6cdfSSumanth Korikkar static int __init sclp_detect_standby_memory(void)
340*f9de6cdfSSumanth Korikkar {
341*f9de6cdfSSumanth Korikkar struct read_storage_sccb *sccb;
342*f9de6cdfSSumanth Korikkar int i, id, assigned, rc;
343*f9de6cdfSSumanth Korikkar
344*f9de6cdfSSumanth Korikkar /* No standby memory in kdump mode */
345*f9de6cdfSSumanth Korikkar if (oldmem_data.start)
346*f9de6cdfSSumanth Korikkar return 0;
347*f9de6cdfSSumanth Korikkar if ((sclp.facilities & 0xe00000000000UL) != 0xe00000000000UL)
348*f9de6cdfSSumanth Korikkar return 0;
349*f9de6cdfSSumanth Korikkar rc = -ENOMEM;
350*f9de6cdfSSumanth Korikkar sccb = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
351*f9de6cdfSSumanth Korikkar if (!sccb)
352*f9de6cdfSSumanth Korikkar goto out;
353*f9de6cdfSSumanth Korikkar assigned = 0;
354*f9de6cdfSSumanth Korikkar for (id = 0; id <= sclp_max_storage_id; id++) {
355*f9de6cdfSSumanth Korikkar memset(sccb, 0, PAGE_SIZE);
356*f9de6cdfSSumanth Korikkar sccb->header.length = PAGE_SIZE;
357*f9de6cdfSSumanth Korikkar rc = sclp_sync_request(SCLP_CMDW_READ_STORAGE_INFO | id << 8, sccb);
358*f9de6cdfSSumanth Korikkar if (rc)
359*f9de6cdfSSumanth Korikkar goto out;
360*f9de6cdfSSumanth Korikkar switch (sccb->header.response_code) {
361*f9de6cdfSSumanth Korikkar case 0x0010:
362*f9de6cdfSSumanth Korikkar set_bit(id, sclp_storage_ids);
363*f9de6cdfSSumanth Korikkar for (i = 0; i < sccb->assigned; i++) {
364*f9de6cdfSSumanth Korikkar if (!sccb->entries[i])
365*f9de6cdfSSumanth Korikkar continue;
366*f9de6cdfSSumanth Korikkar assigned++;
367*f9de6cdfSSumanth Korikkar insert_increment(sccb->entries[i] >> 16, 0, 1);
368*f9de6cdfSSumanth Korikkar }
369*f9de6cdfSSumanth Korikkar break;
370*f9de6cdfSSumanth Korikkar case 0x0310:
371*f9de6cdfSSumanth Korikkar break;
372*f9de6cdfSSumanth Korikkar case 0x0410:
373*f9de6cdfSSumanth Korikkar for (i = 0; i < sccb->assigned; i++) {
374*f9de6cdfSSumanth Korikkar if (!sccb->entries[i])
375*f9de6cdfSSumanth Korikkar continue;
376*f9de6cdfSSumanth Korikkar assigned++;
377*f9de6cdfSSumanth Korikkar insert_increment(sccb->entries[i] >> 16, 1, 1);
378*f9de6cdfSSumanth Korikkar }
379*f9de6cdfSSumanth Korikkar break;
380*f9de6cdfSSumanth Korikkar default:
381*f9de6cdfSSumanth Korikkar rc = -EIO;
382*f9de6cdfSSumanth Korikkar break;
383*f9de6cdfSSumanth Korikkar }
384*f9de6cdfSSumanth Korikkar if (!rc)
385*f9de6cdfSSumanth Korikkar sclp_max_storage_id = sccb->max_id;
386*f9de6cdfSSumanth Korikkar }
387*f9de6cdfSSumanth Korikkar if (rc || list_empty(&sclp_mem_list))
388*f9de6cdfSSumanth Korikkar goto out;
389*f9de6cdfSSumanth Korikkar for (i = 1; i <= sclp.rnmax - assigned; i++)
390*f9de6cdfSSumanth Korikkar insert_increment(0, 1, 0);
391*f9de6cdfSSumanth Korikkar rc = register_memory_notifier(&sclp_mem_nb);
392*f9de6cdfSSumanth Korikkar if (rc)
393*f9de6cdfSSumanth Korikkar goto out;
394*f9de6cdfSSumanth Korikkar sclp_add_standby_memory();
395*f9de6cdfSSumanth Korikkar out:
396*f9de6cdfSSumanth Korikkar free_page((unsigned long)sccb);
397*f9de6cdfSSumanth Korikkar return rc;
398*f9de6cdfSSumanth Korikkar }
399*f9de6cdfSSumanth Korikkar __initcall(sclp_detect_standby_memory);
400