diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2013-02-28 06:07:48 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2013-03-07 03:52:23 -0500 |
commit | 4fa3c019640ef776e393345ed35d9ec5c51aa3c1 (patch) | |
tree | 496a04beb012b9111f3735dbf3cf14a5861b71bd | |
parent | 93481c90200c50c7874b6a773acc87095ee3907d (diff) |
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>
-rw-r--r-- | arch/s390/include/asm/eadm.h | 2 | ||||
-rw-r--r-- | drivers/s390/block/scm_blk.c | 61 | ||||
-rw-r--r-- | drivers/s390/block/scm_blk.h | 2 |
3 files changed, 60 insertions, 5 deletions
diff --git a/arch/s390/include/asm/eadm.h b/arch/s390/include/asm/eadm.h index a4a1ea49003e..78141be88b3d 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; |
diff --git a/drivers/s390/block/scm_blk.c b/drivers/s390/block/scm_blk.c index d9c7e940fa35..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; |
@@ -222,6 +227,10 @@ static void scm_blk_request(struct request_queue *rq) | |||
222 | if (req->cmd_type != REQ_TYPE_FS) | 227 | if (req->cmd_type != REQ_TYPE_FS) |
223 | continue; | 228 | continue; |
224 | 229 | ||
230 | if (!scm_permit_request(bdev, req)) { | ||
231 | scm_ensure_queue_restart(bdev); | ||
232 | return; | ||
233 | } | ||
225 | scmrq = scm_request_fetch(); | 234 | scmrq = scm_request_fetch(); |
226 | if (!scmrq) { | 235 | if (!scmrq) { |
227 | SCM_LOG(5, "no request"); | 236 | SCM_LOG(5, "no request"); |
@@ -285,6 +294,38 @@ void scm_blk_irq(struct scm_device *scmdev, void *data, int error) | |||
285 | tasklet_hi_schedule(&bdev->tasklet); | 294 | tasklet_hi_schedule(&bdev->tasklet); |
286 | } | 295 | } |
287 | 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 | |||
288 | static void scm_blk_tasklet(struct scm_blk_dev *bdev) | 329 | static void scm_blk_tasklet(struct scm_blk_dev *bdev) |
289 | { | 330 | { |
290 | struct scm_request *scmrq; | 331 | struct scm_request *scmrq; |
@@ -298,11 +339,8 @@ static void scm_blk_tasklet(struct scm_blk_dev *bdev) | |||
298 | spin_unlock_irqrestore(&bdev->lock, flags); | 339 | spin_unlock_irqrestore(&bdev->lock, flags); |
299 | 340 | ||
300 | if (scmrq->error && scmrq->retries-- > 0) { | 341 | if (scmrq->error && scmrq->retries-- > 0) { |
301 | if (scm_start_aob(scmrq->aob)) { | 342 | scm_blk_handle_error(scmrq); |
302 | spin_lock_irqsave(&bdev->rq_lock, flags); | 343 | |
303 | scm_request_requeue(scmrq); | ||
304 | spin_unlock_irqrestore(&bdev->rq_lock, flags); | ||
305 | } | ||
306 | /* Request restarted or requeued, handle next. */ | 344 | /* Request restarted or requeued, handle next. */ |
307 | spin_lock_irqsave(&bdev->lock, flags); | 345 | spin_lock_irqsave(&bdev->lock, flags); |
308 | continue; | 346 | continue; |
@@ -336,6 +374,7 @@ int scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev) | |||
336 | } | 374 | } |
337 | 375 | ||
338 | bdev->scmdev = scmdev; | 376 | bdev->scmdev = scmdev; |
377 | bdev->state = SCM_OPER; | ||
339 | spin_lock_init(&bdev->rq_lock); | 378 | spin_lock_init(&bdev->rq_lock); |
340 | spin_lock_init(&bdev->lock); | 379 | spin_lock_init(&bdev->lock); |
341 | INIT_LIST_HEAD(&bdev->finished_requests); | 380 | INIT_LIST_HEAD(&bdev->finished_requests); |
@@ -400,6 +439,18 @@ void scm_blk_dev_cleanup(struct scm_blk_dev *bdev) | |||
400 | put_disk(bdev->gendisk); | 439 | put_disk(bdev->gendisk); |
401 | } | 440 | } |
402 | 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 | |||
403 | static int __init scm_blk_init(void) | 454 | static int __init scm_blk_init(void) |
404 | { | 455 | { |
405 | 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 *); |