aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Ott2013-02-28 05:07:48 -0600
committerMartin Schwidefsky2013-03-07 02:52:23 -0600
commit4fa3c019640ef776e393345ed35d9ec5c51aa3c1 (patch)
tree496a04beb012b9111f3735dbf3cf14a5861b71bd
parent93481c90200c50c7874b6a773acc87095ee3907d (diff)
downloadomapdrm-4fa3c019640ef776e393345ed35d9ec5c51aa3c1.tar.gz
omapdrm-4fa3c019640ef776e393345ed35d9ec5c51aa3c1.tar.xz
omapdrm-4fa3c019640ef776e393345ed35d9ec5c51aa3c1.zip
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.h2
-rw-r--r--drivers/s390/block/scm_blk.c61
-rw-r--r--drivers/s390/block/scm_blk.h2
3 files changed, 60 insertions, 5 deletions
diff --git a/arch/s390/include/asm/eadm.h b/arch/s390/include/asm/eadm.h
index a4a1ea49003..78141be88b3 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
37struct msb { 39struct 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 d9c7e940fa3..5ac9c935c15 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
138static 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
138static void scm_request_prepare(struct scm_request *scmrq) 143static 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
297static 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
319restart:
320 if (!scm_start_aob(scmrq->aob))
321 return;
322
323requeue:
324 spin_lock_irqsave(&bdev->rq_lock, flags);
325 scm_request_requeue(scmrq);
326 spin_unlock_irqrestore(&bdev->rq_lock, flags);
327}
328
288static void scm_blk_tasklet(struct scm_blk_dev *bdev) 329static 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
442void 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
403static int __init scm_blk_init(void) 454static 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 3c1ccf49464..8b387b32fd6 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
49int scm_blk_dev_setup(struct scm_blk_dev *, struct scm_device *); 50int scm_blk_dev_setup(struct scm_blk_dev *, struct scm_device *);
50void scm_blk_dev_cleanup(struct scm_blk_dev *); 51void scm_blk_dev_cleanup(struct scm_blk_dev *);
52void scm_blk_set_available(struct scm_blk_dev *);
51void scm_blk_irq(struct scm_device *, void *, int); 53void scm_blk_irq(struct scm_device *, void *, int);
52 54
53void scm_request_finish(struct scm_request *); 55void scm_request_finish(struct scm_request *);