diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-02-19 11:33:12 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-02-19 11:33:12 -0500 |
commit | ff5f16820f632079d5095884adc5d71ee3249fa4 (patch) | |
tree | 5f474546110a3c7c3194264978304d268a716840 | |
parent | 409ee136f28f71cbec518cfc85a379a508ccbaa0 (diff) | |
parent | 12d319b920fa673a4d5e7c1785c5dc82dcd15257 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 fixes from Martin Schwidefsky:
"Several bug fixes:
- There are four different stack tracers, and three of them have
bugs. For 4.5 the bugs are fixed and we prepare a cleanup patch
for the next merge window.
- Three bug fixes for the dasd driver in regard to parallel access
volumes and the new max_dev_sectors block device queue limit
- The irq restore optimization needs a fixup for memcpy_real
- The diagnose trace code has a conflict with lockdep"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
s390/dasd: fix performance drop
s390/maccess: reduce stnsm instructions
s390/diag: avoid lockdep recursion
s390/dasd: fix refcount for PAV reassignment
s390/dasd: prevent incorrect length error under z/VM after PAV changes
s390: fix DAT off memory access, e.g. on kdump
s390/oprofile: fix address range for asynchronous stack
s390/perf_event: fix address range for asynchronous stack
s390/stacktrace: add save_stack_trace_regs()
s390/stacktrace: save full stack traces
s390/stacktrace: add missing end marker
s390/stacktrace: fix address ranges for asynchronous and panic stack
s390/stacktrace: fix save_stack_trace_tsk() for current task
-rw-r--r-- | arch/s390/kernel/perf_event.c | 8 | ||||
-rw-r--r-- | arch/s390/kernel/stacktrace.c | 47 | ||||
-rw-r--r-- | arch/s390/kernel/trace.c | 3 | ||||
-rw-r--r-- | arch/s390/mm/maccess.c | 12 | ||||
-rw-r--r-- | arch/s390/oprofile/backtrace.c | 8 | ||||
-rw-r--r-- | drivers/s390/block/dasd.c | 1 | ||||
-rw-r--r-- | drivers/s390/block/dasd_alias.c | 23 |
7 files changed, 73 insertions, 29 deletions
diff --git a/arch/s390/kernel/perf_event.c b/arch/s390/kernel/perf_event.c index cfcba2dd9bb5..0943b11a2f6e 100644 --- a/arch/s390/kernel/perf_event.c +++ b/arch/s390/kernel/perf_event.c | |||
@@ -260,12 +260,13 @@ static unsigned long __store_trace(struct perf_callchain_entry *entry, | |||
260 | void perf_callchain_kernel(struct perf_callchain_entry *entry, | 260 | void perf_callchain_kernel(struct perf_callchain_entry *entry, |
261 | struct pt_regs *regs) | 261 | struct pt_regs *regs) |
262 | { | 262 | { |
263 | unsigned long head; | 263 | unsigned long head, frame_size; |
264 | struct stack_frame *head_sf; | 264 | struct stack_frame *head_sf; |
265 | 265 | ||
266 | if (user_mode(regs)) | 266 | if (user_mode(regs)) |
267 | return; | 267 | return; |
268 | 268 | ||
269 | frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs); | ||
269 | head = regs->gprs[15]; | 270 | head = regs->gprs[15]; |
270 | head_sf = (struct stack_frame *) head; | 271 | head_sf = (struct stack_frame *) head; |
271 | 272 | ||
@@ -273,8 +274,9 @@ void perf_callchain_kernel(struct perf_callchain_entry *entry, | |||
273 | return; | 274 | return; |
274 | 275 | ||
275 | head = head_sf->back_chain; | 276 | head = head_sf->back_chain; |
276 | head = __store_trace(entry, head, S390_lowcore.async_stack - ASYNC_SIZE, | 277 | head = __store_trace(entry, head, |
277 | S390_lowcore.async_stack); | 278 | S390_lowcore.async_stack + frame_size - ASYNC_SIZE, |
279 | S390_lowcore.async_stack + frame_size); | ||
278 | 280 | ||
279 | __store_trace(entry, head, S390_lowcore.thread_info, | 281 | __store_trace(entry, head, S390_lowcore.thread_info, |
280 | S390_lowcore.thread_info + THREAD_SIZE); | 282 | S390_lowcore.thread_info + THREAD_SIZE); |
diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c index 5acba3cb7220..8f64ebd63767 100644 --- a/arch/s390/kernel/stacktrace.c +++ b/arch/s390/kernel/stacktrace.c | |||
@@ -59,26 +59,32 @@ static unsigned long save_context_stack(struct stack_trace *trace, | |||
59 | } | 59 | } |
60 | } | 60 | } |
61 | 61 | ||
62 | void save_stack_trace(struct stack_trace *trace) | 62 | static void __save_stack_trace(struct stack_trace *trace, unsigned long sp) |
63 | { | 63 | { |
64 | register unsigned long sp asm ("15"); | 64 | unsigned long new_sp, frame_size; |
65 | unsigned long orig_sp, new_sp; | ||
66 | 65 | ||
67 | orig_sp = sp; | 66 | frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs); |
68 | new_sp = save_context_stack(trace, orig_sp, | 67 | new_sp = save_context_stack(trace, sp, |
69 | S390_lowcore.panic_stack - PAGE_SIZE, | 68 | S390_lowcore.panic_stack + frame_size - PAGE_SIZE, |
70 | S390_lowcore.panic_stack, 1); | 69 | S390_lowcore.panic_stack + frame_size, 1); |
71 | if (new_sp != orig_sp) | ||
72 | return; | ||
73 | new_sp = save_context_stack(trace, new_sp, | 70 | new_sp = save_context_stack(trace, new_sp, |
74 | S390_lowcore.async_stack - ASYNC_SIZE, | 71 | S390_lowcore.async_stack + frame_size - ASYNC_SIZE, |
75 | S390_lowcore.async_stack, 1); | 72 | S390_lowcore.async_stack + frame_size, 1); |
76 | if (new_sp != orig_sp) | ||
77 | return; | ||
78 | save_context_stack(trace, new_sp, | 73 | save_context_stack(trace, new_sp, |
79 | S390_lowcore.thread_info, | 74 | S390_lowcore.thread_info, |
80 | S390_lowcore.thread_info + THREAD_SIZE, 1); | 75 | S390_lowcore.thread_info + THREAD_SIZE, 1); |
81 | } | 76 | } |
77 | |||
78 | void save_stack_trace(struct stack_trace *trace) | ||
79 | { | ||
80 | register unsigned long r15 asm ("15"); | ||
81 | unsigned long sp; | ||
82 | |||
83 | sp = r15; | ||
84 | __save_stack_trace(trace, sp); | ||
85 | if (trace->nr_entries < trace->max_entries) | ||
86 | trace->entries[trace->nr_entries++] = ULONG_MAX; | ||
87 | } | ||
82 | EXPORT_SYMBOL_GPL(save_stack_trace); | 88 | EXPORT_SYMBOL_GPL(save_stack_trace); |
83 | 89 | ||
84 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | 90 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) |
@@ -86,6 +92,10 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |||
86 | unsigned long sp, low, high; | 92 | unsigned long sp, low, high; |
87 | 93 | ||
88 | sp = tsk->thread.ksp; | 94 | sp = tsk->thread.ksp; |
95 | if (tsk == current) { | ||
96 | /* Get current stack pointer. */ | ||
97 | asm volatile("la %0,0(15)" : "=a" (sp)); | ||
98 | } | ||
89 | low = (unsigned long) task_stack_page(tsk); | 99 | low = (unsigned long) task_stack_page(tsk); |
90 | high = (unsigned long) task_pt_regs(tsk); | 100 | high = (unsigned long) task_pt_regs(tsk); |
91 | save_context_stack(trace, sp, low, high, 0); | 101 | save_context_stack(trace, sp, low, high, 0); |
@@ -93,3 +103,14 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |||
93 | trace->entries[trace->nr_entries++] = ULONG_MAX; | 103 | trace->entries[trace->nr_entries++] = ULONG_MAX; |
94 | } | 104 | } |
95 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); | 105 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); |
106 | |||
107 | void save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace) | ||
108 | { | ||
109 | unsigned long sp; | ||
110 | |||
111 | sp = kernel_stack_pointer(regs); | ||
112 | __save_stack_trace(trace, sp); | ||
113 | if (trace->nr_entries < trace->max_entries) | ||
114 | trace->entries[trace->nr_entries++] = ULONG_MAX; | ||
115 | } | ||
116 | EXPORT_SYMBOL_GPL(save_stack_trace_regs); | ||
diff --git a/arch/s390/kernel/trace.c b/arch/s390/kernel/trace.c index 21a5df99552b..dde7654f5c68 100644 --- a/arch/s390/kernel/trace.c +++ b/arch/s390/kernel/trace.c | |||
@@ -18,6 +18,9 @@ void trace_s390_diagnose_norecursion(int diag_nr) | |||
18 | unsigned long flags; | 18 | unsigned long flags; |
19 | unsigned int *depth; | 19 | unsigned int *depth; |
20 | 20 | ||
21 | /* Avoid lockdep recursion. */ | ||
22 | if (IS_ENABLED(CONFIG_LOCKDEP)) | ||
23 | return; | ||
21 | local_irq_save(flags); | 24 | local_irq_save(flags); |
22 | depth = this_cpu_ptr(&diagnose_trace_depth); | 25 | depth = this_cpu_ptr(&diagnose_trace_depth); |
23 | if (*depth == 0) { | 26 | if (*depth == 0) { |
diff --git a/arch/s390/mm/maccess.c b/arch/s390/mm/maccess.c index fec59c067d0d..792f9c63fbca 100644 --- a/arch/s390/mm/maccess.c +++ b/arch/s390/mm/maccess.c | |||
@@ -93,15 +93,19 @@ static int __memcpy_real(void *dest, void *src, size_t count) | |||
93 | */ | 93 | */ |
94 | int memcpy_real(void *dest, void *src, size_t count) | 94 | int memcpy_real(void *dest, void *src, size_t count) |
95 | { | 95 | { |
96 | int irqs_disabled, rc; | ||
96 | unsigned long flags; | 97 | unsigned long flags; |
97 | int rc; | ||
98 | 98 | ||
99 | if (!count) | 99 | if (!count) |
100 | return 0; | 100 | return 0; |
101 | local_irq_save(flags); | 101 | flags = __arch_local_irq_stnsm(0xf8UL); |
102 | __arch_local_irq_stnsm(0xfbUL); | 102 | irqs_disabled = arch_irqs_disabled_flags(flags); |
103 | if (!irqs_disabled) | ||
104 | trace_hardirqs_off(); | ||
103 | rc = __memcpy_real(dest, src, count); | 105 | rc = __memcpy_real(dest, src, count); |
104 | local_irq_restore(flags); | 106 | if (!irqs_disabled) |
107 | trace_hardirqs_on(); | ||
108 | __arch_local_irq_ssm(flags); | ||
105 | return rc; | 109 | return rc; |
106 | } | 110 | } |
107 | 111 | ||
diff --git a/arch/s390/oprofile/backtrace.c b/arch/s390/oprofile/backtrace.c index fe0bfe370c45..1884e1759529 100644 --- a/arch/s390/oprofile/backtrace.c +++ b/arch/s390/oprofile/backtrace.c | |||
@@ -54,12 +54,13 @@ __show_trace(unsigned int *depth, unsigned long sp, | |||
54 | 54 | ||
55 | void s390_backtrace(struct pt_regs * const regs, unsigned int depth) | 55 | void s390_backtrace(struct pt_regs * const regs, unsigned int depth) |
56 | { | 56 | { |
57 | unsigned long head; | 57 | unsigned long head, frame_size; |
58 | struct stack_frame* head_sf; | 58 | struct stack_frame* head_sf; |
59 | 59 | ||
60 | if (user_mode(regs)) | 60 | if (user_mode(regs)) |
61 | return; | 61 | return; |
62 | 62 | ||
63 | frame_size = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs); | ||
63 | head = regs->gprs[15]; | 64 | head = regs->gprs[15]; |
64 | head_sf = (struct stack_frame*)head; | 65 | head_sf = (struct stack_frame*)head; |
65 | 66 | ||
@@ -68,8 +69,9 @@ void s390_backtrace(struct pt_regs * const regs, unsigned int depth) | |||
68 | 69 | ||
69 | head = head_sf->back_chain; | 70 | head = head_sf->back_chain; |
70 | 71 | ||
71 | head = __show_trace(&depth, head, S390_lowcore.async_stack - ASYNC_SIZE, | 72 | head = __show_trace(&depth, head, |
72 | S390_lowcore.async_stack); | 73 | S390_lowcore.async_stack + frame_size - ASYNC_SIZE, |
74 | S390_lowcore.async_stack + frame_size); | ||
73 | 75 | ||
74 | __show_trace(&depth, head, S390_lowcore.thread_info, | 76 | __show_trace(&depth, head, S390_lowcore.thread_info, |
75 | S390_lowcore.thread_info + THREAD_SIZE); | 77 | S390_lowcore.thread_info + THREAD_SIZE); |
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 41605dac8309..c78db05e75b1 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c | |||
@@ -3035,6 +3035,7 @@ static void dasd_setup_queue(struct dasd_block *block) | |||
3035 | max = block->base->discipline->max_blocks << block->s2b_shift; | 3035 | max = block->base->discipline->max_blocks << block->s2b_shift; |
3036 | } | 3036 | } |
3037 | queue_flag_set_unlocked(QUEUE_FLAG_NONROT, block->request_queue); | 3037 | queue_flag_set_unlocked(QUEUE_FLAG_NONROT, block->request_queue); |
3038 | block->request_queue->limits.max_dev_sectors = max; | ||
3038 | blk_queue_logical_block_size(block->request_queue, | 3039 | blk_queue_logical_block_size(block->request_queue, |
3039 | block->bp_block); | 3040 | block->bp_block); |
3040 | blk_queue_max_hw_sectors(block->request_queue, max); | 3041 | blk_queue_max_hw_sectors(block->request_queue, max); |
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index 184b1dbeb554..286782c60da4 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c | |||
@@ -264,8 +264,10 @@ void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device) | |||
264 | spin_unlock_irqrestore(&lcu->lock, flags); | 264 | spin_unlock_irqrestore(&lcu->lock, flags); |
265 | cancel_work_sync(&lcu->suc_data.worker); | 265 | cancel_work_sync(&lcu->suc_data.worker); |
266 | spin_lock_irqsave(&lcu->lock, flags); | 266 | spin_lock_irqsave(&lcu->lock, flags); |
267 | if (device == lcu->suc_data.device) | 267 | if (device == lcu->suc_data.device) { |
268 | dasd_put_device(device); | ||
268 | lcu->suc_data.device = NULL; | 269 | lcu->suc_data.device = NULL; |
270 | } | ||
269 | } | 271 | } |
270 | was_pending = 0; | 272 | was_pending = 0; |
271 | if (device == lcu->ruac_data.device) { | 273 | if (device == lcu->ruac_data.device) { |
@@ -273,8 +275,10 @@ void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device) | |||
273 | was_pending = 1; | 275 | was_pending = 1; |
274 | cancel_delayed_work_sync(&lcu->ruac_data.dwork); | 276 | cancel_delayed_work_sync(&lcu->ruac_data.dwork); |
275 | spin_lock_irqsave(&lcu->lock, flags); | 277 | spin_lock_irqsave(&lcu->lock, flags); |
276 | if (device == lcu->ruac_data.device) | 278 | if (device == lcu->ruac_data.device) { |
279 | dasd_put_device(device); | ||
277 | lcu->ruac_data.device = NULL; | 280 | lcu->ruac_data.device = NULL; |
281 | } | ||
278 | } | 282 | } |
279 | private->lcu = NULL; | 283 | private->lcu = NULL; |
280 | spin_unlock_irqrestore(&lcu->lock, flags); | 284 | spin_unlock_irqrestore(&lcu->lock, flags); |
@@ -549,8 +553,10 @@ static void lcu_update_work(struct work_struct *work) | |||
549 | if ((rc && (rc != -EOPNOTSUPP)) || (lcu->flags & NEED_UAC_UPDATE)) { | 553 | if ((rc && (rc != -EOPNOTSUPP)) || (lcu->flags & NEED_UAC_UPDATE)) { |
550 | DBF_DEV_EVENT(DBF_WARNING, device, "could not update" | 554 | DBF_DEV_EVENT(DBF_WARNING, device, "could not update" |
551 | " alias data in lcu (rc = %d), retry later", rc); | 555 | " alias data in lcu (rc = %d), retry later", rc); |
552 | schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ); | 556 | if (!schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ)) |
557 | dasd_put_device(device); | ||
553 | } else { | 558 | } else { |
559 | dasd_put_device(device); | ||
554 | lcu->ruac_data.device = NULL; | 560 | lcu->ruac_data.device = NULL; |
555 | lcu->flags &= ~UPDATE_PENDING; | 561 | lcu->flags &= ~UPDATE_PENDING; |
556 | } | 562 | } |
@@ -593,8 +599,10 @@ static int _schedule_lcu_update(struct alias_lcu *lcu, | |||
593 | */ | 599 | */ |
594 | if (!usedev) | 600 | if (!usedev) |
595 | return -EINVAL; | 601 | return -EINVAL; |
602 | dasd_get_device(usedev); | ||
596 | lcu->ruac_data.device = usedev; | 603 | lcu->ruac_data.device = usedev; |
597 | schedule_delayed_work(&lcu->ruac_data.dwork, 0); | 604 | if (!schedule_delayed_work(&lcu->ruac_data.dwork, 0)) |
605 | dasd_put_device(usedev); | ||
598 | return 0; | 606 | return 0; |
599 | } | 607 | } |
600 | 608 | ||
@@ -723,7 +731,7 @@ static int reset_summary_unit_check(struct alias_lcu *lcu, | |||
723 | ASCEBC((char *) &cqr->magic, 4); | 731 | ASCEBC((char *) &cqr->magic, 4); |
724 | ccw = cqr->cpaddr; | 732 | ccw = cqr->cpaddr; |
725 | ccw->cmd_code = DASD_ECKD_CCW_RSCK; | 733 | ccw->cmd_code = DASD_ECKD_CCW_RSCK; |
726 | ccw->flags = 0 ; | 734 | ccw->flags = CCW_FLAG_SLI; |
727 | ccw->count = 16; | 735 | ccw->count = 16; |
728 | ccw->cda = (__u32)(addr_t) cqr->data; | 736 | ccw->cda = (__u32)(addr_t) cqr->data; |
729 | ((char *)cqr->data)[0] = reason; | 737 | ((char *)cqr->data)[0] = reason; |
@@ -930,6 +938,7 @@ static void summary_unit_check_handling_work(struct work_struct *work) | |||
930 | /* 3. read new alias configuration */ | 938 | /* 3. read new alias configuration */ |
931 | _schedule_lcu_update(lcu, device); | 939 | _schedule_lcu_update(lcu, device); |
932 | lcu->suc_data.device = NULL; | 940 | lcu->suc_data.device = NULL; |
941 | dasd_put_device(device); | ||
933 | spin_unlock_irqrestore(&lcu->lock, flags); | 942 | spin_unlock_irqrestore(&lcu->lock, flags); |
934 | } | 943 | } |
935 | 944 | ||
@@ -989,6 +998,8 @@ void dasd_alias_handle_summary_unit_check(struct dasd_device *device, | |||
989 | } | 998 | } |
990 | lcu->suc_data.reason = reason; | 999 | lcu->suc_data.reason = reason; |
991 | lcu->suc_data.device = device; | 1000 | lcu->suc_data.device = device; |
1001 | dasd_get_device(device); | ||
992 | spin_unlock(&lcu->lock); | 1002 | spin_unlock(&lcu->lock); |
993 | schedule_work(&lcu->suc_data.worker); | 1003 | if (!schedule_work(&lcu->suc_data.worker)) |
1004 | dasd_put_device(device); | ||
994 | }; | 1005 | }; |