diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-18 11:19:13 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-18 11:19:13 -0400 |
| commit | 991657a39dff76c81e7dbcd0b1f8e56a0ff36aa7 (patch) | |
| tree | 3bfc8c7fac0a8fd607e6a95224207c36f3421854 | |
| parent | 1c6ba37b3de535f29ec4353a1f5290f256629775 (diff) | |
| parent | 52319b457cd78aa891f5947cf2237dd5f6a4c52d (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, the most hairy on is the flush_tlb_kernel_range
fix. Another case of "how could this ever have worked?"."
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
s390/kdump: Do not add standby memory for kdump
drivers/i2c: remove !S390 dependency, add missing GENERIC_HARDIRQS dependencies
s390/scm: process availability
s390/scm_blk: suspend writes
s390/scm_drv: extend notify callback
s390/scm_blk: fix request number accounting
s390/mm: fix flush_tlb_kernel_range()
s390/mm: fix vmemmap size calculation
s390: critical section cleanup vs. machine checks
| -rw-r--r-- | arch/s390/include/asm/eadm.h | 6 | ||||
| -rw-r--r-- | arch/s390/include/asm/tlbflush.h | 2 | ||||
| -rw-r--r-- | arch/s390/kernel/entry.S | 3 | ||||
| -rw-r--r-- | arch/s390/kernel/entry64.S | 5 | ||||
| -rw-r--r-- | arch/s390/kernel/setup.c | 2 | ||||
| -rw-r--r-- | drivers/i2c/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/i2c/busses/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/s390/block/scm_blk.c | 69 | ||||
| -rw-r--r-- | drivers/s390/block/scm_blk.h | 2 | ||||
| -rw-r--r-- | drivers/s390/block/scm_drv.c | 23 | ||||
| -rw-r--r-- | drivers/s390/char/sclp_cmd.c | 2 | ||||
| -rw-r--r-- | drivers/s390/cio/chsc.c | 17 | ||||
| -rw-r--r-- | drivers/s390/cio/chsc.h | 2 | ||||
| -rw-r--r-- | drivers/s390/cio/scm.c | 18 |
14 files changed, 136 insertions, 23 deletions
diff --git a/arch/s390/include/asm/eadm.h b/arch/s390/include/asm/eadm.h index 8d4847191ecc..dc9200ca32ed 100644 --- a/arch/s390/include/asm/eadm.h +++ b/arch/s390/include/asm/eadm.h | |||
| @@ -34,6 +34,8 @@ struct arsb { | |||
| 34 | u32 reserved[4]; | 34 | u32 reserved[4]; |
| 35 | } __packed; | 35 | } __packed; |
| 36 | 36 | ||
| 37 | #define EQC_WR_PROHIBIT 22 | ||
| 38 | |||
| 37 | struct msb { | 39 | struct msb { |
| 38 | u8 fmt:4; | 40 | u8 fmt:4; |
| 39 | u8 oc:4; | 41 | u8 oc:4; |
| @@ -96,11 +98,13 @@ struct scm_device { | |||
| 96 | #define OP_STATE_TEMP_ERR 2 | 98 | #define OP_STATE_TEMP_ERR 2 |
| 97 | #define OP_STATE_PERM_ERR 3 | 99 | #define OP_STATE_PERM_ERR 3 |
| 98 | 100 | ||
| 101 | enum scm_event {SCM_CHANGE, SCM_AVAIL}; | ||
| 102 | |||
| 99 | struct scm_driver { | 103 | struct scm_driver { |
| 100 | struct device_driver drv; | 104 | struct device_driver drv; |
| 101 | int (*probe) (struct scm_device *scmdev); | 105 | int (*probe) (struct scm_device *scmdev); |
| 102 | int (*remove) (struct scm_device *scmdev); | 106 | int (*remove) (struct scm_device *scmdev); |
| 103 | void (*notify) (struct scm_device *scmdev); | 107 | void (*notify) (struct scm_device *scmdev, enum scm_event event); |
| 104 | void (*handler) (struct scm_device *scmdev, void *data, int error); | 108 | void (*handler) (struct scm_device *scmdev, void *data, int error); |
| 105 | }; | 109 | }; |
| 106 | 110 | ||
diff --git a/arch/s390/include/asm/tlbflush.h b/arch/s390/include/asm/tlbflush.h index 1d8fe2b17ef6..6b32af30878c 100644 --- a/arch/s390/include/asm/tlbflush.h +++ b/arch/s390/include/asm/tlbflush.h | |||
| @@ -74,8 +74,6 @@ static inline void __tlb_flush_idte(unsigned long asce) | |||
| 74 | 74 | ||
| 75 | static inline void __tlb_flush_mm(struct mm_struct * mm) | 75 | static inline void __tlb_flush_mm(struct mm_struct * mm) |
| 76 | { | 76 | { |
| 77 | if (unlikely(cpumask_empty(mm_cpumask(mm)))) | ||
| 78 | return; | ||
| 79 | /* | 77 | /* |
| 80 | * If the machine has IDTE we prefer to do a per mm flush | 78 | * If the machine has IDTE we prefer to do a per mm flush |
| 81 | * on all cpus instead of doing a local flush if the mm | 79 | * on all cpus instead of doing a local flush if the mm |
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 550228523267..94feff7d6132 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S | |||
| @@ -636,7 +636,8 @@ ENTRY(mcck_int_handler) | |||
| 636 | UPDATE_VTIME %r14,%r15,__LC_MCCK_ENTER_TIMER | 636 | UPDATE_VTIME %r14,%r15,__LC_MCCK_ENTER_TIMER |
| 637 | mcck_skip: | 637 | mcck_skip: |
| 638 | SWITCH_ASYNC __LC_GPREGS_SAVE_AREA+32,__LC_PANIC_STACK,PAGE_SHIFT | 638 | SWITCH_ASYNC __LC_GPREGS_SAVE_AREA+32,__LC_PANIC_STACK,PAGE_SHIFT |
| 639 | mvc __PT_R0(64,%r11),__LC_GPREGS_SAVE_AREA | 639 | stm %r0,%r7,__PT_R0(%r11) |
| 640 | mvc __PT_R8(32,%r11),__LC_GPREGS_SAVE_AREA+32 | ||
| 640 | stm %r8,%r9,__PT_PSW(%r11) | 641 | stm %r8,%r9,__PT_PSW(%r11) |
| 641 | xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) | 642 | xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) |
| 642 | l %r1,BASED(.Ldo_machine_check) | 643 | l %r1,BASED(.Ldo_machine_check) |
diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index 9c837c101297..2e6d60c55f90 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S | |||
| @@ -678,8 +678,9 @@ ENTRY(mcck_int_handler) | |||
| 678 | UPDATE_VTIME %r14,__LC_MCCK_ENTER_TIMER | 678 | UPDATE_VTIME %r14,__LC_MCCK_ENTER_TIMER |
| 679 | LAST_BREAK %r14 | 679 | LAST_BREAK %r14 |
| 680 | mcck_skip: | 680 | mcck_skip: |
| 681 | lghi %r14,__LC_GPREGS_SAVE_AREA | 681 | lghi %r14,__LC_GPREGS_SAVE_AREA+64 |
| 682 | mvc __PT_R0(128,%r11),0(%r14) | 682 | stmg %r0,%r7,__PT_R0(%r11) |
| 683 | mvc __PT_R8(64,%r11),0(%r14) | ||
| 683 | stmg %r8,%r9,__PT_PSW(%r11) | 684 | stmg %r8,%r9,__PT_PSW(%r11) |
| 684 | xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) | 685 | xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) |
| 685 | lgr %r2,%r11 # pass pointer to pt_regs | 686 | lgr %r2,%r11 # pass pointer to pt_regs |
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index a5360de85ec7..29268859d8ee 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c | |||
| @@ -571,6 +571,8 @@ static void __init setup_memory_end(void) | |||
| 571 | 571 | ||
| 572 | /* Split remaining virtual space between 1:1 mapping & vmemmap array */ | 572 | /* Split remaining virtual space between 1:1 mapping & vmemmap array */ |
| 573 | tmp = VMALLOC_START / (PAGE_SIZE + sizeof(struct page)); | 573 | tmp = VMALLOC_START / (PAGE_SIZE + sizeof(struct page)); |
| 574 | /* vmemmap contains a multiple of PAGES_PER_SECTION struct pages */ | ||
| 575 | tmp = SECTION_ALIGN_UP(tmp); | ||
| 574 | tmp = VMALLOC_START - tmp * sizeof(struct page); | 576 | tmp = VMALLOC_START - tmp * sizeof(struct page); |
| 575 | tmp &= ~((vmax >> 11) - 1); /* align to page table level */ | 577 | tmp &= ~((vmax >> 11) - 1); /* align to page table level */ |
| 576 | tmp = min(tmp, 1UL << MAX_PHYSMEM_BITS); | 578 | tmp = min(tmp, 1UL << MAX_PHYSMEM_BITS); |
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 46cde098c11c..e380c6eef3af 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | menuconfig I2C | 5 | menuconfig I2C |
| 6 | tristate "I2C support" | 6 | tristate "I2C support" |
| 7 | depends on !S390 | ||
| 8 | select RT_MUTEXES | 7 | select RT_MUTEXES |
| 9 | ---help--- | 8 | ---help--- |
| 10 | I2C (pronounce: I-squared-C) is a slow serial bus protocol used in | 9 | I2C (pronounce: I-squared-C) is a slow serial bus protocol used in |
| @@ -76,6 +75,7 @@ config I2C_HELPER_AUTO | |||
| 76 | 75 | ||
| 77 | config I2C_SMBUS | 76 | config I2C_SMBUS |
| 78 | tristate "SMBus-specific protocols" if !I2C_HELPER_AUTO | 77 | tristate "SMBus-specific protocols" if !I2C_HELPER_AUTO |
| 78 | depends on GENERIC_HARDIRQS | ||
| 79 | help | 79 | help |
| 80 | Say Y here if you want support for SMBus extensions to the I2C | 80 | Say Y here if you want support for SMBus extensions to the I2C |
| 81 | specification. At the moment, the only supported extension is | 81 | specification. At the moment, the only supported extension is |
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index a3725de92384..adfee98486b1 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig | |||
| @@ -114,7 +114,7 @@ config I2C_I801 | |||
| 114 | 114 | ||
| 115 | config I2C_ISCH | 115 | config I2C_ISCH |
| 116 | tristate "Intel SCH SMBus 1.0" | 116 | tristate "Intel SCH SMBus 1.0" |
| 117 | depends on PCI | 117 | depends on PCI && GENERIC_HARDIRQS |
| 118 | select LPC_SCH | 118 | select LPC_SCH |
| 119 | help | 119 | help |
| 120 | Say Y here if you want to use SMBus controller on the Intel SCH | 120 | Say Y here if you want to use SMBus controller on the Intel SCH |
| @@ -543,6 +543,7 @@ config I2C_NUC900 | |||
| 543 | 543 | ||
| 544 | config I2C_OCORES | 544 | config I2C_OCORES |
| 545 | tristate "OpenCores I2C Controller" | 545 | tristate "OpenCores I2C Controller" |
| 546 | depends on GENERIC_HARDIRQS | ||
| 546 | help | 547 | help |
| 547 | If you say yes to this option, support will be included for the | 548 | If you say yes to this option, support will be included for the |
| 548 | OpenCores I2C controller. For details see | 549 | OpenCores I2C controller. For details see |
| @@ -777,7 +778,7 @@ config I2C_DIOLAN_U2C | |||
| 777 | 778 | ||
| 778 | config I2C_PARPORT | 779 | config I2C_PARPORT |
| 779 | tristate "Parallel port adapter" | 780 | tristate "Parallel port adapter" |
| 780 | depends on PARPORT | 781 | depends on PARPORT && GENERIC_HARDIRQS |
| 781 | select I2C_ALGOBIT | 782 | select I2C_ALGOBIT |
| 782 | select I2C_SMBUS | 783 | select I2C_SMBUS |
| 783 | help | 784 | help |
| @@ -802,6 +803,7 @@ config I2C_PARPORT | |||
| 802 | 803 | ||
| 803 | config I2C_PARPORT_LIGHT | 804 | config I2C_PARPORT_LIGHT |
| 804 | tristate "Parallel port adapter (light)" | 805 | tristate "Parallel port adapter (light)" |
| 806 | depends on GENERIC_HARDIRQS | ||
| 805 | select I2C_ALGOBIT | 807 | select I2C_ALGOBIT |
| 806 | select I2C_SMBUS | 808 | select I2C_SMBUS |
| 807 | help | 809 | help |
diff --git a/drivers/s390/block/scm_blk.c b/drivers/s390/block/scm_blk.c index 9978ad4433cb..5ac9c935c151 100644 --- a/drivers/s390/block/scm_blk.c +++ b/drivers/s390/block/scm_blk.c | |||
| @@ -135,6 +135,11 @@ static const struct block_device_operations scm_blk_devops = { | |||
| 135 | .release = scm_release, | 135 | .release = scm_release, |
| 136 | }; | 136 | }; |
| 137 | 137 | ||
| 138 | static bool scm_permit_request(struct scm_blk_dev *bdev, struct request *req) | ||
| 139 | { | ||
| 140 | return rq_data_dir(req) != WRITE || bdev->state != SCM_WR_PROHIBIT; | ||
| 141 | } | ||
| 142 | |||
| 138 | static void scm_request_prepare(struct scm_request *scmrq) | 143 | static void scm_request_prepare(struct scm_request *scmrq) |
| 139 | { | 144 | { |
| 140 | struct scm_blk_dev *bdev = scmrq->bdev; | 145 | struct scm_blk_dev *bdev = scmrq->bdev; |
| @@ -195,14 +200,18 @@ void scm_request_requeue(struct scm_request *scmrq) | |||
| 195 | 200 | ||
| 196 | scm_release_cluster(scmrq); | 201 | scm_release_cluster(scmrq); |
| 197 | blk_requeue_request(bdev->rq, scmrq->request); | 202 | blk_requeue_request(bdev->rq, scmrq->request); |
| 203 | atomic_dec(&bdev->queued_reqs); | ||
| 198 | scm_request_done(scmrq); | 204 | scm_request_done(scmrq); |
| 199 | scm_ensure_queue_restart(bdev); | 205 | scm_ensure_queue_restart(bdev); |
| 200 | } | 206 | } |
| 201 | 207 | ||
| 202 | void scm_request_finish(struct scm_request *scmrq) | 208 | void scm_request_finish(struct scm_request *scmrq) |
| 203 | { | 209 | { |
| 210 | struct scm_blk_dev *bdev = scmrq->bdev; | ||
| 211 | |||
| 204 | scm_release_cluster(scmrq); | 212 | scm_release_cluster(scmrq); |
| 205 | blk_end_request_all(scmrq->request, scmrq->error); | 213 | blk_end_request_all(scmrq->request, scmrq->error); |
| 214 | atomic_dec(&bdev->queued_reqs); | ||
| 206 | scm_request_done(scmrq); | 215 | scm_request_done(scmrq); |
| 207 | } | 216 | } |
| 208 | 217 | ||
| @@ -218,6 +227,10 @@ static void scm_blk_request(struct request_queue *rq) | |||
| 218 | if (req->cmd_type != REQ_TYPE_FS) | 227 | if (req->cmd_type != REQ_TYPE_FS) |
| 219 | continue; | 228 | continue; |
| 220 | 229 | ||
| 230 | if (!scm_permit_request(bdev, req)) { | ||
| 231 | scm_ensure_queue_restart(bdev); | ||
| 232 | return; | ||
| 233 | } | ||
| 221 | scmrq = scm_request_fetch(); | 234 | scmrq = scm_request_fetch(); |
| 222 | if (!scmrq) { | 235 | if (!scmrq) { |
| 223 | SCM_LOG(5, "no request"); | 236 | SCM_LOG(5, "no request"); |
| @@ -231,11 +244,13 @@ static void scm_blk_request(struct request_queue *rq) | |||
| 231 | return; | 244 | return; |
| 232 | } | 245 | } |
| 233 | if (scm_need_cluster_request(scmrq)) { | 246 | if (scm_need_cluster_request(scmrq)) { |
| 247 | atomic_inc(&bdev->queued_reqs); | ||
| 234 | blk_start_request(req); | 248 | blk_start_request(req); |
| 235 | scm_initiate_cluster_request(scmrq); | 249 | scm_initiate_cluster_request(scmrq); |
| 236 | return; | 250 | return; |
| 237 | } | 251 | } |
| 238 | scm_request_prepare(scmrq); | 252 | scm_request_prepare(scmrq); |
| 253 | atomic_inc(&bdev->queued_reqs); | ||
| 239 | blk_start_request(req); | 254 | blk_start_request(req); |
| 240 | 255 | ||
| 241 | ret = scm_start_aob(scmrq->aob); | 256 | ret = scm_start_aob(scmrq->aob); |
| @@ -244,7 +259,6 @@ static void scm_blk_request(struct request_queue *rq) | |||
| 244 | scm_request_requeue(scmrq); | 259 | scm_request_requeue(scmrq); |
| 245 | return; | 260 | return; |
| 246 | } | 261 | } |
| 247 | atomic_inc(&bdev->queued_reqs); | ||
| 248 | } | 262 | } |
| 249 | } | 263 | } |
| 250 | 264 | ||
| @@ -280,6 +294,38 @@ void scm_blk_irq(struct scm_device *scmdev, void *data, int error) | |||
| 280 | tasklet_hi_schedule(&bdev->tasklet); | 294 | tasklet_hi_schedule(&bdev->tasklet); |
| 281 | } | 295 | } |
| 282 | 296 | ||
| 297 | static void scm_blk_handle_error(struct scm_request *scmrq) | ||
| 298 | { | ||
| 299 | struct scm_blk_dev *bdev = scmrq->bdev; | ||
| 300 | unsigned long flags; | ||
| 301 | |||
| 302 | if (scmrq->error != -EIO) | ||
| 303 | goto restart; | ||
| 304 | |||
| 305 | /* For -EIO the response block is valid. */ | ||
| 306 | switch (scmrq->aob->response.eqc) { | ||
| 307 | case EQC_WR_PROHIBIT: | ||
| 308 | spin_lock_irqsave(&bdev->lock, flags); | ||
| 309 | if (bdev->state != SCM_WR_PROHIBIT) | ||
| 310 | pr_info("%lu: Write access to the SCM increment is suspended\n", | ||
| 311 | (unsigned long) bdev->scmdev->address); | ||
| 312 | bdev->state = SCM_WR_PROHIBIT; | ||
| 313 | spin_unlock_irqrestore(&bdev->lock, flags); | ||
| 314 | goto requeue; | ||
| 315 | default: | ||
| 316 | break; | ||
| 317 | } | ||
| 318 | |||
| 319 | restart: | ||
| 320 | if (!scm_start_aob(scmrq->aob)) | ||
| 321 | return; | ||
| 322 | |||
| 323 | requeue: | ||
| 324 | spin_lock_irqsave(&bdev->rq_lock, flags); | ||
| 325 | scm_request_requeue(scmrq); | ||
| 326 | spin_unlock_irqrestore(&bdev->rq_lock, flags); | ||
| 327 | } | ||
| 328 | |||
| 283 | static void scm_blk_tasklet(struct scm_blk_dev *bdev) | 329 | static void scm_blk_tasklet(struct scm_blk_dev *bdev) |
| 284 | { | 330 | { |
| 285 | struct scm_request *scmrq; | 331 | struct scm_request *scmrq; |
| @@ -293,11 +339,8 @@ static void scm_blk_tasklet(struct scm_blk_dev *bdev) | |||
| 293 | spin_unlock_irqrestore(&bdev->lock, flags); | 339 | spin_unlock_irqrestore(&bdev->lock, flags); |
| 294 | 340 | ||
| 295 | if (scmrq->error && scmrq->retries-- > 0) { | 341 | if (scmrq->error && scmrq->retries-- > 0) { |
| 296 | if (scm_start_aob(scmrq->aob)) { | 342 | scm_blk_handle_error(scmrq); |
| 297 | spin_lock_irqsave(&bdev->rq_lock, flags); | 343 | |
| 298 | scm_request_requeue(scmrq); | ||
| 299 | spin_unlock_irqrestore(&bdev->rq_lock, flags); | ||
| 300 | } | ||
| 301 | /* Request restarted or requeued, handle next. */ | 344 | /* Request restarted or requeued, handle next. */ |
| 302 | spin_lock_irqsave(&bdev->lock, flags); | 345 | spin_lock_irqsave(&bdev->lock, flags); |
| 303 | continue; | 346 | continue; |
| @@ -310,7 +353,6 @@ static void scm_blk_tasklet(struct scm_blk_dev *bdev) | |||
| 310 | } | 353 | } |
| 311 | 354 | ||
| 312 | scm_request_finish(scmrq); | 355 | scm_request_finish(scmrq); |
| 313 | atomic_dec(&bdev->queued_reqs); | ||
| 314 | spin_lock_irqsave(&bdev->lock, flags); | 356 | spin_lock_irqsave(&bdev->lock, flags); |
| 315 | } | 357 | } |
| 316 | spin_unlock_irqrestore(&bdev->lock, flags); | 358 | spin_unlock_irqrestore(&bdev->lock, flags); |
| @@ -332,6 +374,7 @@ int scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev) | |||
| 332 | } | 374 | } |
| 333 | 375 | ||
| 334 | bdev->scmdev = scmdev; | 376 | bdev->scmdev = scmdev; |
| 377 | bdev->state = SCM_OPER; | ||
| 335 | spin_lock_init(&bdev->rq_lock); | 378 | spin_lock_init(&bdev->rq_lock); |
| 336 | spin_lock_init(&bdev->lock); | 379 | spin_lock_init(&bdev->lock); |
| 337 | INIT_LIST_HEAD(&bdev->finished_requests); | 380 | INIT_LIST_HEAD(&bdev->finished_requests); |
| @@ -396,6 +439,18 @@ void scm_blk_dev_cleanup(struct scm_blk_dev *bdev) | |||
| 396 | put_disk(bdev->gendisk); | 439 | put_disk(bdev->gendisk); |
| 397 | } | 440 | } |
| 398 | 441 | ||
| 442 | void scm_blk_set_available(struct scm_blk_dev *bdev) | ||
| 443 | { | ||
| 444 | unsigned long flags; | ||
| 445 | |||
| 446 | spin_lock_irqsave(&bdev->lock, flags); | ||
| 447 | if (bdev->state == SCM_WR_PROHIBIT) | ||
| 448 | pr_info("%lu: Write access to the SCM increment is restored\n", | ||
| 449 | (unsigned long) bdev->scmdev->address); | ||
| 450 | bdev->state = SCM_OPER; | ||
| 451 | spin_unlock_irqrestore(&bdev->lock, flags); | ||
| 452 | } | ||
| 453 | |||
| 399 | static int __init scm_blk_init(void) | 454 | static int __init scm_blk_init(void) |
| 400 | { | 455 | { |
| 401 | int ret = -EINVAL; | 456 | int ret = -EINVAL; |
diff --git a/drivers/s390/block/scm_blk.h b/drivers/s390/block/scm_blk.h index 3c1ccf494647..8b387b32fd62 100644 --- a/drivers/s390/block/scm_blk.h +++ b/drivers/s390/block/scm_blk.h | |||
| @@ -21,6 +21,7 @@ struct scm_blk_dev { | |||
| 21 | spinlock_t rq_lock; /* guard the request queue */ | 21 | spinlock_t rq_lock; /* guard the request queue */ |
| 22 | spinlock_t lock; /* guard the rest of the blockdev */ | 22 | spinlock_t lock; /* guard the rest of the blockdev */ |
| 23 | atomic_t queued_reqs; | 23 | atomic_t queued_reqs; |
| 24 | enum {SCM_OPER, SCM_WR_PROHIBIT} state; | ||
| 24 | struct list_head finished_requests; | 25 | struct list_head finished_requests; |
| 25 | #ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE | 26 | #ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE |
| 26 | struct list_head cluster_list; | 27 | struct list_head cluster_list; |
| @@ -48,6 +49,7 @@ struct scm_request { | |||
| 48 | 49 | ||
| 49 | int scm_blk_dev_setup(struct scm_blk_dev *, struct scm_device *); | 50 | int scm_blk_dev_setup(struct scm_blk_dev *, struct scm_device *); |
| 50 | void scm_blk_dev_cleanup(struct scm_blk_dev *); | 51 | void scm_blk_dev_cleanup(struct scm_blk_dev *); |
| 52 | void scm_blk_set_available(struct scm_blk_dev *); | ||
| 51 | void scm_blk_irq(struct scm_device *, void *, int); | 53 | void scm_blk_irq(struct scm_device *, void *, int); |
| 52 | 54 | ||
| 53 | void scm_request_finish(struct scm_request *); | 55 | void scm_request_finish(struct scm_request *); |
diff --git a/drivers/s390/block/scm_drv.c b/drivers/s390/block/scm_drv.c index 9fa0a908607b..5f6180d6ff08 100644 --- a/drivers/s390/block/scm_drv.c +++ b/drivers/s390/block/scm_drv.c | |||
| @@ -13,12 +13,23 @@ | |||
| 13 | #include <asm/eadm.h> | 13 | #include <asm/eadm.h> |
| 14 | #include "scm_blk.h" | 14 | #include "scm_blk.h" |
| 15 | 15 | ||
| 16 | static void notify(struct scm_device *scmdev) | 16 | static void scm_notify(struct scm_device *scmdev, enum scm_event event) |
| 17 | { | 17 | { |
| 18 | pr_info("%lu: The capabilities of the SCM increment changed\n", | 18 | struct scm_blk_dev *bdev = dev_get_drvdata(&scmdev->dev); |
| 19 | (unsigned long) scmdev->address); | 19 | |
| 20 | SCM_LOG(2, "State changed"); | 20 | switch (event) { |
| 21 | SCM_LOG_STATE(2, scmdev); | 21 | case SCM_CHANGE: |
| 22 | pr_info("%lu: The capabilities of the SCM increment changed\n", | ||
| 23 | (unsigned long) scmdev->address); | ||
| 24 | SCM_LOG(2, "State changed"); | ||
| 25 | SCM_LOG_STATE(2, scmdev); | ||
| 26 | break; | ||
| 27 | case SCM_AVAIL: | ||
| 28 | SCM_LOG(2, "Increment available"); | ||
| 29 | SCM_LOG_STATE(2, scmdev); | ||
| 30 | scm_blk_set_available(bdev); | ||
| 31 | break; | ||
| 32 | } | ||
| 22 | } | 33 | } |
| 23 | 34 | ||
| 24 | static int scm_probe(struct scm_device *scmdev) | 35 | static int scm_probe(struct scm_device *scmdev) |
| @@ -64,7 +75,7 @@ static struct scm_driver scm_drv = { | |||
| 64 | .name = "scm_block", | 75 | .name = "scm_block", |
| 65 | .owner = THIS_MODULE, | 76 | .owner = THIS_MODULE, |
| 66 | }, | 77 | }, |
| 67 | .notify = notify, | 78 | .notify = scm_notify, |
| 68 | .probe = scm_probe, | 79 | .probe = scm_probe, |
| 69 | .remove = scm_remove, | 80 | .remove = scm_remove, |
| 70 | .handler = scm_blk_irq, | 81 | .handler = scm_blk_irq, |
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 30a2255389e5..cd798386b622 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c | |||
| @@ -627,6 +627,8 @@ static int __init sclp_detect_standby_memory(void) | |||
| 627 | struct read_storage_sccb *sccb; | 627 | struct read_storage_sccb *sccb; |
| 628 | int i, id, assigned, rc; | 628 | int i, id, assigned, rc; |
| 629 | 629 | ||
| 630 | if (OLDMEM_BASE) /* No standby memory in kdump mode */ | ||
| 631 | return 0; | ||
| 630 | if (!early_read_info_sccb_valid) | 632 | if (!early_read_info_sccb_valid) |
| 631 | return 0; | 633 | return 0; |
| 632 | if ((sclp_facilities & 0xe00000000000ULL) != 0xe00000000000ULL) | 634 | if ((sclp_facilities & 0xe00000000000ULL) != 0xe00000000000ULL) |
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 31ceef1beb8b..e16c553f6556 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
| @@ -433,6 +433,20 @@ static void chsc_process_sei_scm_change(struct chsc_sei_nt0_area *sei_area) | |||
| 433 | " failed (rc=%d).\n", ret); | 433 | " failed (rc=%d).\n", ret); |
| 434 | } | 434 | } |
| 435 | 435 | ||
| 436 | static void chsc_process_sei_scm_avail(struct chsc_sei_nt0_area *sei_area) | ||
| 437 | { | ||
| 438 | int ret; | ||
| 439 | |||
| 440 | CIO_CRW_EVENT(4, "chsc: scm available information\n"); | ||
| 441 | if (sei_area->rs != 7) | ||
| 442 | return; | ||
| 443 | |||
| 444 | ret = scm_process_availability_information(); | ||
| 445 | if (ret) | ||
| 446 | CIO_CRW_EVENT(0, "chsc: process availability information" | ||
| 447 | " failed (rc=%d).\n", ret); | ||
| 448 | } | ||
| 449 | |||
| 436 | static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area) | 450 | static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area) |
| 437 | { | 451 | { |
| 438 | switch (sei_area->cc) { | 452 | switch (sei_area->cc) { |
| @@ -468,6 +482,9 @@ static void chsc_process_sei_nt0(struct chsc_sei_nt0_area *sei_area) | |||
| 468 | case 12: /* scm change notification */ | 482 | case 12: /* scm change notification */ |
| 469 | chsc_process_sei_scm_change(sei_area); | 483 | chsc_process_sei_scm_change(sei_area); |
| 470 | break; | 484 | break; |
| 485 | case 14: /* scm available notification */ | ||
| 486 | chsc_process_sei_scm_avail(sei_area); | ||
| 487 | break; | ||
| 471 | default: /* other stuff */ | 488 | default: /* other stuff */ |
| 472 | CIO_CRW_EVENT(2, "chsc: sei nt0 unhandled cc=%d\n", | 489 | CIO_CRW_EVENT(2, "chsc: sei nt0 unhandled cc=%d\n", |
| 473 | sei_area->cc); | 490 | sei_area->cc); |
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 227e05f674b3..349d5fc47196 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h | |||
| @@ -156,8 +156,10 @@ int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token); | |||
| 156 | 156 | ||
| 157 | #ifdef CONFIG_SCM_BUS | 157 | #ifdef CONFIG_SCM_BUS |
| 158 | int scm_update_information(void); | 158 | int scm_update_information(void); |
| 159 | int scm_process_availability_information(void); | ||
| 159 | #else /* CONFIG_SCM_BUS */ | 160 | #else /* CONFIG_SCM_BUS */ |
| 160 | static inline int scm_update_information(void) { return 0; } | 161 | static inline int scm_update_information(void) { return 0; } |
| 162 | static inline int scm_process_availability_information(void) { return 0; } | ||
| 161 | #endif /* CONFIG_SCM_BUS */ | 163 | #endif /* CONFIG_SCM_BUS */ |
| 162 | 164 | ||
| 163 | 165 | ||
diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c index bcf20f3aa51b..46ec25632e8b 100644 --- a/drivers/s390/cio/scm.c +++ b/drivers/s390/cio/scm.c | |||
| @@ -211,7 +211,7 @@ static void scmdev_update(struct scm_device *scmdev, struct sale *sale) | |||
| 211 | goto out; | 211 | goto out; |
| 212 | scmdrv = to_scm_drv(scmdev->dev.driver); | 212 | scmdrv = to_scm_drv(scmdev->dev.driver); |
| 213 | if (changed && scmdrv->notify) | 213 | if (changed && scmdrv->notify) |
| 214 | scmdrv->notify(scmdev); | 214 | scmdrv->notify(scmdev, SCM_CHANGE); |
| 215 | out: | 215 | out: |
| 216 | device_unlock(&scmdev->dev); | 216 | device_unlock(&scmdev->dev); |
| 217 | if (changed) | 217 | if (changed) |
| @@ -297,6 +297,22 @@ int scm_update_information(void) | |||
| 297 | return ret; | 297 | return ret; |
| 298 | } | 298 | } |
| 299 | 299 | ||
| 300 | static int scm_dev_avail(struct device *dev, void *unused) | ||
| 301 | { | ||
| 302 | struct scm_driver *scmdrv = to_scm_drv(dev->driver); | ||
| 303 | struct scm_device *scmdev = to_scm_dev(dev); | ||
| 304 | |||
| 305 | if (dev->driver && scmdrv->notify) | ||
| 306 | scmdrv->notify(scmdev, SCM_AVAIL); | ||
| 307 | |||
| 308 | return 0; | ||
| 309 | } | ||
| 310 | |||
| 311 | int scm_process_availability_information(void) | ||
| 312 | { | ||
| 313 | return bus_for_each_dev(&scm_bus_type, NULL, NULL, scm_dev_avail); | ||
| 314 | } | ||
| 315 | |||
| 300 | static int __init scm_init(void) | 316 | static int __init scm_init(void) |
| 301 | { | 317 | { |
| 302 | int ret; | 318 | int ret; |
