diff options
Diffstat (limited to 'arch/s390')
-rw-r--r-- | arch/s390/Kconfig | 1 | ||||
-rw-r--r-- | arch/s390/hypfs/hypfs_diag.c | 3 | ||||
-rw-r--r-- | arch/s390/include/asm/qdio.h | 78 | ||||
-rw-r--r-- | arch/s390/kernel/suspend.c | 118 | ||||
-rw-r--r-- | arch/s390/kernel/swsusp_asm64.S | 3 | ||||
-rw-r--r-- | arch/s390/kernel/time.c | 13 |
6 files changed, 207 insertions, 9 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index ed5cb5af5281..6b99fc3f9b63 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig | |||
@@ -91,6 +91,7 @@ config S390 | |||
91 | select HAVE_ARCH_MUTEX_CPU_RELAX | 91 | select HAVE_ARCH_MUTEX_CPU_RELAX |
92 | select HAVE_ARCH_JUMP_LABEL if !MARCH_G5 | 92 | select HAVE_ARCH_JUMP_LABEL if !MARCH_G5 |
93 | select HAVE_RCU_TABLE_FREE if SMP | 93 | select HAVE_RCU_TABLE_FREE if SMP |
94 | select ARCH_SAVE_PAGE_KEYS if HIBERNATION | ||
94 | select ARCH_INLINE_SPIN_TRYLOCK | 95 | select ARCH_INLINE_SPIN_TRYLOCK |
95 | select ARCH_INLINE_SPIN_TRYLOCK_BH | 96 | select ARCH_INLINE_SPIN_TRYLOCK_BH |
96 | select ARCH_INLINE_SPIN_LOCK | 97 | select ARCH_INLINE_SPIN_LOCK |
diff --git a/arch/s390/hypfs/hypfs_diag.c b/arch/s390/hypfs/hypfs_diag.c index 6023c6dc1fb7..74c8f5e76ce4 100644 --- a/arch/s390/hypfs/hypfs_diag.c +++ b/arch/s390/hypfs/hypfs_diag.c | |||
@@ -562,10 +562,9 @@ static int dbfs_d204_create(void **data, void **data_free_ptr, size_t *size) | |||
562 | void *base; | 562 | void *base; |
563 | 563 | ||
564 | buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr); | 564 | buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr); |
565 | base = vmalloc(buf_size); | 565 | base = vzalloc(buf_size); |
566 | if (!base) | 566 | if (!base) |
567 | return -ENOMEM; | 567 | return -ENOMEM; |
568 | memset(base, 0, buf_size); | ||
569 | d204 = page_align_ptr(base + sizeof(d204->hdr)) - sizeof(d204->hdr); | 568 | d204 = page_align_ptr(base + sizeof(d204->hdr)) - sizeof(d204->hdr); |
570 | rc = diag204_do_store(d204->buf, diag204_buf_pages); | 569 | rc = diag204_do_store(d204->buf, diag204_buf_pages); |
571 | if (rc) { | 570 | if (rc) { |
diff --git a/arch/s390/include/asm/qdio.h b/arch/s390/include/asm/qdio.h index 15c97625df8d..21993623da9a 100644 --- a/arch/s390/include/asm/qdio.h +++ b/arch/s390/include/asm/qdio.h | |||
@@ -123,6 +123,40 @@ struct slibe { | |||
123 | }; | 123 | }; |
124 | 124 | ||
125 | /** | 125 | /** |
126 | * struct qaob - queue asynchronous operation block | ||
127 | * @res0: reserved parameters | ||
128 | * @res1: reserved parameter | ||
129 | * @res2: reserved parameter | ||
130 | * @res3: reserved parameter | ||
131 | * @aorc: asynchronous operation return code | ||
132 | * @flags: internal flags | ||
133 | * @cbtbs: control block type | ||
134 | * @sb_count: number of storage blocks | ||
135 | * @sba: storage block element addresses | ||
136 | * @dcount: size of storage block elements | ||
137 | * @user0: user defineable value | ||
138 | * @res4: reserved paramater | ||
139 | * @user1: user defineable value | ||
140 | * @user2: user defineable value | ||
141 | */ | ||
142 | struct qaob { | ||
143 | u64 res0[6]; | ||
144 | u8 res1; | ||
145 | u8 res2; | ||
146 | u8 res3; | ||
147 | u8 aorc; | ||
148 | u8 flags; | ||
149 | u16 cbtbs; | ||
150 | u8 sb_count; | ||
151 | u64 sba[QDIO_MAX_ELEMENTS_PER_BUFFER]; | ||
152 | u16 dcount[QDIO_MAX_ELEMENTS_PER_BUFFER]; | ||
153 | u64 user0; | ||
154 | u64 res4[2]; | ||
155 | u64 user1; | ||
156 | u64 user2; | ||
157 | } __attribute__ ((packed, aligned(256))); | ||
158 | |||
159 | /** | ||
126 | * struct slib - storage list information block (SLIB) | 160 | * struct slib - storage list information block (SLIB) |
127 | * @nsliba: next SLIB address (if any) | 161 | * @nsliba: next SLIB address (if any) |
128 | * @sla: SL address | 162 | * @sla: SL address |
@@ -225,6 +259,41 @@ struct slsb { | |||
225 | #define CHSC_AC2_DATA_DIV_AVAILABLE 0x0010 | 259 | #define CHSC_AC2_DATA_DIV_AVAILABLE 0x0010 |
226 | #define CHSC_AC2_DATA_DIV_ENABLED 0x0002 | 260 | #define CHSC_AC2_DATA_DIV_ENABLED 0x0002 |
227 | 261 | ||
262 | /** | ||
263 | * struct qdio_outbuf_state - SBAL related asynchronous operation information | ||
264 | * (for communication with upper layer programs) | ||
265 | * (only required for use with completion queues) | ||
266 | * @flags: flags indicating state of buffer | ||
267 | * @aob: pointer to QAOB used for the particular SBAL | ||
268 | * @user: pointer to upper layer program's state information related to SBAL | ||
269 | * (stored in user1 data of QAOB) | ||
270 | */ | ||
271 | struct qdio_outbuf_state { | ||
272 | u8 flags; | ||
273 | struct qaob *aob; | ||
274 | void *user; | ||
275 | }; | ||
276 | |||
277 | #define QDIO_OUTBUF_STATE_FLAG_NONE 0x00 | ||
278 | #define QDIO_OUTBUF_STATE_FLAG_PENDING 0x01 | ||
279 | |||
280 | #define CHSC_AC1_INITIATE_INPUTQ 0x80 | ||
281 | |||
282 | |||
283 | /* qdio adapter-characteristics-1 flag */ | ||
284 | #define AC1_SIGA_INPUT_NEEDED 0x40 /* process input queues */ | ||
285 | #define AC1_SIGA_OUTPUT_NEEDED 0x20 /* process output queues */ | ||
286 | #define AC1_SIGA_SYNC_NEEDED 0x10 /* ask hypervisor to sync */ | ||
287 | #define AC1_AUTOMATIC_SYNC_ON_THININT 0x08 /* set by hypervisor */ | ||
288 | #define AC1_AUTOMATIC_SYNC_ON_OUT_PCI 0x04 /* set by hypervisor */ | ||
289 | #define AC1_SC_QEBSM_AVAILABLE 0x02 /* available for subchannel */ | ||
290 | #define AC1_SC_QEBSM_ENABLED 0x01 /* enabled for subchannel */ | ||
291 | |||
292 | #define CHSC_AC2_DATA_DIV_AVAILABLE 0x0010 | ||
293 | #define CHSC_AC2_DATA_DIV_ENABLED 0x0002 | ||
294 | |||
295 | #define CHSC_AC3_FORMAT2_CQ_AVAILABLE 0x8000 | ||
296 | |||
228 | struct qdio_ssqd_desc { | 297 | struct qdio_ssqd_desc { |
229 | u8 flags; | 298 | u8 flags; |
230 | u8:8; | 299 | u8:8; |
@@ -243,8 +312,7 @@ struct qdio_ssqd_desc { | |||
243 | u64 sch_token; | 312 | u64 sch_token; |
244 | u8 mro; | 313 | u8 mro; |
245 | u8 mri; | 314 | u8 mri; |
246 | u8:8; | 315 | u16 qdioac3; |
247 | u8 sbalic; | ||
248 | u16:16; | 316 | u16:16; |
249 | u8:8; | 317 | u8:8; |
250 | u8 mmwc; | 318 | u8 mmwc; |
@@ -280,9 +348,11 @@ typedef void qdio_handler_t(struct ccw_device *, unsigned int, int, | |||
280 | * @no_output_qs: number of output queues | 348 | * @no_output_qs: number of output queues |
281 | * @input_handler: handler to be called for input queues | 349 | * @input_handler: handler to be called for input queues |
282 | * @output_handler: handler to be called for output queues | 350 | * @output_handler: handler to be called for output queues |
351 | * @queue_start_poll: polling handlers (one per input queue or NULL) | ||
283 | * @int_parm: interruption parameter | 352 | * @int_parm: interruption parameter |
284 | * @input_sbal_addr_array: address of no_input_qs * 128 pointers | 353 | * @input_sbal_addr_array: address of no_input_qs * 128 pointers |
285 | * @output_sbal_addr_array: address of no_output_qs * 128 pointers | 354 | * @output_sbal_addr_array: address of no_output_qs * 128 pointers |
355 | * @output_sbal_state_array: no_output_qs * 128 state info (for CQ or NULL) | ||
286 | */ | 356 | */ |
287 | struct qdio_initialize { | 357 | struct qdio_initialize { |
288 | struct ccw_device *cdev; | 358 | struct ccw_device *cdev; |
@@ -297,11 +367,12 @@ struct qdio_initialize { | |||
297 | unsigned int no_output_qs; | 367 | unsigned int no_output_qs; |
298 | qdio_handler_t *input_handler; | 368 | qdio_handler_t *input_handler; |
299 | qdio_handler_t *output_handler; | 369 | qdio_handler_t *output_handler; |
300 | void (*queue_start_poll) (struct ccw_device *, int, unsigned long); | 370 | void (**queue_start_poll) (struct ccw_device *, int, unsigned long); |
301 | int scan_threshold; | 371 | int scan_threshold; |
302 | unsigned long int_parm; | 372 | unsigned long int_parm; |
303 | void **input_sbal_addr_array; | 373 | void **input_sbal_addr_array; |
304 | void **output_sbal_addr_array; | 374 | void **output_sbal_addr_array; |
375 | struct qdio_outbuf_state *output_sbal_state_array; | ||
305 | }; | 376 | }; |
306 | 377 | ||
307 | #define QDIO_STATE_INACTIVE 0x00000002 /* after qdio_cleanup */ | 378 | #define QDIO_STATE_INACTIVE 0x00000002 /* after qdio_cleanup */ |
@@ -316,6 +387,7 @@ struct qdio_initialize { | |||
316 | extern int qdio_allocate(struct qdio_initialize *); | 387 | extern int qdio_allocate(struct qdio_initialize *); |
317 | extern int qdio_establish(struct qdio_initialize *); | 388 | extern int qdio_establish(struct qdio_initialize *); |
318 | extern int qdio_activate(struct ccw_device *); | 389 | extern int qdio_activate(struct ccw_device *); |
390 | extern void qdio_release_aob(struct qaob *); | ||
319 | extern int do_QDIO(struct ccw_device *, unsigned int, int, unsigned int, | 391 | extern int do_QDIO(struct ccw_device *, unsigned int, int, unsigned int, |
320 | unsigned int); | 392 | unsigned int); |
321 | extern int qdio_start_irq(struct ccw_device *, int); | 393 | extern int qdio_start_irq(struct ccw_device *, int); |
diff --git a/arch/s390/kernel/suspend.c b/arch/s390/kernel/suspend.c index cf9e5c6d5527..b6f9afed74ec 100644 --- a/arch/s390/kernel/suspend.c +++ b/arch/s390/kernel/suspend.c | |||
@@ -7,6 +7,7 @@ | |||
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/pfn.h> | 9 | #include <linux/pfn.h> |
10 | #include <linux/mm.h> | ||
10 | #include <asm/system.h> | 11 | #include <asm/system.h> |
11 | 12 | ||
12 | /* | 13 | /* |
@@ -14,6 +15,123 @@ | |||
14 | */ | 15 | */ |
15 | extern const void __nosave_begin, __nosave_end; | 16 | extern const void __nosave_begin, __nosave_end; |
16 | 17 | ||
18 | /* | ||
19 | * The restore of the saved pages in an hibernation image will set | ||
20 | * the change and referenced bits in the storage key for each page. | ||
21 | * Overindication of the referenced bits after an hibernation cycle | ||
22 | * does not cause any harm but the overindication of the change bits | ||
23 | * would cause trouble. | ||
24 | * Use the ARCH_SAVE_PAGE_KEYS hooks to save the storage key of each | ||
25 | * page to the most significant byte of the associated page frame | ||
26 | * number in the hibernation image. | ||
27 | */ | ||
28 | |||
29 | /* | ||
30 | * Key storage is allocated as a linked list of pages. | ||
31 | * The size of the keys array is (PAGE_SIZE - sizeof(long)) | ||
32 | */ | ||
33 | struct page_key_data { | ||
34 | struct page_key_data *next; | ||
35 | unsigned char data[]; | ||
36 | }; | ||
37 | |||
38 | #define PAGE_KEY_DATA_SIZE (PAGE_SIZE - sizeof(struct page_key_data *)) | ||
39 | |||
40 | static struct page_key_data *page_key_data; | ||
41 | static struct page_key_data *page_key_rp, *page_key_wp; | ||
42 | static unsigned long page_key_rx, page_key_wx; | ||
43 | |||
44 | /* | ||
45 | * For each page in the hibernation image one additional byte is | ||
46 | * stored in the most significant byte of the page frame number. | ||
47 | * On suspend no additional memory is required but on resume the | ||
48 | * keys need to be memorized until the page data has been restored. | ||
49 | * Only then can the storage keys be set to their old state. | ||
50 | */ | ||
51 | unsigned long page_key_additional_pages(unsigned long pages) | ||
52 | { | ||
53 | return DIV_ROUND_UP(pages, PAGE_KEY_DATA_SIZE); | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Free page_key_data list of arrays. | ||
58 | */ | ||
59 | void page_key_free(void) | ||
60 | { | ||
61 | struct page_key_data *pkd; | ||
62 | |||
63 | while (page_key_data) { | ||
64 | pkd = page_key_data; | ||
65 | page_key_data = pkd->next; | ||
66 | free_page((unsigned long) pkd); | ||
67 | } | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * Allocate page_key_data list of arrays with enough room to store | ||
72 | * one byte for each page in the hibernation image. | ||
73 | */ | ||
74 | int page_key_alloc(unsigned long pages) | ||
75 | { | ||
76 | struct page_key_data *pk; | ||
77 | unsigned long size; | ||
78 | |||
79 | size = DIV_ROUND_UP(pages, PAGE_KEY_DATA_SIZE); | ||
80 | while (size--) { | ||
81 | pk = (struct page_key_data *) get_zeroed_page(GFP_KERNEL); | ||
82 | if (!pk) { | ||
83 | page_key_free(); | ||
84 | return -ENOMEM; | ||
85 | } | ||
86 | pk->next = page_key_data; | ||
87 | page_key_data = pk; | ||
88 | } | ||
89 | page_key_rp = page_key_wp = page_key_data; | ||
90 | page_key_rx = page_key_wx = 0; | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * Save the storage key into the upper 8 bits of the page frame number. | ||
96 | */ | ||
97 | void page_key_read(unsigned long *pfn) | ||
98 | { | ||
99 | unsigned long addr; | ||
100 | |||
101 | addr = (unsigned long) page_address(pfn_to_page(*pfn)); | ||
102 | *(unsigned char *) pfn = (unsigned char) page_get_storage_key(addr); | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * Extract the storage key from the upper 8 bits of the page frame number | ||
107 | * and store it in the page_key_data list of arrays. | ||
108 | */ | ||
109 | void page_key_memorize(unsigned long *pfn) | ||
110 | { | ||
111 | page_key_wp->data[page_key_wx] = *(unsigned char *) pfn; | ||
112 | *(unsigned char *) pfn = 0; | ||
113 | if (++page_key_wx < PAGE_KEY_DATA_SIZE) | ||
114 | return; | ||
115 | page_key_wp = page_key_wp->next; | ||
116 | page_key_wx = 0; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * Get the next key from the page_key_data list of arrays and set the | ||
121 | * storage key of the page referred by @address. If @address refers to | ||
122 | * a "safe" page the swsusp_arch_resume code will transfer the storage | ||
123 | * key from the buffer page to the original page. | ||
124 | */ | ||
125 | void page_key_write(void *address) | ||
126 | { | ||
127 | page_set_storage_key((unsigned long) address, | ||
128 | page_key_rp->data[page_key_rx], 0); | ||
129 | if (++page_key_rx >= PAGE_KEY_DATA_SIZE) | ||
130 | return; | ||
131 | page_key_rp = page_key_rp->next; | ||
132 | page_key_rx = 0; | ||
133 | } | ||
134 | |||
17 | int pfn_is_nosave(unsigned long pfn) | 135 | int pfn_is_nosave(unsigned long pfn) |
18 | { | 136 | { |
19 | unsigned long nosave_begin_pfn = PFN_DOWN(__pa(&__nosave_begin)); | 137 | unsigned long nosave_begin_pfn = PFN_DOWN(__pa(&__nosave_begin)); |
diff --git a/arch/s390/kernel/swsusp_asm64.S b/arch/s390/kernel/swsusp_asm64.S index 51bcdb50a230..acb78cdee896 100644 --- a/arch/s390/kernel/swsusp_asm64.S +++ b/arch/s390/kernel/swsusp_asm64.S | |||
@@ -136,11 +136,14 @@ ENTRY(swsusp_arch_resume) | |||
136 | 0: | 136 | 0: |
137 | lg %r2,8(%r1) | 137 | lg %r2,8(%r1) |
138 | lg %r4,0(%r1) | 138 | lg %r4,0(%r1) |
139 | iske %r0,%r4 | ||
139 | lghi %r3,PAGE_SIZE | 140 | lghi %r3,PAGE_SIZE |
140 | lghi %r5,PAGE_SIZE | 141 | lghi %r5,PAGE_SIZE |
141 | 1: | 142 | 1: |
142 | mvcle %r2,%r4,0 | 143 | mvcle %r2,%r4,0 |
143 | jo 1b | 144 | jo 1b |
145 | lg %r2,8(%r1) | ||
146 | sske %r0,%r2 | ||
144 | lg %r1,16(%r1) | 147 | lg %r1,16(%r1) |
145 | ltgr %r1,%r1 | 148 | ltgr %r1,%r1 |
146 | jnz 0b | 149 | jnz 0b |
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index dff933065ab6..8d65bd0383fc 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c | |||
@@ -109,10 +109,14 @@ static void fixup_clock_comparator(unsigned long long delta) | |||
109 | set_clock_comparator(S390_lowcore.clock_comparator); | 109 | set_clock_comparator(S390_lowcore.clock_comparator); |
110 | } | 110 | } |
111 | 111 | ||
112 | static int s390_next_event(unsigned long delta, | 112 | static int s390_next_ktime(ktime_t expires, |
113 | struct clock_event_device *evt) | 113 | struct clock_event_device *evt) |
114 | { | 114 | { |
115 | S390_lowcore.clock_comparator = get_clock() + delta; | 115 | u64 nsecs; |
116 | |||
117 | nsecs = ktime_to_ns(ktime_sub(expires, ktime_get_monotonic_offset())); | ||
118 | do_div(nsecs, 125); | ||
119 | S390_lowcore.clock_comparator = TOD_UNIX_EPOCH + (nsecs << 9); | ||
116 | set_clock_comparator(S390_lowcore.clock_comparator); | 120 | set_clock_comparator(S390_lowcore.clock_comparator); |
117 | return 0; | 121 | return 0; |
118 | } | 122 | } |
@@ -137,14 +141,15 @@ void init_cpu_timer(void) | |||
137 | cpu = smp_processor_id(); | 141 | cpu = smp_processor_id(); |
138 | cd = &per_cpu(comparators, cpu); | 142 | cd = &per_cpu(comparators, cpu); |
139 | cd->name = "comparator"; | 143 | cd->name = "comparator"; |
140 | cd->features = CLOCK_EVT_FEAT_ONESHOT; | 144 | cd->features = CLOCK_EVT_FEAT_ONESHOT | |
145 | CLOCK_EVT_FEAT_KTIME; | ||
141 | cd->mult = 16777; | 146 | cd->mult = 16777; |
142 | cd->shift = 12; | 147 | cd->shift = 12; |
143 | cd->min_delta_ns = 1; | 148 | cd->min_delta_ns = 1; |
144 | cd->max_delta_ns = LONG_MAX; | 149 | cd->max_delta_ns = LONG_MAX; |
145 | cd->rating = 400; | 150 | cd->rating = 400; |
146 | cd->cpumask = cpumask_of(cpu); | 151 | cd->cpumask = cpumask_of(cpu); |
147 | cd->set_next_event = s390_next_event; | 152 | cd->set_next_ktime = s390_next_ktime; |
148 | cd->set_mode = s390_set_mode; | 153 | cd->set_mode = s390_set_mode; |
149 | 154 | ||
150 | clockevents_register_device(cd); | 155 | clockevents_register_device(cd); |