diff options
author | Heiko Carstens <heiko.carstens@de.ibm.com> | 2017-08-07 09:16:15 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2017-08-09 09:09:35 -0400 |
commit | 3f4298427ad521fdc74fb991b17d84959513218a (patch) | |
tree | c4a9cff2e5ac1261c745b74dc3a70a644fd448bd | |
parent | cd4386a931b6310b05559d2e28efda04d30ab593 (diff) |
s390/vmcp: make use of contiguous memory allocator
If memory is fragmented it is unlikely that large order memory
allocations succeed. This has been an issue with the vmcp device
driver since a long time, since it requires large physical contiguous
memory ares for large responses.
To hopefully resolve this issue make use of the contiguous memory
allocator (cma). This patch adds a vmcp specific vmcp cma area with a
default size of 4MB. The size can be changed either via the
VMCP_CMA_SIZE config option at compile time or with the "vmcp_cma"
kernel parameter (e.g. "vmcp_cma=16m").
For any vmcp response buffers larger than 16k memory from the cma area
will be allocated. If such an allocation fails, there is a fallback to
the buddy allocator.
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | Documentation/admin-guide/kernel-parameters.txt | 4 | ||||
-rw-r--r-- | arch/s390/include/asm/setup.h | 6 | ||||
-rw-r--r-- | arch/s390/kernel/setup.c | 1 | ||||
-rw-r--r-- | drivers/s390/char/Kconfig | 11 | ||||
-rw-r--r-- | drivers/s390/char/vmcp.c | 74 | ||||
-rw-r--r-- | drivers/s390/char/vmcp.h | 3 |
6 files changed, 90 insertions, 9 deletions
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index d9c171ce4190..5a2d5079139b 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt | |||
@@ -4375,6 +4375,10 @@ | |||
4375 | decrease the size and leave more room for directly | 4375 | decrease the size and leave more room for directly |
4376 | mapped kernel RAM. | 4376 | mapped kernel RAM. |
4377 | 4377 | ||
4378 | vmcp_cma=nn[MG] [KNL,S390] | ||
4379 | Sets the memory size reserved for contiguous memory | ||
4380 | allocations for the vmcp device driver. | ||
4381 | |||
4378 | vmhalt= [KNL,S390] Perform z/VM CP command after system halt. | 4382 | vmhalt= [KNL,S390] Perform z/VM CP command after system halt. |
4379 | Format: <command> | 4383 | Format: <command> |
4380 | 4384 | ||
diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index 61da4bd6edad..490e035b3716 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h | |||
@@ -108,6 +108,12 @@ extern void pfault_fini(void); | |||
108 | #define pfault_fini() do { } while (0) | 108 | #define pfault_fini() do { } while (0) |
109 | #endif /* CONFIG_PFAULT */ | 109 | #endif /* CONFIG_PFAULT */ |
110 | 110 | ||
111 | #ifdef CONFIG_VMCP | ||
112 | void vmcp_cma_reserve(void); | ||
113 | #else | ||
114 | static inline void vmcp_cma_reserve(void) { } | ||
115 | #endif | ||
116 | |||
111 | void report_user_fault(struct pt_regs *regs, long signr, int is_mm_fault); | 117 | void report_user_fault(struct pt_regs *regs, long signr, int is_mm_fault); |
112 | 118 | ||
113 | void cmma_init(void); | 119 | void cmma_init(void); |
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index a50238e17867..164a1e16b53e 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c | |||
@@ -925,6 +925,7 @@ void __init setup_arch(char **cmdline_p) | |||
925 | setup_memory_end(); | 925 | setup_memory_end(); |
926 | setup_memory(); | 926 | setup_memory(); |
927 | dma_contiguous_reserve(memory_end); | 927 | dma_contiguous_reserve(memory_end); |
928 | vmcp_cma_reserve(); | ||
928 | 929 | ||
929 | check_initrd(); | 930 | check_initrd(); |
930 | reserve_crashkernel(); | 931 | reserve_crashkernel(); |
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index b3f1c458905f..97c4c9fdd53d 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig | |||
@@ -169,10 +169,21 @@ config VMCP | |||
169 | def_bool y | 169 | def_bool y |
170 | prompt "Support for the z/VM CP interface" | 170 | prompt "Support for the z/VM CP interface" |
171 | depends on S390 | 171 | depends on S390 |
172 | select CMA | ||
172 | help | 173 | help |
173 | Select this option if you want to be able to interact with the control | 174 | Select this option if you want to be able to interact with the control |
174 | program on z/VM | 175 | program on z/VM |
175 | 176 | ||
177 | config VMCP_CMA_SIZE | ||
178 | int "Memory in MiB reserved for z/VM CP interface" | ||
179 | default "4" | ||
180 | depends on VMCP | ||
181 | help | ||
182 | Specify the default amount of memory in MiB reserved for the z/VM CP | ||
183 | interface. If needed this memory is used for large contiguous memory | ||
184 | allocations. The default can be changed with the kernel command line | ||
185 | parameter "vmcp_cma". | ||
186 | |||
176 | config MONREADER | 187 | config MONREADER |
177 | def_tristate m | 188 | def_tristate m |
178 | prompt "API for reading z/VM monitor service records" | 189 | prompt "API for reading z/VM monitor service records" |
diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c index b5e3a49745f9..c202b407698f 100644 --- a/drivers/s390/char/vmcp.c +++ b/drivers/s390/char/vmcp.c | |||
@@ -17,15 +17,77 @@ | |||
17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
18 | #include <linux/miscdevice.h> | 18 | #include <linux/miscdevice.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/uaccess.h> | ||
20 | #include <linux/export.h> | 21 | #include <linux/export.h> |
22 | #include <linux/mutex.h> | ||
23 | #include <linux/cma.h> | ||
24 | #include <linux/mm.h> | ||
21 | #include <asm/compat.h> | 25 | #include <asm/compat.h> |
22 | #include <asm/cpcmd.h> | 26 | #include <asm/cpcmd.h> |
23 | #include <asm/debug.h> | 27 | #include <asm/debug.h> |
24 | #include <linux/uaccess.h> | ||
25 | #include "vmcp.h" | 28 | #include "vmcp.h" |
26 | 29 | ||
27 | static debug_info_t *vmcp_debug; | 30 | static debug_info_t *vmcp_debug; |
28 | 31 | ||
32 | static unsigned long vmcp_cma_size __initdata = CONFIG_VMCP_CMA_SIZE * 1024 * 1024; | ||
33 | static struct cma *vmcp_cma; | ||
34 | |||
35 | static int __init early_parse_vmcp_cma(char *p) | ||
36 | { | ||
37 | vmcp_cma_size = ALIGN(memparse(p, NULL), PAGE_SIZE); | ||
38 | return 0; | ||
39 | } | ||
40 | early_param("vmcp_cma", early_parse_vmcp_cma); | ||
41 | |||
42 | void __init vmcp_cma_reserve(void) | ||
43 | { | ||
44 | if (!MACHINE_IS_VM) | ||
45 | return; | ||
46 | cma_declare_contiguous(0, vmcp_cma_size, 0, 0, 0, false, "vmcp", &vmcp_cma); | ||
47 | } | ||
48 | |||
49 | static void vmcp_response_alloc(struct vmcp_session *session) | ||
50 | { | ||
51 | struct page *page = NULL; | ||
52 | int nr_pages, order; | ||
53 | |||
54 | order = get_order(session->bufsize); | ||
55 | nr_pages = ALIGN(session->bufsize, PAGE_SIZE) >> PAGE_SHIFT; | ||
56 | /* | ||
57 | * For anything below order 3 allocations rely on the buddy | ||
58 | * allocator. If such low-order allocations can't be handled | ||
59 | * anymore the system won't work anyway. | ||
60 | */ | ||
61 | if (order > 2) | ||
62 | page = cma_alloc(vmcp_cma, nr_pages, 0, GFP_KERNEL); | ||
63 | if (page) { | ||
64 | session->response = (char *)page_to_phys(page); | ||
65 | session->cma_alloc = 1; | ||
66 | return; | ||
67 | } | ||
68 | session->response = (char *)__get_free_pages(GFP_KERNEL | __GFP_RETRY_MAYFAIL, order); | ||
69 | } | ||
70 | |||
71 | static void vmcp_response_free(struct vmcp_session *session) | ||
72 | { | ||
73 | int nr_pages, order; | ||
74 | struct page *page; | ||
75 | |||
76 | if (!session->response) | ||
77 | return; | ||
78 | order = get_order(session->bufsize); | ||
79 | nr_pages = ALIGN(session->bufsize, PAGE_SIZE) >> PAGE_SHIFT; | ||
80 | if (session->cma_alloc) { | ||
81 | page = phys_to_page((unsigned long)session->response); | ||
82 | cma_release(vmcp_cma, page, nr_pages); | ||
83 | session->cma_alloc = 0; | ||
84 | goto out; | ||
85 | } | ||
86 | free_pages((unsigned long)session->response, order); | ||
87 | out: | ||
88 | session->response = NULL; | ||
89 | } | ||
90 | |||
29 | static int vmcp_open(struct inode *inode, struct file *file) | 91 | static int vmcp_open(struct inode *inode, struct file *file) |
30 | { | 92 | { |
31 | struct vmcp_session *session; | 93 | struct vmcp_session *session; |
@@ -51,7 +113,7 @@ static int vmcp_release(struct inode *inode, struct file *file) | |||
51 | 113 | ||
52 | session = file->private_data; | 114 | session = file->private_data; |
53 | file->private_data = NULL; | 115 | file->private_data = NULL; |
54 | free_pages((unsigned long)session->response, get_order(session->bufsize)); | 116 | vmcp_response_free(session); |
55 | kfree(session); | 117 | kfree(session); |
56 | return 0; | 118 | return 0; |
57 | } | 119 | } |
@@ -97,9 +159,7 @@ vmcp_write(struct file *file, const char __user *buff, size_t count, | |||
97 | return -ERESTARTSYS; | 159 | return -ERESTARTSYS; |
98 | } | 160 | } |
99 | if (!session->response) | 161 | if (!session->response) |
100 | session->response = (char *)__get_free_pages(GFP_KERNEL | 162 | vmcp_response_alloc(session); |
101 | | __GFP_RETRY_MAYFAIL, | ||
102 | get_order(session->bufsize)); | ||
103 | if (!session->response) { | 163 | if (!session->response) { |
104 | mutex_unlock(&session->mutex); | 164 | mutex_unlock(&session->mutex); |
105 | kfree(cmd); | 165 | kfree(cmd); |
@@ -146,9 +206,7 @@ static long vmcp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
146 | mutex_unlock(&session->mutex); | 206 | mutex_unlock(&session->mutex); |
147 | return put_user(temp, argp); | 207 | return put_user(temp, argp); |
148 | case VMCP_SETBUF: | 208 | case VMCP_SETBUF: |
149 | free_pages((unsigned long)session->response, | 209 | vmcp_response_free(session); |
150 | get_order(session->bufsize)); | ||
151 | session->response=NULL; | ||
152 | temp = get_user(session->bufsize, argp); | 210 | temp = get_user(session->bufsize, argp); |
153 | if (temp) | 211 | if (temp) |
154 | session->bufsize = PAGE_SIZE; | 212 | session->bufsize = PAGE_SIZE; |
diff --git a/drivers/s390/char/vmcp.h b/drivers/s390/char/vmcp.h index 1e29b0418382..4e725edf449f 100644 --- a/drivers/s390/char/vmcp.h +++ b/drivers/s390/char/vmcp.h | |||
@@ -20,8 +20,9 @@ | |||
20 | #define VMCP_GETSIZE _IOR(0x10, 3, int) | 20 | #define VMCP_GETSIZE _IOR(0x10, 3, int) |
21 | 21 | ||
22 | struct vmcp_session { | 22 | struct vmcp_session { |
23 | unsigned int bufsize; | ||
24 | char *response; | 23 | char *response; |
24 | unsigned int bufsize; | ||
25 | unsigned int cma_alloc : 1; | ||
25 | int resp_size; | 26 | int resp_size; |
26 | int resp_code; | 27 | int resp_code; |
27 | /* As we use copy_from/to_user, which might * | 28 | /* As we use copy_from/to_user, which might * |