aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-09-22 12:32:07 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2017-09-22 12:32:07 -0400
commitc8107ed99ad02d519d4fc1c3017ac7cede47e788 (patch)
treef5dc1703510b8675f97ee3bd2bf13deeaf70af2a
parent7a6d0071d821965349ff853041f1c1aab496f2d9 (diff)
parent51dce3867c6c63c7500332e5448c2ba76808d6b5 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 fixes from Martin Schwidefsky: - A couple of bug fixes: memory management, perf, cio, dasd and scm_blk. - A larger change in regard to the CPU topology to improve performance for systems running under z/VM or KVM. * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: s390/topology: enable / disable topology dynamically s390/topology: alternative topology for topology-less machines s390/mm: fix write access check in gup_huge_pmd() s390/mm: make pmdp_invalidate() do invalidation only s390/cio: recover from bad paths s390/scm_blk: consistently use blk_status_t as error type s390/dasd: fix race during dasd initialization s390/perf: fix bug when creating per-thread event
-rw-r--r--arch/s390/include/asm/pgtable.h4
-rw-r--r--arch/s390/kernel/early.c12
-rw-r--r--arch/s390/kernel/perf_cpum_sf.c9
-rw-r--r--arch/s390/kernel/topology.c148
-rw-r--r--arch/s390/mm/gup.c7
-rw-r--r--drivers/s390/block/dasd.c12
-rw-r--r--drivers/s390/block/scm_blk.c6
-rw-r--r--drivers/s390/cio/device.c12
-rw-r--r--drivers/s390/cio/device.h1
-rw-r--r--drivers/s390/cio/device_fsm.c12
-rw-r--r--drivers/s390/cio/io_sch.h2
11 files changed, 183 insertions, 42 deletions
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h
index dce708e061ea..20e75a2ca93a 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -1507,7 +1507,9 @@ static inline pmd_t pmdp_huge_clear_flush(struct vm_area_struct *vma,
1507static inline void pmdp_invalidate(struct vm_area_struct *vma, 1507static inline void pmdp_invalidate(struct vm_area_struct *vma,
1508 unsigned long addr, pmd_t *pmdp) 1508 unsigned long addr, pmd_t *pmdp)
1509{ 1509{
1510 pmdp_xchg_direct(vma->vm_mm, addr, pmdp, __pmd(_SEGMENT_ENTRY_EMPTY)); 1510 pmd_t pmd = __pmd(pmd_val(*pmdp) | _SEGMENT_ENTRY_INVALID);
1511
1512 pmdp_xchg_direct(vma->vm_mm, addr, pmdp, pmd);
1511} 1513}
1512 1514
1513#define __HAVE_ARCH_PMDP_SET_WRPROTECT 1515#define __HAVE_ARCH_PMDP_SET_WRPROTECT
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index ca8cd80e8feb..60181caf8e8a 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -404,18 +404,6 @@ static inline void save_vector_registers(void)
404#endif 404#endif
405} 405}
406 406
407static int __init topology_setup(char *str)
408{
409 bool enabled;
410 int rc;
411
412 rc = kstrtobool(str, &enabled);
413 if (!rc && !enabled)
414 S390_lowcore.machine_flags &= ~MACHINE_FLAG_TOPOLOGY;
415 return rc;
416}
417early_param("topology", topology_setup);
418
419static int __init disable_vector_extension(char *str) 407static int __init disable_vector_extension(char *str)
420{ 408{
421 S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX; 409 S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX;
diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c
index c1bf75ffb875..7e1e40323b78 100644
--- a/arch/s390/kernel/perf_cpum_sf.c
+++ b/arch/s390/kernel/perf_cpum_sf.c
@@ -823,9 +823,12 @@ static int cpumsf_pmu_event_init(struct perf_event *event)
823 } 823 }
824 824
825 /* Check online status of the CPU to which the event is pinned */ 825 /* Check online status of the CPU to which the event is pinned */
826 if ((unsigned int)event->cpu >= nr_cpumask_bits || 826 if (event->cpu >= 0) {
827 (event->cpu >= 0 && !cpu_online(event->cpu))) 827 if ((unsigned int)event->cpu >= nr_cpumask_bits)
828 return -ENODEV; 828 return -ENODEV;
829 if (!cpu_online(event->cpu))
830 return -ENODEV;
831 }
829 832
830 /* Force reset of idle/hv excludes regardless of what the 833 /* Force reset of idle/hv excludes regardless of what the
831 * user requested. 834 * user requested.
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c
index bb47c92476f0..ed0bdd220e1a 100644
--- a/arch/s390/kernel/topology.c
+++ b/arch/s390/kernel/topology.c
@@ -8,6 +8,8 @@
8 8
9#include <linux/workqueue.h> 9#include <linux/workqueue.h>
10#include <linux/bootmem.h> 10#include <linux/bootmem.h>
11#include <linux/uaccess.h>
12#include <linux/sysctl.h>
11#include <linux/cpuset.h> 13#include <linux/cpuset.h>
12#include <linux/device.h> 14#include <linux/device.h>
13#include <linux/export.h> 15#include <linux/export.h>
@@ -29,12 +31,20 @@
29#define PTF_VERTICAL (1UL) 31#define PTF_VERTICAL (1UL)
30#define PTF_CHECK (2UL) 32#define PTF_CHECK (2UL)
31 33
34enum {
35 TOPOLOGY_MODE_HW,
36 TOPOLOGY_MODE_SINGLE,
37 TOPOLOGY_MODE_PACKAGE,
38 TOPOLOGY_MODE_UNINITIALIZED
39};
40
32struct mask_info { 41struct mask_info {
33 struct mask_info *next; 42 struct mask_info *next;
34 unsigned char id; 43 unsigned char id;
35 cpumask_t mask; 44 cpumask_t mask;
36}; 45};
37 46
47static int topology_mode = TOPOLOGY_MODE_UNINITIALIZED;
38static void set_topology_timer(void); 48static void set_topology_timer(void);
39static void topology_work_fn(struct work_struct *work); 49static void topology_work_fn(struct work_struct *work);
40static struct sysinfo_15_1_x *tl_info; 50static struct sysinfo_15_1_x *tl_info;
@@ -59,11 +69,26 @@ static cpumask_t cpu_group_map(struct mask_info *info, unsigned int cpu)
59 cpumask_t mask; 69 cpumask_t mask;
60 70
61 cpumask_copy(&mask, cpumask_of(cpu)); 71 cpumask_copy(&mask, cpumask_of(cpu));
62 if (!MACHINE_HAS_TOPOLOGY) 72 switch (topology_mode) {
63 return mask; 73 case TOPOLOGY_MODE_HW:
64 for (; info; info = info->next) { 74 while (info) {
65 if (cpumask_test_cpu(cpu, &info->mask)) 75 if (cpumask_test_cpu(cpu, &info->mask)) {
66 return info->mask; 76 mask = info->mask;
77 break;
78 }
79 info = info->next;
80 }
81 if (cpumask_empty(&mask))
82 cpumask_copy(&mask, cpumask_of(cpu));
83 break;
84 case TOPOLOGY_MODE_PACKAGE:
85 cpumask_copy(&mask, cpu_present_mask);
86 break;
87 default:
88 /* fallthrough */
89 case TOPOLOGY_MODE_SINGLE:
90 cpumask_copy(&mask, cpumask_of(cpu));
91 break;
67 } 92 }
68 return mask; 93 return mask;
69} 94}
@@ -74,7 +99,7 @@ static cpumask_t cpu_thread_map(unsigned int cpu)
74 int i; 99 int i;
75 100
76 cpumask_copy(&mask, cpumask_of(cpu)); 101 cpumask_copy(&mask, cpumask_of(cpu));
77 if (!MACHINE_HAS_TOPOLOGY) 102 if (topology_mode != TOPOLOGY_MODE_HW)
78 return mask; 103 return mask;
79 cpu -= cpu % (smp_cpu_mtid + 1); 104 cpu -= cpu % (smp_cpu_mtid + 1);
80 for (i = 0; i <= smp_cpu_mtid; i++) 105 for (i = 0; i <= smp_cpu_mtid; i++)
@@ -184,10 +209,8 @@ static void topology_update_polarization_simple(void)
184{ 209{
185 int cpu; 210 int cpu;
186 211
187 mutex_lock(&smp_cpu_state_mutex);
188 for_each_possible_cpu(cpu) 212 for_each_possible_cpu(cpu)
189 smp_cpu_set_polarization(cpu, POLARIZATION_HRZ); 213 smp_cpu_set_polarization(cpu, POLARIZATION_HRZ);
190 mutex_unlock(&smp_cpu_state_mutex);
191} 214}
192 215
193static int ptf(unsigned long fc) 216static int ptf(unsigned long fc)
@@ -223,7 +246,7 @@ int topology_set_cpu_management(int fc)
223static void update_cpu_masks(void) 246static void update_cpu_masks(void)
224{ 247{
225 struct cpu_topology_s390 *topo; 248 struct cpu_topology_s390 *topo;
226 int cpu; 249 int cpu, id;
227 250
228 for_each_possible_cpu(cpu) { 251 for_each_possible_cpu(cpu) {
229 topo = &cpu_topology[cpu]; 252 topo = &cpu_topology[cpu];
@@ -231,12 +254,13 @@ static void update_cpu_masks(void)
231 topo->core_mask = cpu_group_map(&socket_info, cpu); 254 topo->core_mask = cpu_group_map(&socket_info, cpu);
232 topo->book_mask = cpu_group_map(&book_info, cpu); 255 topo->book_mask = cpu_group_map(&book_info, cpu);
233 topo->drawer_mask = cpu_group_map(&drawer_info, cpu); 256 topo->drawer_mask = cpu_group_map(&drawer_info, cpu);
234 if (!MACHINE_HAS_TOPOLOGY) { 257 if (topology_mode != TOPOLOGY_MODE_HW) {
258 id = topology_mode == TOPOLOGY_MODE_PACKAGE ? 0 : cpu;
235 topo->thread_id = cpu; 259 topo->thread_id = cpu;
236 topo->core_id = cpu; 260 topo->core_id = cpu;
237 topo->socket_id = cpu; 261 topo->socket_id = id;
238 topo->book_id = cpu; 262 topo->book_id = id;
239 topo->drawer_id = cpu; 263 topo->drawer_id = id;
240 if (cpu_present(cpu)) 264 if (cpu_present(cpu))
241 cpumask_set_cpu(cpu, &cpus_with_topology); 265 cpumask_set_cpu(cpu, &cpus_with_topology);
242 } 266 }
@@ -254,6 +278,7 @@ static int __arch_update_cpu_topology(void)
254 struct sysinfo_15_1_x *info = tl_info; 278 struct sysinfo_15_1_x *info = tl_info;
255 int rc = 0; 279 int rc = 0;
256 280
281 mutex_lock(&smp_cpu_state_mutex);
257 cpumask_clear(&cpus_with_topology); 282 cpumask_clear(&cpus_with_topology);
258 if (MACHINE_HAS_TOPOLOGY) { 283 if (MACHINE_HAS_TOPOLOGY) {
259 rc = 1; 284 rc = 1;
@@ -263,6 +288,7 @@ static int __arch_update_cpu_topology(void)
263 update_cpu_masks(); 288 update_cpu_masks();
264 if (!MACHINE_HAS_TOPOLOGY) 289 if (!MACHINE_HAS_TOPOLOGY)
265 topology_update_polarization_simple(); 290 topology_update_polarization_simple();
291 mutex_unlock(&smp_cpu_state_mutex);
266 return rc; 292 return rc;
267} 293}
268 294
@@ -289,6 +315,11 @@ void topology_schedule_update(void)
289 schedule_work(&topology_work); 315 schedule_work(&topology_work);
290} 316}
291 317
318static void topology_flush_work(void)
319{
320 flush_work(&topology_work);
321}
322
292static void topology_timer_fn(unsigned long ignored) 323static void topology_timer_fn(unsigned long ignored)
293{ 324{
294 if (ptf(PTF_CHECK)) 325 if (ptf(PTF_CHECK))
@@ -459,6 +490,12 @@ void __init topology_init_early(void)
459 struct sysinfo_15_1_x *info; 490 struct sysinfo_15_1_x *info;
460 491
461 set_sched_topology(s390_topology); 492 set_sched_topology(s390_topology);
493 if (topology_mode == TOPOLOGY_MODE_UNINITIALIZED) {
494 if (MACHINE_HAS_TOPOLOGY)
495 topology_mode = TOPOLOGY_MODE_HW;
496 else
497 topology_mode = TOPOLOGY_MODE_SINGLE;
498 }
462 if (!MACHINE_HAS_TOPOLOGY) 499 if (!MACHINE_HAS_TOPOLOGY)
463 goto out; 500 goto out;
464 tl_info = memblock_virt_alloc(PAGE_SIZE, PAGE_SIZE); 501 tl_info = memblock_virt_alloc(PAGE_SIZE, PAGE_SIZE);
@@ -474,12 +511,97 @@ out:
474 __arch_update_cpu_topology(); 511 __arch_update_cpu_topology();
475} 512}
476 513
514static inline int topology_get_mode(int enabled)
515{
516 if (!enabled)
517 return TOPOLOGY_MODE_SINGLE;
518 return MACHINE_HAS_TOPOLOGY ? TOPOLOGY_MODE_HW : TOPOLOGY_MODE_PACKAGE;
519}
520
521static inline int topology_is_enabled(void)
522{
523 return topology_mode != TOPOLOGY_MODE_SINGLE;
524}
525
526static int __init topology_setup(char *str)
527{
528 bool enabled;
529 int rc;
530
531 rc = kstrtobool(str, &enabled);
532 if (rc)
533 return rc;
534 topology_mode = topology_get_mode(enabled);
535 return 0;
536}
537early_param("topology", topology_setup);
538
539static int topology_ctl_handler(struct ctl_table *ctl, int write,
540 void __user *buffer, size_t *lenp, loff_t *ppos)
541{
542 unsigned int len;
543 int new_mode;
544 char buf[2];
545
546 if (!*lenp || *ppos) {
547 *lenp = 0;
548 return 0;
549 }
550 if (!write) {
551 strncpy(buf, topology_is_enabled() ? "1\n" : "0\n",
552 ARRAY_SIZE(buf));
553 len = strnlen(buf, ARRAY_SIZE(buf));
554 if (len > *lenp)
555 len = *lenp;
556 if (copy_to_user(buffer, buf, len))
557 return -EFAULT;
558 goto out;
559 }
560 len = *lenp;
561 if (copy_from_user(buf, buffer, len > sizeof(buf) ? sizeof(buf) : len))
562 return -EFAULT;
563 if (buf[0] != '0' && buf[0] != '1')
564 return -EINVAL;
565 mutex_lock(&smp_cpu_state_mutex);
566 new_mode = topology_get_mode(buf[0] == '1');
567 if (topology_mode != new_mode) {
568 topology_mode = new_mode;
569 topology_schedule_update();
570 }
571 mutex_unlock(&smp_cpu_state_mutex);
572 topology_flush_work();
573out:
574 *lenp = len;
575 *ppos += len;
576 return 0;
577}
578
579static struct ctl_table topology_ctl_table[] = {
580 {
581 .procname = "topology",
582 .mode = 0644,
583 .proc_handler = topology_ctl_handler,
584 },
585 { },
586};
587
588static struct ctl_table topology_dir_table[] = {
589 {
590 .procname = "s390",
591 .maxlen = 0,
592 .mode = 0555,
593 .child = topology_ctl_table,
594 },
595 { },
596};
597
477static int __init topology_init(void) 598static int __init topology_init(void)
478{ 599{
479 if (MACHINE_HAS_TOPOLOGY) 600 if (MACHINE_HAS_TOPOLOGY)
480 set_topology_timer(); 601 set_topology_timer();
481 else 602 else
482 topology_update_polarization_simple(); 603 topology_update_polarization_simple();
604 register_sysctl_table(topology_dir_table);
483 return device_create_file(cpu_subsys.dev_root, &dev_attr_dispatching); 605 return device_create_file(cpu_subsys.dev_root, &dev_attr_dispatching);
484} 606}
485device_initcall(topology_init); 607device_initcall(topology_init);
diff --git a/arch/s390/mm/gup.c b/arch/s390/mm/gup.c
index 8ecc25e760fa..98ffe3ee9411 100644
--- a/arch/s390/mm/gup.c
+++ b/arch/s390/mm/gup.c
@@ -56,13 +56,12 @@ static inline int gup_pte_range(pmd_t *pmdp, pmd_t pmd, unsigned long addr,
56static inline int gup_huge_pmd(pmd_t *pmdp, pmd_t pmd, unsigned long addr, 56static inline int gup_huge_pmd(pmd_t *pmdp, pmd_t pmd, unsigned long addr,
57 unsigned long end, int write, struct page **pages, int *nr) 57 unsigned long end, int write, struct page **pages, int *nr)
58{ 58{
59 unsigned long mask, result;
60 struct page *head, *page; 59 struct page *head, *page;
60 unsigned long mask;
61 int refs; 61 int refs;
62 62
63 result = write ? 0 : _SEGMENT_ENTRY_PROTECT; 63 mask = (write ? _SEGMENT_ENTRY_PROTECT : 0) | _SEGMENT_ENTRY_INVALID;
64 mask = result | _SEGMENT_ENTRY_INVALID; 64 if ((pmd_val(pmd) & mask) != 0)
65 if ((pmd_val(pmd) & mask) != result)
66 return 0; 65 return 0;
67 VM_BUG_ON(!pfn_valid(pmd_val(pmd) >> PAGE_SHIFT)); 66 VM_BUG_ON(!pfn_valid(pmd_val(pmd) >> PAGE_SHIFT));
68 67
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index ea19b4ff87a2..29f35e29d480 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1644,7 +1644,9 @@ void dasd_generic_handle_state_change(struct dasd_device *device)
1644 dasd_schedule_device_bh(device); 1644 dasd_schedule_device_bh(device);
1645 if (device->block) { 1645 if (device->block) {
1646 dasd_schedule_block_bh(device->block); 1646 dasd_schedule_block_bh(device->block);
1647 blk_mq_run_hw_queues(device->block->request_queue, true); 1647 if (device->block->request_queue)
1648 blk_mq_run_hw_queues(device->block->request_queue,
1649 true);
1648 } 1650 }
1649} 1651}
1650EXPORT_SYMBOL_GPL(dasd_generic_handle_state_change); 1652EXPORT_SYMBOL_GPL(dasd_generic_handle_state_change);
@@ -3759,7 +3761,9 @@ int dasd_generic_path_operational(struct dasd_device *device)
3759 dasd_schedule_device_bh(device); 3761 dasd_schedule_device_bh(device);
3760 if (device->block) { 3762 if (device->block) {
3761 dasd_schedule_block_bh(device->block); 3763 dasd_schedule_block_bh(device->block);
3762 blk_mq_run_hw_queues(device->block->request_queue, true); 3764 if (device->block->request_queue)
3765 blk_mq_run_hw_queues(device->block->request_queue,
3766 true);
3763 } 3767 }
3764 3768
3765 if (!device->stopped) 3769 if (!device->stopped)
@@ -4025,7 +4029,9 @@ int dasd_generic_restore_device(struct ccw_device *cdev)
4025 4029
4026 if (device->block) { 4030 if (device->block) {
4027 dasd_schedule_block_bh(device->block); 4031 dasd_schedule_block_bh(device->block);
4028 blk_mq_run_hw_queues(device->block->request_queue, true); 4032 if (device->block->request_queue)
4033 blk_mq_run_hw_queues(device->block->request_queue,
4034 true);
4029 } 4035 }
4030 4036
4031 clear_bit(DASD_FLAG_SUSPENDED, &device->flags); 4037 clear_bit(DASD_FLAG_SUSPENDED, &device->flags);
diff --git a/drivers/s390/block/scm_blk.c b/drivers/s390/block/scm_blk.c
index 2e7fd966c515..eb51893c74a4 100644
--- a/drivers/s390/block/scm_blk.c
+++ b/drivers/s390/block/scm_blk.c
@@ -249,7 +249,7 @@ static void scm_request_requeue(struct scm_request *scmrq)
249static void scm_request_finish(struct scm_request *scmrq) 249static void scm_request_finish(struct scm_request *scmrq)
250{ 250{
251 struct scm_blk_dev *bdev = scmrq->bdev; 251 struct scm_blk_dev *bdev = scmrq->bdev;
252 int *error; 252 blk_status_t *error;
253 int i; 253 int i;
254 254
255 for (i = 0; i < nr_requests_per_io && scmrq->request[i]; i++) { 255 for (i = 0; i < nr_requests_per_io && scmrq->request[i]; i++) {
@@ -415,7 +415,7 @@ void scm_blk_irq(struct scm_device *scmdev, void *data, blk_status_t error)
415 415
416static void scm_blk_request_done(struct request *req) 416static void scm_blk_request_done(struct request *req)
417{ 417{
418 int *error = blk_mq_rq_to_pdu(req); 418 blk_status_t *error = blk_mq_rq_to_pdu(req);
419 419
420 blk_mq_end_request(req, *error); 420 blk_mq_end_request(req, *error);
421} 421}
@@ -450,7 +450,7 @@ int scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev)
450 atomic_set(&bdev->queued_reqs, 0); 450 atomic_set(&bdev->queued_reqs, 0);
451 451
452 bdev->tag_set.ops = &scm_mq_ops; 452 bdev->tag_set.ops = &scm_mq_ops;
453 bdev->tag_set.cmd_size = sizeof(int); 453 bdev->tag_set.cmd_size = sizeof(blk_status_t);
454 bdev->tag_set.nr_hw_queues = nr_requests; 454 bdev->tag_set.nr_hw_queues = nr_requests;
455 bdev->tag_set.queue_depth = nr_requests_per_io * nr_requests; 455 bdev->tag_set.queue_depth = nr_requests_per_io * nr_requests;
456 bdev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; 456 bdev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 489b583f263d..e5c32f4b5287 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -1225,10 +1225,16 @@ static int device_is_disconnected(struct ccw_device *cdev)
1225static int recovery_check(struct device *dev, void *data) 1225static int recovery_check(struct device *dev, void *data)
1226{ 1226{
1227 struct ccw_device *cdev = to_ccwdev(dev); 1227 struct ccw_device *cdev = to_ccwdev(dev);
1228 struct subchannel *sch;
1228 int *redo = data; 1229 int *redo = data;
1229 1230
1230 spin_lock_irq(cdev->ccwlock); 1231 spin_lock_irq(cdev->ccwlock);
1231 switch (cdev->private->state) { 1232 switch (cdev->private->state) {
1233 case DEV_STATE_ONLINE:
1234 sch = to_subchannel(cdev->dev.parent);
1235 if ((sch->schib.pmcw.pam & sch->opm) == sch->vpm)
1236 break;
1237 /* fall through */
1232 case DEV_STATE_DISCONNECTED: 1238 case DEV_STATE_DISCONNECTED:
1233 CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n", 1239 CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n",
1234 cdev->private->dev_id.ssid, 1240 cdev->private->dev_id.ssid,
@@ -1260,7 +1266,7 @@ static void recovery_work_func(struct work_struct *unused)
1260 } 1266 }
1261 spin_unlock_irq(&recovery_lock); 1267 spin_unlock_irq(&recovery_lock);
1262 } else 1268 } else
1263 CIO_MSG_EVENT(4, "recovery: end\n"); 1269 CIO_MSG_EVENT(3, "recovery: end\n");
1264} 1270}
1265 1271
1266static DECLARE_WORK(recovery_work, recovery_work_func); 1272static DECLARE_WORK(recovery_work, recovery_work_func);
@@ -1274,11 +1280,11 @@ static void recovery_func(unsigned long data)
1274 schedule_work(&recovery_work); 1280 schedule_work(&recovery_work);
1275} 1281}
1276 1282
1277static void ccw_device_schedule_recovery(void) 1283void ccw_device_schedule_recovery(void)
1278{ 1284{
1279 unsigned long flags; 1285 unsigned long flags;
1280 1286
1281 CIO_MSG_EVENT(4, "recovery: schedule\n"); 1287 CIO_MSG_EVENT(3, "recovery: schedule\n");
1282 spin_lock_irqsave(&recovery_lock, flags); 1288 spin_lock_irqsave(&recovery_lock, flags);
1283 if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) { 1289 if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) {
1284 recovery_phase = 0; 1290 recovery_phase = 0;
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index ec497af99dd8..69cb70f080a5 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -134,6 +134,7 @@ void ccw_device_set_disconnected(struct ccw_device *cdev);
134void ccw_device_set_notoper(struct ccw_device *cdev); 134void ccw_device_set_notoper(struct ccw_device *cdev);
135 135
136void ccw_device_set_timeout(struct ccw_device *, int); 136void ccw_device_set_timeout(struct ccw_device *, int);
137void ccw_device_schedule_recovery(void);
137 138
138/* Channel measurement facility related */ 139/* Channel measurement facility related */
139void retry_set_schib(struct ccw_device *cdev); 140void retry_set_schib(struct ccw_device *cdev);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 12016e32e519..f98ea674c3d8 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -476,6 +476,17 @@ static void create_fake_irb(struct irb *irb, int type)
476 } 476 }
477} 477}
478 478
479static void ccw_device_handle_broken_paths(struct ccw_device *cdev)
480{
481 struct subchannel *sch = to_subchannel(cdev->dev.parent);
482 u8 broken_paths = (sch->schib.pmcw.pam & sch->opm) ^ sch->vpm;
483
484 if (broken_paths && (cdev->private->path_broken_mask != broken_paths))
485 ccw_device_schedule_recovery();
486
487 cdev->private->path_broken_mask = broken_paths;
488}
489
479void ccw_device_verify_done(struct ccw_device *cdev, int err) 490void ccw_device_verify_done(struct ccw_device *cdev, int err)
480{ 491{
481 struct subchannel *sch; 492 struct subchannel *sch;
@@ -508,6 +519,7 @@ callback:
508 memset(&cdev->private->irb, 0, sizeof(struct irb)); 519 memset(&cdev->private->irb, 0, sizeof(struct irb));
509 } 520 }
510 ccw_device_report_path_events(cdev); 521 ccw_device_report_path_events(cdev);
522 ccw_device_handle_broken_paths(cdev);
511 break; 523 break;
512 case -ETIME: 524 case -ETIME:
513 case -EUSERS: 525 case -EUSERS:
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index 220f49145b2f..9a1b56b2df3e 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -131,6 +131,8 @@ struct ccw_device_private {
131 not operable */ 131 not operable */
132 u8 path_gone_mask; /* mask of paths, that became unavailable */ 132 u8 path_gone_mask; /* mask of paths, that became unavailable */
133 u8 path_new_mask; /* mask of paths, that became available */ 133 u8 path_new_mask; /* mask of paths, that became available */
134 u8 path_broken_mask; /* mask of paths, which were found to be
135 unusable */
134 struct { 136 struct {
135 unsigned int fast:1; /* post with "channel end" */ 137 unsigned int fast:1; /* post with "channel end" */
136 unsigned int repall:1; /* report every interrupt status */ 138 unsigned int repall:1; /* report every interrupt status */