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
This commit is contained in:
		
				commit
				
					
						991657a39d
					
				
			
		
					 14 changed files with 136 additions and 23 deletions
				
			
		| 
						 | 
					@ -34,6 +34,8 @@ struct arsb {
 | 
				
			||||||
	u32 reserved[4];
 | 
						u32 reserved[4];
 | 
				
			||||||
} __packed;
 | 
					} __packed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define EQC_WR_PROHIBIT 22
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct msb {
 | 
					struct msb {
 | 
				
			||||||
	u8 fmt:4;
 | 
						u8 fmt:4;
 | 
				
			||||||
	u8 oc:4;
 | 
						u8 oc:4;
 | 
				
			||||||
| 
						 | 
					@ -96,11 +98,13 @@ struct scm_device {
 | 
				
			||||||
#define OP_STATE_TEMP_ERR	2
 | 
					#define OP_STATE_TEMP_ERR	2
 | 
				
			||||||
#define OP_STATE_PERM_ERR	3
 | 
					#define OP_STATE_PERM_ERR	3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum scm_event {SCM_CHANGE, SCM_AVAIL};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct scm_driver {
 | 
					struct scm_driver {
 | 
				
			||||||
	struct device_driver drv;
 | 
						struct device_driver drv;
 | 
				
			||||||
	int (*probe) (struct scm_device *scmdev);
 | 
						int (*probe) (struct scm_device *scmdev);
 | 
				
			||||||
	int (*remove) (struct scm_device *scmdev);
 | 
						int (*remove) (struct scm_device *scmdev);
 | 
				
			||||||
	void (*notify) (struct scm_device *scmdev);
 | 
						void (*notify) (struct scm_device *scmdev, enum scm_event event);
 | 
				
			||||||
	void (*handler) (struct scm_device *scmdev, void *data, int error);
 | 
						void (*handler) (struct scm_device *scmdev, void *data, int error);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -74,8 +74,6 @@ static inline void __tlb_flush_idte(unsigned long asce)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void __tlb_flush_mm(struct mm_struct * mm)
 | 
					static inline void __tlb_flush_mm(struct mm_struct * mm)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (unlikely(cpumask_empty(mm_cpumask(mm))))
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * If the machine has IDTE we prefer to do a per mm flush
 | 
						 * If the machine has IDTE we prefer to do a per mm flush
 | 
				
			||||||
	 * on all cpus instead of doing a local flush if the mm
 | 
						 * on all cpus instead of doing a local flush if the mm
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -636,7 +636,8 @@ ENTRY(mcck_int_handler)
 | 
				
			||||||
	UPDATE_VTIME %r14,%r15,__LC_MCCK_ENTER_TIMER
 | 
						UPDATE_VTIME %r14,%r15,__LC_MCCK_ENTER_TIMER
 | 
				
			||||||
mcck_skip:
 | 
					mcck_skip:
 | 
				
			||||||
	SWITCH_ASYNC __LC_GPREGS_SAVE_AREA+32,__LC_PANIC_STACK,PAGE_SHIFT
 | 
						SWITCH_ASYNC __LC_GPREGS_SAVE_AREA+32,__LC_PANIC_STACK,PAGE_SHIFT
 | 
				
			||||||
	mvc	__PT_R0(64,%r11),__LC_GPREGS_SAVE_AREA
 | 
						stm	%r0,%r7,__PT_R0(%r11)
 | 
				
			||||||
 | 
						mvc	__PT_R8(32,%r11),__LC_GPREGS_SAVE_AREA+32
 | 
				
			||||||
	stm	%r8,%r9,__PT_PSW(%r11)
 | 
						stm	%r8,%r9,__PT_PSW(%r11)
 | 
				
			||||||
	xc	__SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
 | 
						xc	__SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
 | 
				
			||||||
	l	%r1,BASED(.Ldo_machine_check)
 | 
						l	%r1,BASED(.Ldo_machine_check)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -678,8 +678,9 @@ ENTRY(mcck_int_handler)
 | 
				
			||||||
	UPDATE_VTIME %r14,__LC_MCCK_ENTER_TIMER
 | 
						UPDATE_VTIME %r14,__LC_MCCK_ENTER_TIMER
 | 
				
			||||||
	LAST_BREAK %r14
 | 
						LAST_BREAK %r14
 | 
				
			||||||
mcck_skip:
 | 
					mcck_skip:
 | 
				
			||||||
	lghi	%r14,__LC_GPREGS_SAVE_AREA
 | 
						lghi	%r14,__LC_GPREGS_SAVE_AREA+64
 | 
				
			||||||
	mvc	__PT_R0(128,%r11),0(%r14)
 | 
						stmg	%r0,%r7,__PT_R0(%r11)
 | 
				
			||||||
 | 
						mvc	__PT_R8(64,%r11),0(%r14)
 | 
				
			||||||
	stmg	%r8,%r9,__PT_PSW(%r11)
 | 
						stmg	%r8,%r9,__PT_PSW(%r11)
 | 
				
			||||||
	xc	__SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
 | 
						xc	__SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
 | 
				
			||||||
	lgr	%r2,%r11		# pass pointer to pt_regs
 | 
						lgr	%r2,%r11		# pass pointer to pt_regs
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -571,6 +571,8 @@ static void __init setup_memory_end(void)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Split remaining virtual space between 1:1 mapping & vmemmap array */
 | 
						/* Split remaining virtual space between 1:1 mapping & vmemmap array */
 | 
				
			||||||
	tmp = VMALLOC_START / (PAGE_SIZE + sizeof(struct page));
 | 
						tmp = VMALLOC_START / (PAGE_SIZE + sizeof(struct page));
 | 
				
			||||||
 | 
						/* vmemmap contains a multiple of PAGES_PER_SECTION struct pages */
 | 
				
			||||||
 | 
						tmp = SECTION_ALIGN_UP(tmp);
 | 
				
			||||||
	tmp = VMALLOC_START - tmp * sizeof(struct page);
 | 
						tmp = VMALLOC_START - tmp * sizeof(struct page);
 | 
				
			||||||
	tmp &= ~((vmax >> 11) - 1);	/* align to page table level */
 | 
						tmp &= ~((vmax >> 11) - 1);	/* align to page table level */
 | 
				
			||||||
	tmp = min(tmp, 1UL << MAX_PHYSMEM_BITS);
 | 
						tmp = min(tmp, 1UL << MAX_PHYSMEM_BITS);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
menuconfig I2C
 | 
					menuconfig I2C
 | 
				
			||||||
	tristate "I2C support"
 | 
						tristate "I2C support"
 | 
				
			||||||
	depends on !S390
 | 
					 | 
				
			||||||
	select RT_MUTEXES
 | 
						select RT_MUTEXES
 | 
				
			||||||
	---help---
 | 
						---help---
 | 
				
			||||||
	  I2C (pronounce: I-squared-C) is a slow serial bus protocol used in
 | 
						  I2C (pronounce: I-squared-C) is a slow serial bus protocol used in
 | 
				
			||||||
| 
						 | 
					@ -76,6 +75,7 @@ config I2C_HELPER_AUTO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config I2C_SMBUS
 | 
					config I2C_SMBUS
 | 
				
			||||||
	tristate "SMBus-specific protocols" if !I2C_HELPER_AUTO
 | 
						tristate "SMBus-specific protocols" if !I2C_HELPER_AUTO
 | 
				
			||||||
 | 
						depends on GENERIC_HARDIRQS
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  Say Y here if you want support for SMBus extensions to the I2C
 | 
						  Say Y here if you want support for SMBus extensions to the I2C
 | 
				
			||||||
	  specification. At the moment, the only supported extension is
 | 
						  specification. At the moment, the only supported extension is
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -114,7 +114,7 @@ config I2C_I801
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config I2C_ISCH
 | 
					config I2C_ISCH
 | 
				
			||||||
	tristate "Intel SCH SMBus 1.0"
 | 
						tristate "Intel SCH SMBus 1.0"
 | 
				
			||||||
	depends on PCI
 | 
						depends on PCI && GENERIC_HARDIRQS
 | 
				
			||||||
	select LPC_SCH
 | 
						select LPC_SCH
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  Say Y here if you want to use SMBus controller on the Intel SCH
 | 
						  Say Y here if you want to use SMBus controller on the Intel SCH
 | 
				
			||||||
| 
						 | 
					@ -543,6 +543,7 @@ config I2C_NUC900
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config I2C_OCORES
 | 
					config I2C_OCORES
 | 
				
			||||||
	tristate "OpenCores I2C Controller"
 | 
						tristate "OpenCores I2C Controller"
 | 
				
			||||||
 | 
						depends on GENERIC_HARDIRQS
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  If you say yes to this option, support will be included for the
 | 
						  If you say yes to this option, support will be included for the
 | 
				
			||||||
	  OpenCores I2C controller. For details see
 | 
						  OpenCores I2C controller. For details see
 | 
				
			||||||
| 
						 | 
					@ -777,7 +778,7 @@ config I2C_DIOLAN_U2C
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config I2C_PARPORT
 | 
					config I2C_PARPORT
 | 
				
			||||||
	tristate "Parallel port adapter"
 | 
						tristate "Parallel port adapter"
 | 
				
			||||||
	depends on PARPORT
 | 
						depends on PARPORT && GENERIC_HARDIRQS
 | 
				
			||||||
	select I2C_ALGOBIT
 | 
						select I2C_ALGOBIT
 | 
				
			||||||
	select I2C_SMBUS
 | 
						select I2C_SMBUS
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
| 
						 | 
					@ -802,6 +803,7 @@ config I2C_PARPORT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config I2C_PARPORT_LIGHT
 | 
					config I2C_PARPORT_LIGHT
 | 
				
			||||||
	tristate "Parallel port adapter (light)"
 | 
						tristate "Parallel port adapter (light)"
 | 
				
			||||||
 | 
						depends on GENERIC_HARDIRQS
 | 
				
			||||||
	select I2C_ALGOBIT
 | 
						select I2C_ALGOBIT
 | 
				
			||||||
	select I2C_SMBUS
 | 
						select I2C_SMBUS
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -135,6 +135,11 @@ static const struct block_device_operations scm_blk_devops = {
 | 
				
			||||||
	.release = scm_release,
 | 
						.release = scm_release,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool scm_permit_request(struct scm_blk_dev *bdev, struct request *req)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return rq_data_dir(req) != WRITE || bdev->state != SCM_WR_PROHIBIT;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void scm_request_prepare(struct scm_request *scmrq)
 | 
					static void scm_request_prepare(struct scm_request *scmrq)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct scm_blk_dev *bdev = scmrq->bdev;
 | 
						struct scm_blk_dev *bdev = scmrq->bdev;
 | 
				
			||||||
| 
						 | 
					@ -195,14 +200,18 @@ void scm_request_requeue(struct scm_request *scmrq)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	scm_release_cluster(scmrq);
 | 
						scm_release_cluster(scmrq);
 | 
				
			||||||
	blk_requeue_request(bdev->rq, scmrq->request);
 | 
						blk_requeue_request(bdev->rq, scmrq->request);
 | 
				
			||||||
 | 
						atomic_dec(&bdev->queued_reqs);
 | 
				
			||||||
	scm_request_done(scmrq);
 | 
						scm_request_done(scmrq);
 | 
				
			||||||
	scm_ensure_queue_restart(bdev);
 | 
						scm_ensure_queue_restart(bdev);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void scm_request_finish(struct scm_request *scmrq)
 | 
					void scm_request_finish(struct scm_request *scmrq)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct scm_blk_dev *bdev = scmrq->bdev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	scm_release_cluster(scmrq);
 | 
						scm_release_cluster(scmrq);
 | 
				
			||||||
	blk_end_request_all(scmrq->request, scmrq->error);
 | 
						blk_end_request_all(scmrq->request, scmrq->error);
 | 
				
			||||||
 | 
						atomic_dec(&bdev->queued_reqs);
 | 
				
			||||||
	scm_request_done(scmrq);
 | 
						scm_request_done(scmrq);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -218,6 +227,10 @@ static void scm_blk_request(struct request_queue *rq)
 | 
				
			||||||
		if (req->cmd_type != REQ_TYPE_FS)
 | 
							if (req->cmd_type != REQ_TYPE_FS)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!scm_permit_request(bdev, req)) {
 | 
				
			||||||
 | 
								scm_ensure_queue_restart(bdev);
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		scmrq = scm_request_fetch();
 | 
							scmrq = scm_request_fetch();
 | 
				
			||||||
		if (!scmrq) {
 | 
							if (!scmrq) {
 | 
				
			||||||
			SCM_LOG(5, "no request");
 | 
								SCM_LOG(5, "no request");
 | 
				
			||||||
| 
						 | 
					@ -231,11 +244,13 @@ static void scm_blk_request(struct request_queue *rq)
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (scm_need_cluster_request(scmrq)) {
 | 
							if (scm_need_cluster_request(scmrq)) {
 | 
				
			||||||
 | 
								atomic_inc(&bdev->queued_reqs);
 | 
				
			||||||
			blk_start_request(req);
 | 
								blk_start_request(req);
 | 
				
			||||||
			scm_initiate_cluster_request(scmrq);
 | 
								scm_initiate_cluster_request(scmrq);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		scm_request_prepare(scmrq);
 | 
							scm_request_prepare(scmrq);
 | 
				
			||||||
 | 
							atomic_inc(&bdev->queued_reqs);
 | 
				
			||||||
		blk_start_request(req);
 | 
							blk_start_request(req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ret = scm_start_aob(scmrq->aob);
 | 
							ret = scm_start_aob(scmrq->aob);
 | 
				
			||||||
| 
						 | 
					@ -244,7 +259,6 @@ static void scm_blk_request(struct request_queue *rq)
 | 
				
			||||||
			scm_request_requeue(scmrq);
 | 
								scm_request_requeue(scmrq);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		atomic_inc(&bdev->queued_reqs);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -280,6 +294,38 @@ void scm_blk_irq(struct scm_device *scmdev, void *data, int error)
 | 
				
			||||||
	tasklet_hi_schedule(&bdev->tasklet);
 | 
						tasklet_hi_schedule(&bdev->tasklet);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void scm_blk_handle_error(struct scm_request *scmrq)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct scm_blk_dev *bdev = scmrq->bdev;
 | 
				
			||||||
 | 
						unsigned long flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (scmrq->error != -EIO)
 | 
				
			||||||
 | 
							goto restart;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* For -EIO the response block is valid. */
 | 
				
			||||||
 | 
						switch (scmrq->aob->response.eqc) {
 | 
				
			||||||
 | 
						case EQC_WR_PROHIBIT:
 | 
				
			||||||
 | 
							spin_lock_irqsave(&bdev->lock, flags);
 | 
				
			||||||
 | 
							if (bdev->state != SCM_WR_PROHIBIT)
 | 
				
			||||||
 | 
								pr_info("%lu: Write access to the SCM increment is suspended\n",
 | 
				
			||||||
 | 
									(unsigned long) bdev->scmdev->address);
 | 
				
			||||||
 | 
							bdev->state = SCM_WR_PROHIBIT;
 | 
				
			||||||
 | 
							spin_unlock_irqrestore(&bdev->lock, flags);
 | 
				
			||||||
 | 
							goto requeue;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					restart:
 | 
				
			||||||
 | 
						if (!scm_start_aob(scmrq->aob))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					requeue:
 | 
				
			||||||
 | 
						spin_lock_irqsave(&bdev->rq_lock, flags);
 | 
				
			||||||
 | 
						scm_request_requeue(scmrq);
 | 
				
			||||||
 | 
						spin_unlock_irqrestore(&bdev->rq_lock, flags);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void scm_blk_tasklet(struct scm_blk_dev *bdev)
 | 
					static void scm_blk_tasklet(struct scm_blk_dev *bdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct scm_request *scmrq;
 | 
						struct scm_request *scmrq;
 | 
				
			||||||
| 
						 | 
					@ -293,11 +339,8 @@ static void scm_blk_tasklet(struct scm_blk_dev *bdev)
 | 
				
			||||||
		spin_unlock_irqrestore(&bdev->lock, flags);
 | 
							spin_unlock_irqrestore(&bdev->lock, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (scmrq->error && scmrq->retries-- > 0) {
 | 
							if (scmrq->error && scmrq->retries-- > 0) {
 | 
				
			||||||
			if (scm_start_aob(scmrq->aob)) {
 | 
								scm_blk_handle_error(scmrq);
 | 
				
			||||||
				spin_lock_irqsave(&bdev->rq_lock, flags);
 | 
					
 | 
				
			||||||
				scm_request_requeue(scmrq);
 | 
					 | 
				
			||||||
				spin_unlock_irqrestore(&bdev->rq_lock, flags);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			/* Request restarted or requeued, handle next. */
 | 
								/* Request restarted or requeued, handle next. */
 | 
				
			||||||
			spin_lock_irqsave(&bdev->lock, flags);
 | 
								spin_lock_irqsave(&bdev->lock, flags);
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
| 
						 | 
					@ -310,7 +353,6 @@ static void scm_blk_tasklet(struct scm_blk_dev *bdev)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		scm_request_finish(scmrq);
 | 
							scm_request_finish(scmrq);
 | 
				
			||||||
		atomic_dec(&bdev->queued_reqs);
 | 
					 | 
				
			||||||
		spin_lock_irqsave(&bdev->lock, flags);
 | 
							spin_lock_irqsave(&bdev->lock, flags);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	spin_unlock_irqrestore(&bdev->lock, flags);
 | 
						spin_unlock_irqrestore(&bdev->lock, flags);
 | 
				
			||||||
| 
						 | 
					@ -332,6 +374,7 @@ int scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bdev->scmdev = scmdev;
 | 
						bdev->scmdev = scmdev;
 | 
				
			||||||
 | 
						bdev->state = SCM_OPER;
 | 
				
			||||||
	spin_lock_init(&bdev->rq_lock);
 | 
						spin_lock_init(&bdev->rq_lock);
 | 
				
			||||||
	spin_lock_init(&bdev->lock);
 | 
						spin_lock_init(&bdev->lock);
 | 
				
			||||||
	INIT_LIST_HEAD(&bdev->finished_requests);
 | 
						INIT_LIST_HEAD(&bdev->finished_requests);
 | 
				
			||||||
| 
						 | 
					@ -396,6 +439,18 @@ void scm_blk_dev_cleanup(struct scm_blk_dev *bdev)
 | 
				
			||||||
	put_disk(bdev->gendisk);
 | 
						put_disk(bdev->gendisk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void scm_blk_set_available(struct scm_blk_dev *bdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock_irqsave(&bdev->lock, flags);
 | 
				
			||||||
 | 
						if (bdev->state == SCM_WR_PROHIBIT)
 | 
				
			||||||
 | 
							pr_info("%lu: Write access to the SCM increment is restored\n",
 | 
				
			||||||
 | 
								(unsigned long) bdev->scmdev->address);
 | 
				
			||||||
 | 
						bdev->state = SCM_OPER;
 | 
				
			||||||
 | 
						spin_unlock_irqrestore(&bdev->lock, flags);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __init scm_blk_init(void)
 | 
					static int __init scm_blk_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret = -EINVAL;
 | 
						int ret = -EINVAL;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ struct scm_blk_dev {
 | 
				
			||||||
	spinlock_t rq_lock;	/* guard the request queue */
 | 
						spinlock_t rq_lock;	/* guard the request queue */
 | 
				
			||||||
	spinlock_t lock;	/* guard the rest of the blockdev */
 | 
						spinlock_t lock;	/* guard the rest of the blockdev */
 | 
				
			||||||
	atomic_t queued_reqs;
 | 
						atomic_t queued_reqs;
 | 
				
			||||||
 | 
						enum {SCM_OPER, SCM_WR_PROHIBIT} state;
 | 
				
			||||||
	struct list_head finished_requests;
 | 
						struct list_head finished_requests;
 | 
				
			||||||
#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
 | 
					#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
 | 
				
			||||||
	struct list_head cluster_list;
 | 
						struct list_head cluster_list;
 | 
				
			||||||
| 
						 | 
					@ -48,6 +49,7 @@ struct scm_request {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int scm_blk_dev_setup(struct scm_blk_dev *, struct scm_device *);
 | 
					int scm_blk_dev_setup(struct scm_blk_dev *, struct scm_device *);
 | 
				
			||||||
void scm_blk_dev_cleanup(struct scm_blk_dev *);
 | 
					void scm_blk_dev_cleanup(struct scm_blk_dev *);
 | 
				
			||||||
 | 
					void scm_blk_set_available(struct scm_blk_dev *);
 | 
				
			||||||
void scm_blk_irq(struct scm_device *, void *, int);
 | 
					void scm_blk_irq(struct scm_device *, void *, int);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void scm_request_finish(struct scm_request *);
 | 
					void scm_request_finish(struct scm_request *);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,12 +13,23 @@
 | 
				
			||||||
#include <asm/eadm.h>
 | 
					#include <asm/eadm.h>
 | 
				
			||||||
#include "scm_blk.h"
 | 
					#include "scm_blk.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void notify(struct scm_device *scmdev)
 | 
					static void scm_notify(struct scm_device *scmdev, enum scm_event event)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct scm_blk_dev *bdev = dev_get_drvdata(&scmdev->dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (event) {
 | 
				
			||||||
 | 
						case SCM_CHANGE:
 | 
				
			||||||
		pr_info("%lu: The capabilities of the SCM increment changed\n",
 | 
							pr_info("%lu: The capabilities of the SCM increment changed\n",
 | 
				
			||||||
			(unsigned long) scmdev->address);
 | 
								(unsigned long) scmdev->address);
 | 
				
			||||||
		SCM_LOG(2, "State changed");
 | 
							SCM_LOG(2, "State changed");
 | 
				
			||||||
		SCM_LOG_STATE(2, scmdev);
 | 
							SCM_LOG_STATE(2, scmdev);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case SCM_AVAIL:
 | 
				
			||||||
 | 
							SCM_LOG(2, "Increment available");
 | 
				
			||||||
 | 
							SCM_LOG_STATE(2, scmdev);
 | 
				
			||||||
 | 
							scm_blk_set_available(bdev);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int scm_probe(struct scm_device *scmdev)
 | 
					static int scm_probe(struct scm_device *scmdev)
 | 
				
			||||||
| 
						 | 
					@ -64,7 +75,7 @@ static struct scm_driver scm_drv = {
 | 
				
			||||||
		.name = "scm_block",
 | 
							.name = "scm_block",
 | 
				
			||||||
		.owner = THIS_MODULE,
 | 
							.owner = THIS_MODULE,
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	.notify = notify,
 | 
						.notify = scm_notify,
 | 
				
			||||||
	.probe = scm_probe,
 | 
						.probe = scm_probe,
 | 
				
			||||||
	.remove = scm_remove,
 | 
						.remove = scm_remove,
 | 
				
			||||||
	.handler = scm_blk_irq,
 | 
						.handler = scm_blk_irq,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -627,6 +627,8 @@ static int __init sclp_detect_standby_memory(void)
 | 
				
			||||||
	struct read_storage_sccb *sccb;
 | 
						struct read_storage_sccb *sccb;
 | 
				
			||||||
	int i, id, assigned, rc;
 | 
						int i, id, assigned, rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (OLDMEM_BASE) /* No standby memory in kdump mode */
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
	if (!early_read_info_sccb_valid)
 | 
						if (!early_read_info_sccb_valid)
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	if ((sclp_facilities & 0xe00000000000ULL) != 0xe00000000000ULL)
 | 
						if ((sclp_facilities & 0xe00000000000ULL) != 0xe00000000000ULL)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -433,6 +433,20 @@ static void chsc_process_sei_scm_change(struct chsc_sei_nt0_area *sei_area)
 | 
				
			||||||
			      " failed (rc=%d).\n", ret);
 | 
								      " failed (rc=%d).\n", ret);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void chsc_process_sei_scm_avail(struct chsc_sei_nt0_area *sei_area)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CIO_CRW_EVENT(4, "chsc: scm available information\n");
 | 
				
			||||||
 | 
						if (sei_area->rs != 7)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = scm_process_availability_information();
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							CIO_CRW_EVENT(0, "chsc: process availability information"
 | 
				
			||||||
 | 
								      " failed (rc=%d).\n", ret);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area)
 | 
					static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	switch (sei_area->cc) {
 | 
						switch (sei_area->cc) {
 | 
				
			||||||
| 
						 | 
					@ -468,6 +482,9 @@ static void chsc_process_sei_nt0(struct chsc_sei_nt0_area *sei_area)
 | 
				
			||||||
	case 12: /* scm change notification */
 | 
						case 12: /* scm change notification */
 | 
				
			||||||
		chsc_process_sei_scm_change(sei_area);
 | 
							chsc_process_sei_scm_change(sei_area);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case 14: /* scm available notification */
 | 
				
			||||||
 | 
							chsc_process_sei_scm_avail(sei_area);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	default: /* other stuff */
 | 
						default: /* other stuff */
 | 
				
			||||||
		CIO_CRW_EVENT(2, "chsc: sei nt0 unhandled cc=%d\n",
 | 
							CIO_CRW_EVENT(2, "chsc: sei nt0 unhandled cc=%d\n",
 | 
				
			||||||
			      sei_area->cc);
 | 
								      sei_area->cc);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -156,8 +156,10 @@ int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_SCM_BUS
 | 
					#ifdef CONFIG_SCM_BUS
 | 
				
			||||||
int scm_update_information(void);
 | 
					int scm_update_information(void);
 | 
				
			||||||
 | 
					int scm_process_availability_information(void);
 | 
				
			||||||
#else /* CONFIG_SCM_BUS */
 | 
					#else /* CONFIG_SCM_BUS */
 | 
				
			||||||
static inline int scm_update_information(void) { return 0; }
 | 
					static inline int scm_update_information(void) { return 0; }
 | 
				
			||||||
 | 
					static inline int scm_process_availability_information(void) { return 0; }
 | 
				
			||||||
#endif /* CONFIG_SCM_BUS */
 | 
					#endif /* CONFIG_SCM_BUS */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -211,7 +211,7 @@ static void scmdev_update(struct scm_device *scmdev, struct sale *sale)
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
	scmdrv = to_scm_drv(scmdev->dev.driver);
 | 
						scmdrv = to_scm_drv(scmdev->dev.driver);
 | 
				
			||||||
	if (changed && scmdrv->notify)
 | 
						if (changed && scmdrv->notify)
 | 
				
			||||||
		scmdrv->notify(scmdev);
 | 
							scmdrv->notify(scmdev, SCM_CHANGE);
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	device_unlock(&scmdev->dev);
 | 
						device_unlock(&scmdev->dev);
 | 
				
			||||||
	if (changed)
 | 
						if (changed)
 | 
				
			||||||
| 
						 | 
					@ -297,6 +297,22 @@ int scm_update_information(void)
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int scm_dev_avail(struct device *dev, void *unused)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct scm_driver *scmdrv = to_scm_drv(dev->driver);
 | 
				
			||||||
 | 
						struct scm_device *scmdev = to_scm_dev(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dev->driver && scmdrv->notify)
 | 
				
			||||||
 | 
							scmdrv->notify(scmdev, SCM_AVAIL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int scm_process_availability_information(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return bus_for_each_dev(&scm_bus_type, NULL, NULL, scm_dev_avail);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __init scm_init(void)
 | 
					static int __init scm_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue