s390/scm_blk: suspend writes
Stop writing to scm after certain error conditions such as a concurrent firmware upgrade. Resume to normal state once scm_blk_set_available is called (due to an scm availability notification). Reviewed-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com> Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com> Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
93481c9020
commit
4fa3c01964
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -222,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");
|
||||||
|
@ -285,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;
|
||||||
|
@ -298,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;
|
||||||
|
@ -336,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);
|
||||||
|
@ -400,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 *);
|
||||||
|
|
Loading…
Reference in New Issue