summaryrefslogtreecommitdiff
path: root/drivers/s390/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/char')
-rw-r--r--drivers/s390/char/Makefile1
-rw-r--r--drivers/s390/char/hmcdrv_dev.c19
-rw-r--r--drivers/s390/char/sclp_cmd.c478
-rw-r--r--drivers/s390/char/sclp_mem.c399
-rw-r--r--drivers/s390/char/tape_3590.c2
5 files changed, 447 insertions, 452 deletions
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index 81d6744e1861..dcbd51152ee3 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -21,6 +21,7 @@ obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o \
sclp_early.o sclp_early_core.o sclp_sd.o
+obj-$(CONFIG_MEMORY_HOTPLUG) += sclp_mem.o
obj-$(CONFIG_TN3270) += raw3270.o con3270.o
obj-$(CONFIG_TN3270_FS) += fs3270.o
diff --git a/drivers/s390/char/hmcdrv_dev.c b/drivers/s390/char/hmcdrv_dev.c
index e069dd685899..b26fcf6849f2 100644
--- a/drivers/s390/char/hmcdrv_dev.c
+++ b/drivers/s390/char/hmcdrv_dev.c
@@ -244,24 +244,17 @@ static ssize_t hmcdrv_dev_write(struct file *fp, const char __user *ubuf,
size_t len, loff_t *pos)
{
ssize_t retlen;
+ void *pdata;
pr_debug("writing file '/dev/%pD' at pos. %lld with length %zd\n",
fp, (long long) *pos, len);
if (!fp->private_data) { /* first expect a cmd write */
- fp->private_data = kmalloc(len + 1, GFP_KERNEL);
-
- if (!fp->private_data)
- return -ENOMEM;
-
- if (!copy_from_user(fp->private_data, ubuf, len)) {
- ((char *)fp->private_data)[len] = '\0';
- return len;
- }
-
- kfree(fp->private_data);
- fp->private_data = NULL;
- return -EFAULT;
+ pdata = memdup_user_nul(ubuf, len);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ fp->private_data = pdata;
+ return len;
}
retlen = hmcdrv_dev_transfer((char *) fp->private_data,
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index 16469678548f..3480198eac02 100644
--- a/drivers/s390/char/sclp_cmd.c
+++ b/drivers/s390/char/sclp_cmd.c
@@ -8,31 +8,46 @@
#define KMSG_COMPONENT "sclp_cmd"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-#include <linux/cpufeature.h>
#include <linux/completion.h>
-#include <linux/init.h>
-#include <linux/errno.h>
#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/mmzone.h>
-#include <linux/memory.h>
-#include <linux/memory_hotplug.h>
-#include <linux/module.h>
-#include <asm/ctlreg.h>
#include <asm/chpid.h>
-#include <asm/setup.h>
-#include <asm/page.h>
+#include <asm/ctlreg.h>
#include <asm/sclp.h>
-#include <asm/numa.h>
-#include <asm/facility.h>
-#include <asm/page-states.h>
#include "sclp.h"
-#define SCLP_CMDW_ASSIGN_STORAGE 0x000d0001
-#define SCLP_CMDW_UNASSIGN_STORAGE 0x000c0001
+/* CPU configuration related functions */
+#define SCLP_CMDW_CONFIGURE_CPU 0x00110001
+#define SCLP_CMDW_DECONFIGURE_CPU 0x00100001
+/* Channel path configuration related functions */
+#define SCLP_CMDW_CONFIGURE_CHPATH 0x000f0001
+#define SCLP_CMDW_DECONFIGURE_CHPATH 0x000e0001
+#define SCLP_CMDW_READ_CHPATH_INFORMATION 0x00030001
+
+struct cpu_configure_sccb {
+ struct sccb_header header;
+} __packed __aligned(8);
+
+struct chp_cfg_sccb {
+ struct sccb_header header;
+ u8 ccm;
+ u8 reserved[6];
+ u8 cssid;
+} __packed;
+
+struct chp_info_sccb {
+ struct sccb_header header;
+ u8 recognized[SCLP_CHP_INFO_MASK_SIZE];
+ u8 standby[SCLP_CHP_INFO_MASK_SIZE];
+ u8 configured[SCLP_CHP_INFO_MASK_SIZE];
+ u8 ccm;
+ u8 reserved[6];
+ u8 cssid;
+} __packed;
static void sclp_sync_callback(struct sclp_req *req, void *data)
{
@@ -64,13 +79,11 @@ int sclp_sync_request_timeout(sclp_cmdw_t cmd, void *sccb, int timeout)
request->callback_data = &completion;
init_completion(&completion);
- /* Perform sclp request. */
rc = sclp_add_request(request);
if (rc)
goto out;
wait_for_completion(&completion);
- /* Check response. */
if (request->status != SCLP_REQ_DONE) {
pr_warn("sync request failed (cmd=0x%08x, status=0x%02x)\n",
cmd, request->status);
@@ -81,22 +94,15 @@ out:
return rc;
}
-/*
- * CPU configuration related functions.
- */
-
-#define SCLP_CMDW_CONFIGURE_CPU 0x00110001
-#define SCLP_CMDW_DECONFIGURE_CPU 0x00100001
-
int _sclp_get_core_info(struct sclp_core_info *info)
{
- int rc;
- int length = test_facility(140) ? EXT_SCCB_READ_CPU : PAGE_SIZE;
struct read_cpu_info_sccb *sccb;
+ int rc, length;
if (!SCLP_HAS_CPU_INFO)
return -EOPNOTSUPP;
+ length = test_facility(140) ? EXT_SCCB_READ_CPU : PAGE_SIZE;
sccb = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA | __GFP_ZERO, get_order(length));
if (!sccb)
return -ENOMEM;
@@ -114,14 +120,10 @@ int _sclp_get_core_info(struct sclp_core_info *info)
}
sclp_fill_core_info(info, sccb);
out:
- free_pages((unsigned long) sccb, get_order(length));
+ free_pages((unsigned long)sccb, get_order(length));
return rc;
}
-struct cpu_configure_sccb {
- struct sccb_header header;
-} __attribute__((packed, aligned(8)));
-
static int do_core_configure(sclp_cmdw_t cmd)
{
struct cpu_configure_sccb *sccb;
@@ -130,8 +132,8 @@ static int do_core_configure(sclp_cmdw_t cmd)
if (!SCLP_HAS_CPU_RECONFIG)
return -EOPNOTSUPP;
/*
- * This is not going to cross a page boundary since we force
- * kmalloc to have a minimum alignment of 8 bytes on s390.
+ * Use kmalloc to have a minimum alignment of 8 bytes and ensure sccb
+ * is not going to cross a page boundary.
*/
sccb = kzalloc(sizeof(*sccb), GFP_KERNEL | GFP_DMA);
if (!sccb)
@@ -165,394 +167,6 @@ int sclp_core_deconfigure(u8 core)
return do_core_configure(SCLP_CMDW_DECONFIGURE_CPU | core << 8);
}
-#ifdef CONFIG_MEMORY_HOTPLUG
-
-static DEFINE_MUTEX(sclp_mem_mutex);
-static LIST_HEAD(sclp_mem_list);
-static u8 sclp_max_storage_id;
-static DECLARE_BITMAP(sclp_storage_ids, 256);
-
-struct memory_increment {
- struct list_head list;
- u16 rn;
- int standby;
-};
-
-struct assign_storage_sccb {
- struct sccb_header header;
- u16 rn;
-} __packed;
-
-int arch_get_memory_phys_device(unsigned long start_pfn)
-{
- if (!sclp.rzm)
- return 0;
- return PFN_PHYS(start_pfn) >> ilog2(sclp.rzm);
-}
-
-static unsigned long long rn2addr(u16 rn)
-{
- return (unsigned long long) (rn - 1) * sclp.rzm;
-}
-
-static int do_assign_storage(sclp_cmdw_t cmd, u16 rn)
-{
- struct assign_storage_sccb *sccb;
- int rc;
-
- sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!sccb)
- return -ENOMEM;
- sccb->header.length = PAGE_SIZE;
- sccb->rn = rn;
- rc = sclp_sync_request_timeout(cmd, sccb, SCLP_QUEUE_INTERVAL);
- if (rc)
- goto out;
- switch (sccb->header.response_code) {
- case 0x0020:
- case 0x0120:
- break;
- default:
- pr_warn("assign storage failed (cmd=0x%08x, response=0x%04x, rn=0x%04x)\n",
- cmd, sccb->header.response_code, rn);
- rc = -EIO;
- break;
- }
-out:
- free_page((unsigned long) sccb);
- return rc;
-}
-
-static int sclp_assign_storage(u16 rn)
-{
- unsigned long long start;
- int rc;
-
- rc = do_assign_storage(SCLP_CMDW_ASSIGN_STORAGE, rn);
- if (rc)
- return rc;
- start = rn2addr(rn);
- storage_key_init_range(start, start + sclp.rzm);
- return 0;
-}
-
-static int sclp_unassign_storage(u16 rn)
-{
- return do_assign_storage(SCLP_CMDW_UNASSIGN_STORAGE, rn);
-}
-
-struct attach_storage_sccb {
- struct sccb_header header;
- u16 :16;
- u16 assigned;
- u32 :32;
- u32 entries[];
-} __packed;
-
-static int sclp_attach_storage(u8 id)
-{
- struct attach_storage_sccb *sccb;
- int rc;
- int i;
-
- sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!sccb)
- return -ENOMEM;
- sccb->header.length = PAGE_SIZE;
- sccb->header.function_code = 0x40;
- rc = sclp_sync_request_timeout(0x00080001 | id << 8, sccb,
- SCLP_QUEUE_INTERVAL);
- if (rc)
- goto out;
- switch (sccb->header.response_code) {
- case 0x0020:
- set_bit(id, sclp_storage_ids);
- for (i = 0; i < sccb->assigned; i++) {
- if (sccb->entries[i])
- sclp_unassign_storage(sccb->entries[i] >> 16);
- }
- break;
- default:
- rc = -EIO;
- break;
- }
-out:
- free_page((unsigned long) sccb);
- return rc;
-}
-
-static int sclp_mem_change_state(unsigned long start, unsigned long size,
- int online)
-{
- struct memory_increment *incr;
- unsigned long long istart;
- int rc = 0;
-
- list_for_each_entry(incr, &sclp_mem_list, list) {
- istart = rn2addr(incr->rn);
- if (start + size - 1 < istart)
- break;
- if (start > istart + sclp.rzm - 1)
- continue;
- if (online)
- rc |= sclp_assign_storage(incr->rn);
- else
- sclp_unassign_storage(incr->rn);
- if (rc == 0)
- incr->standby = online ? 0 : 1;
- }
- return rc ? -EIO : 0;
-}
-
-static bool contains_standby_increment(unsigned long start, unsigned long end)
-{
- struct memory_increment *incr;
- unsigned long istart;
-
- list_for_each_entry(incr, &sclp_mem_list, list) {
- istart = rn2addr(incr->rn);
- if (end - 1 < istart)
- continue;
- if (start > istart + sclp.rzm - 1)
- continue;
- if (incr->standby)
- return true;
- }
- return false;
-}
-
-static int sclp_mem_notifier(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- unsigned long start, size;
- struct memory_notify *arg;
- unsigned char id;
- int rc = 0;
-
- arg = data;
- start = arg->start_pfn << PAGE_SHIFT;
- size = arg->nr_pages << PAGE_SHIFT;
- mutex_lock(&sclp_mem_mutex);
- for_each_clear_bit(id, sclp_storage_ids, sclp_max_storage_id + 1)
- sclp_attach_storage(id);
- switch (action) {
- case MEM_GOING_OFFLINE:
- /*
- * We do not allow to set memory blocks offline that contain
- * standby memory. This is done to simplify the "memory online"
- * case.
- */
- if (contains_standby_increment(start, start + size))
- rc = -EPERM;
- break;
- case MEM_PREPARE_ONLINE:
- /*
- * Access the altmap_start_pfn and altmap_nr_pages fields
- * within the struct memory_notify specifically when dealing
- * with only MEM_PREPARE_ONLINE/MEM_FINISH_OFFLINE notifiers.
- *
- * When altmap is in use, take the specified memory range
- * online, which includes the altmap.
- */
- if (arg->altmap_nr_pages) {
- start = PFN_PHYS(arg->altmap_start_pfn);
- size += PFN_PHYS(arg->altmap_nr_pages);
- }
- rc = sclp_mem_change_state(start, size, 1);
- if (rc || !arg->altmap_nr_pages)
- break;
- /*
- * Set CMMA state to nodat here, since the struct page memory
- * at the beginning of the memory block will not go through the
- * buddy allocator later.
- */
- __arch_set_page_nodat((void *)__va(start), arg->altmap_nr_pages);
- break;
- case MEM_FINISH_OFFLINE:
- /*
- * When altmap is in use, take the specified memory range
- * offline, which includes the altmap.
- */
- if (arg->altmap_nr_pages) {
- start = PFN_PHYS(arg->altmap_start_pfn);
- size += PFN_PHYS(arg->altmap_nr_pages);
- }
- sclp_mem_change_state(start, size, 0);
- break;
- default:
- break;
- }
- mutex_unlock(&sclp_mem_mutex);
- return rc ? NOTIFY_BAD : NOTIFY_OK;
-}
-
-static struct notifier_block sclp_mem_nb = {
- .notifier_call = sclp_mem_notifier,
-};
-
-static void __init align_to_block_size(unsigned long long *start,
- unsigned long long *size,
- unsigned long long alignment)
-{
- unsigned long long start_align, size_align;
-
- start_align = roundup(*start, alignment);
- size_align = rounddown(*start + *size, alignment) - start_align;
-
- pr_info("Standby memory at 0x%llx (%lluM of %lluM usable)\n",
- *start, size_align >> 20, *size >> 20);
- *start = start_align;
- *size = size_align;
-}
-
-static void __init add_memory_merged(u16 rn)
-{
- unsigned long long start, size, addr, block_size;
- static u16 first_rn, num;
-
- if (rn && first_rn && (first_rn + num == rn)) {
- num++;
- return;
- }
- if (!first_rn)
- goto skip_add;
- start = rn2addr(first_rn);
- size = (unsigned long long) num * sclp.rzm;
- if (start >= ident_map_size)
- goto skip_add;
- if (start + size > ident_map_size)
- size = ident_map_size - start;
- block_size = memory_block_size_bytes();
- align_to_block_size(&start, &size, block_size);
- if (!size)
- goto skip_add;
- for (addr = start; addr < start + size; addr += block_size)
- add_memory(0, addr, block_size,
- cpu_has_edat1() ?
- MHP_MEMMAP_ON_MEMORY | MHP_OFFLINE_INACCESSIBLE : MHP_NONE);
-skip_add:
- first_rn = rn;
- num = 1;
-}
-
-static void __init sclp_add_standby_memory(void)
-{
- struct memory_increment *incr;
-
- list_for_each_entry(incr, &sclp_mem_list, list)
- if (incr->standby)
- add_memory_merged(incr->rn);
- add_memory_merged(0);
-}
-
-static void __init insert_increment(u16 rn, int standby, int assigned)
-{
- struct memory_increment *incr, *new_incr;
- struct list_head *prev;
- u16 last_rn;
-
- new_incr = kzalloc(sizeof(*new_incr), GFP_KERNEL);
- if (!new_incr)
- return;
- new_incr->rn = rn;
- new_incr->standby = standby;
- last_rn = 0;
- prev = &sclp_mem_list;
- list_for_each_entry(incr, &sclp_mem_list, list) {
- if (assigned && incr->rn > rn)
- break;
- if (!assigned && incr->rn - last_rn > 1)
- break;
- last_rn = incr->rn;
- prev = &incr->list;
- }
- if (!assigned)
- new_incr->rn = last_rn + 1;
- if (new_incr->rn > sclp.rnmax) {
- kfree(new_incr);
- return;
- }
- list_add(&new_incr->list, prev);
-}
-
-static int __init sclp_detect_standby_memory(void)
-{
- struct read_storage_sccb *sccb;
- int i, id, assigned, rc;
-
- if (oldmem_data.start) /* No standby memory in kdump mode */
- return 0;
- if ((sclp.facilities & 0xe00000000000ULL) != 0xe00000000000ULL)
- return 0;
- rc = -ENOMEM;
- sccb = (void *) __get_free_page(GFP_KERNEL | GFP_DMA);
- if (!sccb)
- goto out;
- assigned = 0;
- for (id = 0; id <= sclp_max_storage_id; id++) {
- memset(sccb, 0, PAGE_SIZE);
- sccb->header.length = PAGE_SIZE;
- rc = sclp_sync_request(SCLP_CMDW_READ_STORAGE_INFO | id << 8, sccb);
- if (rc)
- goto out;
- switch (sccb->header.response_code) {
- case 0x0010:
- set_bit(id, sclp_storage_ids);
- for (i = 0; i < sccb->assigned; i++) {
- if (!sccb->entries[i])
- continue;
- assigned++;
- insert_increment(sccb->entries[i] >> 16, 0, 1);
- }
- break;
- case 0x0310:
- break;
- case 0x0410:
- for (i = 0; i < sccb->assigned; i++) {
- if (!sccb->entries[i])
- continue;
- assigned++;
- insert_increment(sccb->entries[i] >> 16, 1, 1);
- }
- break;
- default:
- rc = -EIO;
- break;
- }
- if (!rc)
- sclp_max_storage_id = sccb->max_id;
- }
- if (rc || list_empty(&sclp_mem_list))
- goto out;
- for (i = 1; i <= sclp.rnmax - assigned; i++)
- insert_increment(0, 1, 0);
- rc = register_memory_notifier(&sclp_mem_nb);
- if (rc)
- goto out;
- sclp_add_standby_memory();
-out:
- free_page((unsigned long) sccb);
- return rc;
-}
-__initcall(sclp_detect_standby_memory);
-
-#endif /* CONFIG_MEMORY_HOTPLUG */
-
-/*
- * Channel path configuration related functions.
- */
-
-#define SCLP_CMDW_CONFIGURE_CHPATH 0x000f0001
-#define SCLP_CMDW_DECONFIGURE_CHPATH 0x000e0001
-#define SCLP_CMDW_READ_CHPATH_INFORMATION 0x00030001
-
-struct chp_cfg_sccb {
- struct sccb_header header;
- u8 ccm;
- u8 reserved[6];
- u8 cssid;
-} __attribute__((packed));
-
static int do_chp_configure(sclp_cmdw_t cmd)
{
struct chp_cfg_sccb *sccb;
@@ -560,8 +174,7 @@ static int do_chp_configure(sclp_cmdw_t cmd)
if (!SCLP_HAS_CHP_RECONFIG)
return -EOPNOTSUPP;
- /* Prepare sccb. */
- sccb = (struct chp_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ sccb = (struct chp_cfg_sccb *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sccb)
return -ENOMEM;
sccb->header.length = sizeof(*sccb);
@@ -581,7 +194,7 @@ static int do_chp_configure(sclp_cmdw_t cmd)
break;
}
out:
- free_page((unsigned long) sccb);
+ free_page((unsigned long)sccb);
return rc;
}
@@ -609,16 +222,6 @@ int sclp_chp_deconfigure(struct chp_id chpid)
return do_chp_configure(SCLP_CMDW_DECONFIGURE_CHPATH | chpid.id << 8);
}
-struct chp_info_sccb {
- struct sccb_header header;
- u8 recognized[SCLP_CHP_INFO_MASK_SIZE];
- u8 standby[SCLP_CHP_INFO_MASK_SIZE];
- u8 configured[SCLP_CHP_INFO_MASK_SIZE];
- u8 ccm;
- u8 reserved[6];
- u8 cssid;
-} __attribute__((packed));
-
/**
* sclp_chp_read_info - perform read channel-path information sclp command
* @info: resulting channel-path information data
@@ -634,8 +237,7 @@ int sclp_chp_read_info(struct sclp_chp_info *info)
if (!SCLP_HAS_CHP_INFO)
return -EOPNOTSUPP;
- /* Prepare sccb. */
- sccb = (struct chp_info_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ sccb = (struct chp_info_sccb *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sccb)
return -ENOMEM;
sccb->header.length = sizeof(*sccb);
@@ -652,6 +254,6 @@ int sclp_chp_read_info(struct sclp_chp_info *info)
memcpy(info->standby, sccb->standby, SCLP_CHP_INFO_MASK_SIZE);
memcpy(info->configured, sccb->configured, SCLP_CHP_INFO_MASK_SIZE);
out:
- free_page((unsigned long) sccb);
+ free_page((unsigned long)sccb);
return rc;
}
diff --git a/drivers/s390/char/sclp_mem.c b/drivers/s390/char/sclp_mem.c
new file mode 100644
index 000000000000..27f49f5fd358
--- /dev/null
+++ b/drivers/s390/char/sclp_mem.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Memory hotplug support via sclp
+ *
+ * Copyright IBM Corp. 2025
+ */
+
+#define KMSG_COMPONENT "sclp_mem"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/cpufeature.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/memory.h>
+#include <linux/memory_hotplug.h>
+#include <linux/mm.h>
+#include <linux/mmzone.h>
+#include <linux/slab.h>
+#include <asm/facility.h>
+#include <asm/page.h>
+#include <asm/page-states.h>
+#include <asm/sclp.h>
+
+#include "sclp.h"
+
+#define SCLP_CMDW_ASSIGN_STORAGE 0x000d0001
+#define SCLP_CMDW_UNASSIGN_STORAGE 0x000c0001
+
+static DEFINE_MUTEX(sclp_mem_mutex);
+static LIST_HEAD(sclp_mem_list);
+static u8 sclp_max_storage_id;
+static DECLARE_BITMAP(sclp_storage_ids, 256);
+
+struct memory_increment {
+ struct list_head list;
+ u16 rn;
+ int standby;
+};
+
+struct assign_storage_sccb {
+ struct sccb_header header;
+ u16 rn;
+} __packed;
+
+struct attach_storage_sccb {
+ struct sccb_header header;
+ u16 :16;
+ u16 assigned;
+ u32 :32;
+ u32 entries[];
+} __packed;
+
+int arch_get_memory_phys_device(unsigned long start_pfn)
+{
+ if (!sclp.rzm)
+ return 0;
+ return PFN_PHYS(start_pfn) >> ilog2(sclp.rzm);
+}
+
+static unsigned long rn2addr(u16 rn)
+{
+ return (unsigned long)(rn - 1) * sclp.rzm;
+}
+
+static int do_assign_storage(sclp_cmdw_t cmd, u16 rn)
+{
+ struct assign_storage_sccb *sccb;
+ int rc;
+
+ sccb = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb)
+ return -ENOMEM;
+ sccb->header.length = PAGE_SIZE;
+ sccb->rn = rn;
+ rc = sclp_sync_request_timeout(cmd, sccb, SCLP_QUEUE_INTERVAL);
+ if (rc)
+ goto out;
+ switch (sccb->header.response_code) {
+ case 0x0020:
+ case 0x0120:
+ break;
+ default:
+ pr_warn("assign storage failed (cmd=0x%08x, response=0x%04x, rn=0x%04x)\n",
+ cmd, sccb->header.response_code, rn);
+ rc = -EIO;
+ break;
+ }
+out:
+ free_page((unsigned long)sccb);
+ return rc;
+}
+
+static int sclp_assign_storage(u16 rn)
+{
+ unsigned long start;
+ int rc;
+
+ rc = do_assign_storage(SCLP_CMDW_ASSIGN_STORAGE, rn);
+ if (rc)
+ return rc;
+ start = rn2addr(rn);
+ storage_key_init_range(start, start + sclp.rzm);
+ return 0;
+}
+
+static int sclp_unassign_storage(u16 rn)
+{
+ return do_assign_storage(SCLP_CMDW_UNASSIGN_STORAGE, rn);
+}
+
+static int sclp_attach_storage(u8 id)
+{
+ struct attach_storage_sccb *sccb;
+ int rc, i;
+
+ sccb = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb)
+ return -ENOMEM;
+ sccb->header.length = PAGE_SIZE;
+ sccb->header.function_code = 0x40;
+ rc = sclp_sync_request_timeout(0x00080001 | id << 8, sccb,
+ SCLP_QUEUE_INTERVAL);
+ if (rc)
+ goto out;
+ switch (sccb->header.response_code) {
+ case 0x0020:
+ set_bit(id, sclp_storage_ids);
+ for (i = 0; i < sccb->assigned; i++) {
+ if (sccb->entries[i])
+ sclp_unassign_storage(sccb->entries[i] >> 16);
+ }
+ break;
+ default:
+ rc = -EIO;
+ break;
+ }
+out:
+ free_page((unsigned long)sccb);
+ return rc;
+}
+
+static int sclp_mem_change_state(unsigned long start, unsigned long size,
+ int online)
+{
+ struct memory_increment *incr;
+ unsigned long istart;
+ int rc = 0;
+
+ list_for_each_entry(incr, &sclp_mem_list, list) {
+ istart = rn2addr(incr->rn);
+ if (start + size - 1 < istart)
+ break;
+ if (start > istart + sclp.rzm - 1)
+ continue;
+ if (online)
+ rc |= sclp_assign_storage(incr->rn);
+ else
+ sclp_unassign_storage(incr->rn);
+ if (rc == 0)
+ incr->standby = online ? 0 : 1;
+ }
+ return rc ? -EIO : 0;
+}
+
+static bool contains_standby_increment(unsigned long start, unsigned long end)
+{
+ struct memory_increment *incr;
+ unsigned long istart;
+
+ list_for_each_entry(incr, &sclp_mem_list, list) {
+ istart = rn2addr(incr->rn);
+ if (end - 1 < istart)
+ continue;
+ if (start > istart + sclp.rzm - 1)
+ continue;
+ if (incr->standby)
+ return true;
+ }
+ return false;
+}
+
+static int sclp_mem_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ unsigned long start, size;
+ struct memory_notify *arg;
+ unsigned char id;
+ int rc = 0;
+
+ arg = data;
+ start = arg->start_pfn << PAGE_SHIFT;
+ size = arg->nr_pages << PAGE_SHIFT;
+ mutex_lock(&sclp_mem_mutex);
+ for_each_clear_bit(id, sclp_storage_ids, sclp_max_storage_id + 1)
+ sclp_attach_storage(id);
+ switch (action) {
+ case MEM_GOING_OFFLINE:
+ /*
+ * Do not allow to set memory blocks offline that contain
+ * standby memory. This is done to simplify the "memory online"
+ * case.
+ */
+ if (contains_standby_increment(start, start + size))
+ rc = -EPERM;
+ break;
+ case MEM_PREPARE_ONLINE:
+ /*
+ * Access the altmap_start_pfn and altmap_nr_pages fields
+ * within the struct memory_notify specifically when dealing
+ * with only MEM_PREPARE_ONLINE/MEM_FINISH_OFFLINE notifiers.
+ *
+ * When altmap is in use, take the specified memory range
+ * online, which includes the altmap.
+ */
+ if (arg->altmap_nr_pages) {
+ start = PFN_PHYS(arg->altmap_start_pfn);
+ size += PFN_PHYS(arg->altmap_nr_pages);
+ }
+ rc = sclp_mem_change_state(start, size, 1);
+ if (rc || !arg->altmap_nr_pages)
+ break;
+ /*
+ * Set CMMA state to nodat here, since the struct page memory
+ * at the beginning of the memory block will not go through the
+ * buddy allocator later.
+ */
+ __arch_set_page_nodat((void *)__va(start), arg->altmap_nr_pages);
+ break;
+ case MEM_FINISH_OFFLINE:
+ /*
+ * When altmap is in use, take the specified memory range
+ * offline, which includes the altmap.
+ */
+ if (arg->altmap_nr_pages) {
+ start = PFN_PHYS(arg->altmap_start_pfn);
+ size += PFN_PHYS(arg->altmap_nr_pages);
+ }
+ sclp_mem_change_state(start, size, 0);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&sclp_mem_mutex);
+ return rc ? NOTIFY_BAD : NOTIFY_OK;
+}
+
+static struct notifier_block sclp_mem_nb = {
+ .notifier_call = sclp_mem_notifier,
+};
+
+static void __init align_to_block_size(unsigned long *start,
+ unsigned long *size,
+ unsigned long alignment)
+{
+ unsigned long start_align, size_align;
+
+ start_align = roundup(*start, alignment);
+ size_align = rounddown(*start + *size, alignment) - start_align;
+
+ pr_info("Standby memory at 0x%lx (%luM of %luM usable)\n",
+ *start, size_align >> 20, *size >> 20);
+ *start = start_align;
+ *size = size_align;
+}
+
+static void __init add_memory_merged(u16 rn)
+{
+ unsigned long start, size, addr, block_size;
+ static u16 first_rn, num;
+
+ if (rn && first_rn && (first_rn + num == rn)) {
+ num++;
+ return;
+ }
+ if (!first_rn)
+ goto skip_add;
+ start = rn2addr(first_rn);
+ size = (unsigned long)num * sclp.rzm;
+ if (start >= ident_map_size)
+ goto skip_add;
+ if (start + size > ident_map_size)
+ size = ident_map_size - start;
+ block_size = memory_block_size_bytes();
+ align_to_block_size(&start, &size, block_size);
+ if (!size)
+ goto skip_add;
+ for (addr = start; addr < start + size; addr += block_size) {
+ add_memory(0, addr, block_size,
+ cpu_has_edat1() ?
+ MHP_MEMMAP_ON_MEMORY | MHP_OFFLINE_INACCESSIBLE : MHP_NONE);
+ }
+skip_add:
+ first_rn = rn;
+ num = 1;
+}
+
+static void __init sclp_add_standby_memory(void)
+{
+ struct memory_increment *incr;
+
+ list_for_each_entry(incr, &sclp_mem_list, list) {
+ if (incr->standby)
+ add_memory_merged(incr->rn);
+ }
+ add_memory_merged(0);
+}
+
+static void __init insert_increment(u16 rn, int standby, int assigned)
+{
+ struct memory_increment *incr, *new_incr;
+ struct list_head *prev;
+ u16 last_rn;
+
+ new_incr = kzalloc(sizeof(*new_incr), GFP_KERNEL);
+ if (!new_incr)
+ return;
+ new_incr->rn = rn;
+ new_incr->standby = standby;
+ last_rn = 0;
+ prev = &sclp_mem_list;
+ list_for_each_entry(incr, &sclp_mem_list, list) {
+ if (assigned && incr->rn > rn)
+ break;
+ if (!assigned && incr->rn - last_rn > 1)
+ break;
+ last_rn = incr->rn;
+ prev = &incr->list;
+ }
+ if (!assigned)
+ new_incr->rn = last_rn + 1;
+ if (new_incr->rn > sclp.rnmax) {
+ kfree(new_incr);
+ return;
+ }
+ list_add(&new_incr->list, prev);
+}
+
+static int __init sclp_detect_standby_memory(void)
+{
+ struct read_storage_sccb *sccb;
+ int i, id, assigned, rc;
+
+ /* No standby memory in kdump mode */
+ if (oldmem_data.start)
+ return 0;
+ if ((sclp.facilities & 0xe00000000000UL) != 0xe00000000000UL)
+ return 0;
+ rc = -ENOMEM;
+ sccb = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb)
+ goto out;
+ assigned = 0;
+ for (id = 0; id <= sclp_max_storage_id; id++) {
+ memset(sccb, 0, PAGE_SIZE);
+ sccb->header.length = PAGE_SIZE;
+ rc = sclp_sync_request(SCLP_CMDW_READ_STORAGE_INFO | id << 8, sccb);
+ if (rc)
+ goto out;
+ switch (sccb->header.response_code) {
+ case 0x0010:
+ set_bit(id, sclp_storage_ids);
+ for (i = 0; i < sccb->assigned; i++) {
+ if (!sccb->entries[i])
+ continue;
+ assigned++;
+ insert_increment(sccb->entries[i] >> 16, 0, 1);
+ }
+ break;
+ case 0x0310:
+ break;
+ case 0x0410:
+ for (i = 0; i < sccb->assigned; i++) {
+ if (!sccb->entries[i])
+ continue;
+ assigned++;
+ insert_increment(sccb->entries[i] >> 16, 1, 1);
+ }
+ break;
+ default:
+ rc = -EIO;
+ break;
+ }
+ if (!rc)
+ sclp_max_storage_id = sccb->max_id;
+ }
+ if (rc || list_empty(&sclp_mem_list))
+ goto out;
+ for (i = 1; i <= sclp.rnmax - assigned; i++)
+ insert_increment(0, 1, 0);
+ rc = register_memory_notifier(&sclp_mem_nb);
+ if (rc)
+ goto out;
+ sclp_add_standby_memory();
+out:
+ free_page((unsigned long)sccb);
+ return rc;
+}
+__initcall(sclp_detect_standby_memory);
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index a1bafaf73f87..2a2931d303cb 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -1671,7 +1671,7 @@ tape_3590_init(void)
DBF_EVENT(3, "3590 init\n");
- tape_3590_wq = alloc_workqueue("tape_3590", 0, 0);
+ tape_3590_wq = alloc_workqueue("tape_3590", WQ_PERCPU, 0);
if (!tape_3590_wq)
return -ENOMEM;