aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/block
diff options
context:
space:
mode:
authorSebastian Ott <sebott@linux.vnet.ibm.com>2012-08-28 10:51:19 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2012-09-26 09:45:01 -0400
commit0d804b20735d974534abb422f723d404b779433a (patch)
tree5ca065600a604eec5c45878cb0f432fb5c2b397d /drivers/s390/block
parentf30664e2c85c7804f07c636bbe99f35e0b2d4c76 (diff)
s390/scm_block: force cluster writes
Force writes to Storage Class Memory (SCM) to be in done in clusters. Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/block')
-rw-r--r--drivers/s390/block/Kconfig7
-rw-r--r--drivers/s390/block/Makefile3
-rw-r--r--drivers/s390/block/scm_blk.c37
-rw-r--r--drivers/s390/block/scm_blk.h38
-rw-r--r--drivers/s390/block/scm_blk_cluster.c228
5 files changed, 310 insertions, 3 deletions
diff --git a/drivers/s390/block/Kconfig b/drivers/s390/block/Kconfig
index 18178b64e89a..4a3b62326183 100644
--- a/drivers/s390/block/Kconfig
+++ b/drivers/s390/block/Kconfig
@@ -81,3 +81,10 @@ config SCM_BLOCK
81 81
82 To compile this driver as a module, choose M here: the 82 To compile this driver as a module, choose M here: the
83 module will be called scm_block. 83 module will be called scm_block.
84
85config SCM_BLOCK_CLUSTER_WRITE
86 def_bool y
87 prompt "SCM force cluster writes"
88 depends on SCM_BLOCK
89 help
90 Force writes to Storage Class Memory (SCM) to be in done in clusters.
diff --git a/drivers/s390/block/Makefile b/drivers/s390/block/Makefile
index b64e2b32c753..c2f4e673e031 100644
--- a/drivers/s390/block/Makefile
+++ b/drivers/s390/block/Makefile
@@ -19,4 +19,7 @@ obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o
19obj-$(CONFIG_DCSSBLK) += dcssblk.o 19obj-$(CONFIG_DCSSBLK) += dcssblk.o
20 20
21scm_block-objs := scm_drv.o scm_blk.o 21scm_block-objs := scm_drv.o scm_blk.o
22ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
23scm_block-objs += scm_blk_cluster.o
24endif
22obj-$(CONFIG_SCM_BLOCK) += scm_block.o 25obj-$(CONFIG_SCM_BLOCK) += scm_block.o
diff --git a/drivers/s390/block/scm_blk.c b/drivers/s390/block/scm_blk.c
index 634ad58cbef6..9978ad4433cb 100644
--- a/drivers/s390/block/scm_blk.c
+++ b/drivers/s390/block/scm_blk.c
@@ -37,6 +37,7 @@ static void __scm_free_rq(struct scm_request *scmrq)
37 37
38 free_page((unsigned long) scmrq->aob); 38 free_page((unsigned long) scmrq->aob);
39 free_page((unsigned long) scmrq->aidaw); 39 free_page((unsigned long) scmrq->aidaw);
40 __scm_free_rq_cluster(scmrq);
40 kfree(aobrq); 41 kfree(aobrq);
41} 42}
42 43
@@ -70,6 +71,12 @@ static int __scm_alloc_rq(void)
70 __scm_free_rq(scmrq); 71 __scm_free_rq(scmrq);
71 return -ENOMEM; 72 return -ENOMEM;
72 } 73 }
74
75 if (__scm_alloc_rq_cluster(scmrq)) {
76 __scm_free_rq(scmrq);
77 return -ENOMEM;
78 }
79
73 INIT_LIST_HEAD(&scmrq->list); 80 INIT_LIST_HEAD(&scmrq->list);
74 spin_lock_irq(&list_lock); 81 spin_lock_irq(&list_lock);
75 list_add(&scmrq->list, &inactive_requests); 82 list_add(&scmrq->list, &inactive_requests);
@@ -170,6 +177,7 @@ static inline void scm_request_init(struct scm_blk_dev *bdev,
170 scmrq->bdev = bdev; 177 scmrq->bdev = bdev;
171 scmrq->retries = 4; 178 scmrq->retries = 4;
172 scmrq->error = 0; 179 scmrq->error = 0;
180 scm_request_cluster_init(scmrq);
173} 181}
174 182
175static void scm_ensure_queue_restart(struct scm_blk_dev *bdev) 183static void scm_ensure_queue_restart(struct scm_blk_dev *bdev)
@@ -181,17 +189,19 @@ static void scm_ensure_queue_restart(struct scm_blk_dev *bdev)
181 blk_delay_queue(bdev->rq, SCM_QUEUE_DELAY); 189 blk_delay_queue(bdev->rq, SCM_QUEUE_DELAY);
182} 190}
183 191
184static void scm_request_requeue(struct scm_request *scmrq) 192void scm_request_requeue(struct scm_request *scmrq)
185{ 193{
186 struct scm_blk_dev *bdev = scmrq->bdev; 194 struct scm_blk_dev *bdev = scmrq->bdev;
187 195
196 scm_release_cluster(scmrq);
188 blk_requeue_request(bdev->rq, scmrq->request); 197 blk_requeue_request(bdev->rq, scmrq->request);
189 scm_request_done(scmrq); 198 scm_request_done(scmrq);
190 scm_ensure_queue_restart(bdev); 199 scm_ensure_queue_restart(bdev);
191} 200}
192 201
193static void scm_request_finish(struct scm_request *scmrq) 202void scm_request_finish(struct scm_request *scmrq)
194{ 203{
204 scm_release_cluster(scmrq);
195 blk_end_request_all(scmrq->request, scmrq->error); 205 blk_end_request_all(scmrq->request, scmrq->error);
196 scm_request_done(scmrq); 206 scm_request_done(scmrq);
197} 207}
@@ -215,6 +225,16 @@ static void scm_blk_request(struct request_queue *rq)
215 return; 225 return;
216 } 226 }
217 scm_request_init(bdev, scmrq, req); 227 scm_request_init(bdev, scmrq, req);
228 if (!scm_reserve_cluster(scmrq)) {
229 SCM_LOG(5, "cluster busy");
230 scm_request_done(scmrq);
231 return;
232 }
233 if (scm_need_cluster_request(scmrq)) {
234 blk_start_request(req);
235 scm_initiate_cluster_request(scmrq);
236 return;
237 }
218 scm_request_prepare(scmrq); 238 scm_request_prepare(scmrq);
219 blk_start_request(req); 239 blk_start_request(req);
220 240
@@ -282,6 +302,13 @@ static void scm_blk_tasklet(struct scm_blk_dev *bdev)
282 spin_lock_irqsave(&bdev->lock, flags); 302 spin_lock_irqsave(&bdev->lock, flags);
283 continue; 303 continue;
284 } 304 }
305
306 if (scm_test_cluster_request(scmrq)) {
307 scm_cluster_request_irq(scmrq);
308 spin_lock_irqsave(&bdev->lock, flags);
309 continue;
310 }
311
285 scm_request_finish(scmrq); 312 scm_request_finish(scmrq);
286 atomic_dec(&bdev->queued_reqs); 313 atomic_dec(&bdev->queued_reqs);
287 spin_lock_irqsave(&bdev->lock, flags); 314 spin_lock_irqsave(&bdev->lock, flags);
@@ -325,6 +352,7 @@ int scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev)
325 blk_queue_max_hw_sectors(rq, nr_max_blk << 3); /* 8 * 512 = blk_size */ 352 blk_queue_max_hw_sectors(rq, nr_max_blk << 3); /* 8 * 512 = blk_size */
326 blk_queue_max_segments(rq, nr_max_blk); 353 blk_queue_max_segments(rq, nr_max_blk);
327 queue_flag_set_unlocked(QUEUE_FLAG_NONROT, rq); 354 queue_flag_set_unlocked(QUEUE_FLAG_NONROT, rq);
355 scm_blk_dev_cluster_setup(bdev);
328 356
329 bdev->gendisk = alloc_disk(SCM_NR_PARTS); 357 bdev->gendisk = alloc_disk(SCM_NR_PARTS);
330 if (!bdev->gendisk) 358 if (!bdev->gendisk)
@@ -370,7 +398,10 @@ void scm_blk_dev_cleanup(struct scm_blk_dev *bdev)
370 398
371static int __init scm_blk_init(void) 399static int __init scm_blk_init(void)
372{ 400{
373 int ret; 401 int ret = -EINVAL;
402
403 if (!scm_cluster_size_valid())
404 goto out;
374 405
375 ret = register_blkdev(0, "scm"); 406 ret = register_blkdev(0, "scm");
376 if (ret < 0) 407 if (ret < 0)
diff --git a/drivers/s390/block/scm_blk.h b/drivers/s390/block/scm_blk.h
index 5aba5612588f..7ac6bad919ef 100644
--- a/drivers/s390/block/scm_blk.h
+++ b/drivers/s390/block/scm_blk.h
@@ -22,6 +22,9 @@ struct scm_blk_dev {
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 struct list_head finished_requests; 24 struct list_head finished_requests;
25#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
26 struct list_head cluster_list;
27#endif
25}; 28};
26 29
27struct scm_request { 30struct scm_request {
@@ -32,6 +35,13 @@ struct scm_request {
32 struct list_head list; 35 struct list_head list;
33 u8 retries; 36 u8 retries;
34 int error; 37 int error;
38#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
39 struct {
40 enum {CLUSTER_NONE, CLUSTER_READ, CLUSTER_WRITE} state;
41 struct list_head list;
42 void **buf;
43 } cluster;
44#endif
35}; 45};
36 46
37#define to_aobrq(rq) container_of((void *) rq, struct aob_rq_header, data) 47#define to_aobrq(rq) container_of((void *) rq, struct aob_rq_header, data)
@@ -40,9 +50,37 @@ int scm_blk_dev_setup(struct scm_blk_dev *, struct scm_device *);
40void scm_blk_dev_cleanup(struct scm_blk_dev *); 50void scm_blk_dev_cleanup(struct scm_blk_dev *);
41void scm_blk_irq(struct scm_device *, void *, int); 51void scm_blk_irq(struct scm_device *, void *, int);
42 52
53void scm_request_finish(struct scm_request *);
54void scm_request_requeue(struct scm_request *);
55
43int scm_drv_init(void); 56int scm_drv_init(void);
44void scm_drv_cleanup(void); 57void scm_drv_cleanup(void);
45 58
59#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
60void __scm_free_rq_cluster(struct scm_request *);
61int __scm_alloc_rq_cluster(struct scm_request *);
62void scm_request_cluster_init(struct scm_request *);
63bool scm_reserve_cluster(struct scm_request *);
64void scm_release_cluster(struct scm_request *);
65void scm_blk_dev_cluster_setup(struct scm_blk_dev *);
66bool scm_need_cluster_request(struct scm_request *);
67void scm_initiate_cluster_request(struct scm_request *);
68void scm_cluster_request_irq(struct scm_request *);
69bool scm_test_cluster_request(struct scm_request *);
70bool scm_cluster_size_valid(void);
71#else
72#define __scm_free_rq_cluster(scmrq) {}
73#define __scm_alloc_rq_cluster(scmrq) 0
74#define scm_request_cluster_init(scmrq) {}
75#define scm_reserve_cluster(scmrq) true
76#define scm_release_cluster(scmrq) {}
77#define scm_blk_dev_cluster_setup(bdev) {}
78#define scm_need_cluster_request(scmrq) false
79#define scm_initiate_cluster_request(scmrq) {}
80#define scm_cluster_request_irq(scmrq) {}
81#define scm_test_cluster_request(scmrq) false
82#define scm_cluster_size_valid() true
83#endif
46 84
47extern debug_info_t *scm_debug; 85extern debug_info_t *scm_debug;
48 86
diff --git a/drivers/s390/block/scm_blk_cluster.c b/drivers/s390/block/scm_blk_cluster.c
new file mode 100644
index 000000000000..f4bb61b0cea1
--- /dev/null
+++ b/drivers/s390/block/scm_blk_cluster.c
@@ -0,0 +1,228 @@
1/*
2 * Block driver for s390 storage class memory.
3 *
4 * Copyright IBM Corp. 2012
5 * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
6 */
7
8#include <linux/spinlock.h>
9#include <linux/module.h>
10#include <linux/blkdev.h>
11#include <linux/genhd.h>
12#include <linux/slab.h>
13#include <linux/list.h>
14#include <asm/eadm.h>
15#include "scm_blk.h"
16
17static unsigned int write_cluster_size = 64;
18module_param(write_cluster_size, uint, S_IRUGO);
19MODULE_PARM_DESC(write_cluster_size,
20 "Number of pages used for contiguous writes.");
21
22#define CLUSTER_SIZE (write_cluster_size * PAGE_SIZE)
23
24void __scm_free_rq_cluster(struct scm_request *scmrq)
25{
26 int i;
27
28 if (!scmrq->cluster.buf)
29 return;
30
31 for (i = 0; i < 2 * write_cluster_size; i++)
32 free_page((unsigned long) scmrq->cluster.buf[i]);
33
34 kfree(scmrq->cluster.buf);
35}
36
37int __scm_alloc_rq_cluster(struct scm_request *scmrq)
38{
39 int i;
40
41 scmrq->cluster.buf = kzalloc(sizeof(void *) * 2 * write_cluster_size,
42 GFP_KERNEL);
43 if (!scmrq->cluster.buf)
44 return -ENOMEM;
45
46 for (i = 0; i < 2 * write_cluster_size; i++) {
47 scmrq->cluster.buf[i] = (void *) get_zeroed_page(GFP_DMA);
48 if (!scmrq->cluster.buf[i])
49 return -ENOMEM;
50 }
51 INIT_LIST_HEAD(&scmrq->cluster.list);
52 return 0;
53}
54
55void scm_request_cluster_init(struct scm_request *scmrq)
56{
57 scmrq->cluster.state = CLUSTER_NONE;
58}
59
60static bool clusters_intersect(struct scm_request *A, struct scm_request *B)
61{
62 unsigned long firstA, lastA, firstB, lastB;
63
64 firstA = ((u64) blk_rq_pos(A->request) << 9) / CLUSTER_SIZE;
65 lastA = (((u64) blk_rq_pos(A->request) << 9) +
66 blk_rq_bytes(A->request) - 1) / CLUSTER_SIZE;
67
68 firstB = ((u64) blk_rq_pos(B->request) << 9) / CLUSTER_SIZE;
69 lastB = (((u64) blk_rq_pos(B->request) << 9) +
70 blk_rq_bytes(B->request) - 1) / CLUSTER_SIZE;
71
72 return (firstB <= lastA && firstA <= lastB);
73}
74
75bool scm_reserve_cluster(struct scm_request *scmrq)
76{
77 struct scm_blk_dev *bdev = scmrq->bdev;
78 struct scm_request *iter;
79
80 if (write_cluster_size == 0)
81 return true;
82
83 spin_lock(&bdev->lock);
84 list_for_each_entry(iter, &bdev->cluster_list, cluster.list) {
85 if (clusters_intersect(scmrq, iter) &&
86 (rq_data_dir(scmrq->request) == WRITE ||
87 rq_data_dir(iter->request) == WRITE)) {
88 spin_unlock(&bdev->lock);
89 return false;
90 }
91 }
92 list_add(&scmrq->cluster.list, &bdev->cluster_list);
93 spin_unlock(&bdev->lock);
94
95 return true;
96}
97
98void scm_release_cluster(struct scm_request *scmrq)
99{
100 struct scm_blk_dev *bdev = scmrq->bdev;
101 unsigned long flags;
102
103 if (write_cluster_size == 0)
104 return;
105
106 spin_lock_irqsave(&bdev->lock, flags);
107 list_del(&scmrq->cluster.list);
108 spin_unlock_irqrestore(&bdev->lock, flags);
109}
110
111void scm_blk_dev_cluster_setup(struct scm_blk_dev *bdev)
112{
113 INIT_LIST_HEAD(&bdev->cluster_list);
114 blk_queue_io_opt(bdev->rq, CLUSTER_SIZE);
115}
116
117static void scm_prepare_cluster_request(struct scm_request *scmrq)
118{
119 struct scm_blk_dev *bdev = scmrq->bdev;
120 struct scm_device *scmdev = bdev->gendisk->private_data;
121 struct request *req = scmrq->request;
122 struct aidaw *aidaw = scmrq->aidaw;
123 struct msb *msb = &scmrq->aob->msb[0];
124 struct req_iterator iter;
125 struct bio_vec *bv;
126 int i = 0;
127 u64 addr;
128
129 switch (scmrq->cluster.state) {
130 case CLUSTER_NONE:
131 scmrq->cluster.state = CLUSTER_READ;
132 /* fall through */
133 case CLUSTER_READ:
134 scmrq->aob->request.msb_count = 1;
135 msb->bs = MSB_BS_4K;
136 msb->oc = MSB_OC_READ;
137 msb->flags = MSB_FLAG_IDA;
138 msb->data_addr = (u64) aidaw;
139 msb->blk_count = write_cluster_size;
140
141 addr = scmdev->address + ((u64) blk_rq_pos(req) << 9);
142 msb->scm_addr = round_down(addr, CLUSTER_SIZE);
143
144 if (msb->scm_addr !=
145 round_down(addr + (u64) blk_rq_bytes(req) - 1,
146 CLUSTER_SIZE))
147 msb->blk_count = 2 * write_cluster_size;
148
149 for (i = 0; i < msb->blk_count; i++) {
150 aidaw->data_addr = (u64) scmrq->cluster.buf[i];
151 aidaw++;
152 }
153
154 break;
155 case CLUSTER_WRITE:
156 msb->oc = MSB_OC_WRITE;
157
158 for (addr = msb->scm_addr;
159 addr < scmdev->address + ((u64) blk_rq_pos(req) << 9);
160 addr += PAGE_SIZE) {
161 aidaw->data_addr = (u64) scmrq->cluster.buf[i];
162 aidaw++;
163 i++;
164 }
165 rq_for_each_segment(bv, req, iter) {
166 aidaw->data_addr = (u64) page_address(bv->bv_page);
167 aidaw++;
168 i++;
169 }
170 for (; i < msb->blk_count; i++) {
171 aidaw->data_addr = (u64) scmrq->cluster.buf[i];
172 aidaw++;
173 }
174 break;
175 }
176}
177
178bool scm_need_cluster_request(struct scm_request *scmrq)
179{
180 if (rq_data_dir(scmrq->request) == READ)
181 return false;
182
183 return blk_rq_bytes(scmrq->request) < CLUSTER_SIZE;
184}
185
186/* Called with queue lock held. */
187void scm_initiate_cluster_request(struct scm_request *scmrq)
188{
189 scm_prepare_cluster_request(scmrq);
190 if (scm_start_aob(scmrq->aob))
191 scm_request_requeue(scmrq);
192}
193
194bool scm_test_cluster_request(struct scm_request *scmrq)
195{
196 return scmrq->cluster.state != CLUSTER_NONE;
197}
198
199void scm_cluster_request_irq(struct scm_request *scmrq)
200{
201 struct scm_blk_dev *bdev = scmrq->bdev;
202 unsigned long flags;
203
204 switch (scmrq->cluster.state) {
205 case CLUSTER_NONE:
206 BUG();
207 break;
208 case CLUSTER_READ:
209 if (scmrq->error) {
210 scm_request_finish(scmrq);
211 break;
212 }
213 scmrq->cluster.state = CLUSTER_WRITE;
214 spin_lock_irqsave(&bdev->rq_lock, flags);
215 scm_initiate_cluster_request(scmrq);
216 spin_unlock_irqrestore(&bdev->rq_lock, flags);
217 break;
218 case CLUSTER_WRITE:
219 scm_request_finish(scmrq);
220 break;
221 }
222}
223
224bool scm_cluster_size_valid(void)
225{
226 return write_cluster_size == 0 || write_cluster_size == 32 ||
227 write_cluster_size == 64 || write_cluster_size == 128;
228}