diff options
author | Eric Van Hensbergen <ericvh@gmail.com> | 2008-02-06 20:25:58 -0500 |
---|---|---|
committer | Eric Van Hensbergen <ericvh@opteron.homeip.net> | 2008-02-06 20:25:58 -0500 |
commit | e2735b7720320b68590ca2b32b78ca91213931b2 (patch) | |
tree | fcc260ada01dc7b1d0304aaf34a2a8f21cdc93fd /net/9p/trans_virtio.c | |
parent | 043aba403e9958c6526c9279b63919273cb09c13 (diff) |
9p: block-based virtio client
This replaces the console-based virto client with a block-based
client using a single request queue.
Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>
Diffstat (limited to 'net/9p/trans_virtio.c')
-rw-r--r-- | net/9p/trans_virtio.c | 344 |
1 files changed, 210 insertions, 134 deletions
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index 42eea5fe2628..0f590227943b 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c | |||
@@ -1,17 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * The Guest 9p transport driver | 2 | * The Guest 9p transport driver |
3 | * | 3 | * |
4 | * This is a trivial pipe-based transport driver based on the lguest console | 4 | * This is a block based transport driver based on the lguest block driver |
5 | * code: we use lguest's DMA mechanism to send bytes out, and register a | 5 | * code. |
6 | * DMA buffer to receive bytes in. It is assumed to be present and available | ||
7 | * from the very beginning of boot. | ||
8 | * | ||
9 | * This may be have been done by just instaniating another HVC console, | ||
10 | * but HVC's blocksize of 16 bytes is annoying and painful to performance. | ||
11 | * | ||
12 | * A more efficient transport could be built based on the virtio block driver | ||
13 | * but it requires some changes in the 9p transport model (which are in | ||
14 | * progress) | ||
15 | * | 6 | * |
16 | */ | 7 | */ |
17 | /* | 8 | /* |
@@ -55,11 +46,25 @@ | |||
55 | #include <linux/virtio.h> | 46 | #include <linux/virtio.h> |
56 | #include <linux/virtio_9p.h> | 47 | #include <linux/virtio_9p.h> |
57 | 48 | ||
49 | #define VIRTQUEUE_NUM 128 | ||
50 | |||
58 | /* a single mutex to manage channel initialization and attachment */ | 51 | /* a single mutex to manage channel initialization and attachment */ |
59 | static DECLARE_MUTEX(virtio_9p_lock); | 52 | static DECLARE_MUTEX(virtio_9p_lock); |
60 | /* global which tracks highest initialized channel */ | 53 | /* global which tracks highest initialized channel */ |
61 | static int chan_index; | 54 | static int chan_index; |
62 | 55 | ||
56 | #define P9_INIT_MAXTAG 16 | ||
57 | |||
58 | #define REQ_STATUS_IDLE 0 | ||
59 | #define REQ_STATUS_SENT 1 | ||
60 | #define REQ_STATUS_RCVD 2 | ||
61 | #define REQ_STATUS_FLSH 3 | ||
62 | |||
63 | struct p9_req_t { | ||
64 | int status; | ||
65 | wait_queue_head_t *wq; | ||
66 | }; | ||
67 | |||
63 | /* We keep all per-channel information in a structure. | 68 | /* We keep all per-channel information in a structure. |
64 | * This structure is allocated within the devices dev->mem space. | 69 | * This structure is allocated within the devices dev->mem space. |
65 | * A pointer to the structure will get put in the transport private. | 70 | * A pointer to the structure will get put in the transport private. |
@@ -68,16 +73,57 @@ static struct virtio_chan { | |||
68 | bool initialized; /* channel is initialized */ | 73 | bool initialized; /* channel is initialized */ |
69 | bool inuse; /* channel is in use */ | 74 | bool inuse; /* channel is in use */ |
70 | 75 | ||
71 | struct virtqueue *in_vq, *out_vq; | 76 | spinlock_t lock; |
77 | |||
72 | struct virtio_device *vdev; | 78 | struct virtio_device *vdev; |
79 | struct virtqueue *vq; | ||
73 | 80 | ||
74 | /* This is our input buffer, and how much data is left in it. */ | 81 | struct p9_idpool *tagpool; |
75 | unsigned int in_len; | 82 | struct p9_req_t *reqs; |
76 | char *in, *inbuf; | 83 | int max_tag; |
77 | 84 | ||
78 | wait_queue_head_t wq; /* waitq for buffer */ | 85 | /* Scatterlist: can be too big for stack. */ |
86 | struct scatterlist sg[VIRTQUEUE_NUM]; | ||
79 | } channels[MAX_9P_CHAN]; | 87 | } channels[MAX_9P_CHAN]; |
80 | 88 | ||
89 | /* Lookup requests by tag */ | ||
90 | static struct p9_req_t *p9_lookup_tag(struct virtio_chan *c, u16 tag) | ||
91 | { | ||
92 | /* This looks up the original request by tag so we know which | ||
93 | * buffer to read the data into */ | ||
94 | tag++; | ||
95 | |||
96 | while (tag >= c->max_tag) { | ||
97 | int old_max = c->max_tag; | ||
98 | int count; | ||
99 | |||
100 | if (c->max_tag) | ||
101 | c->max_tag *= 2; | ||
102 | else | ||
103 | c->max_tag = P9_INIT_MAXTAG; | ||
104 | |||
105 | c->reqs = krealloc(c->reqs, sizeof(struct p9_req_t)*c->max_tag, | ||
106 | GFP_ATOMIC); | ||
107 | if (!c->reqs) { | ||
108 | printk(KERN_ERR "Couldn't grow tag array\n"); | ||
109 | BUG(); | ||
110 | } | ||
111 | for (count = old_max; count < c->max_tag; count++) { | ||
112 | c->reqs[count].status = REQ_STATUS_IDLE; | ||
113 | c->reqs[count].wq = kmalloc(sizeof(wait_queue_t), | ||
114 | GFP_ATOMIC); | ||
115 | if (!c->reqs[count].wq) { | ||
116 | printk(KERN_ERR "Couldn't grow tag array\n"); | ||
117 | BUG(); | ||
118 | } | ||
119 | init_waitqueue_head(c->reqs[count].wq); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | return &c->reqs[tag]; | ||
124 | } | ||
125 | |||
126 | |||
81 | /* How many bytes left in this page. */ | 127 | /* How many bytes left in this page. */ |
82 | static unsigned int rest_of_page(void *data) | 128 | static unsigned int rest_of_page(void *data) |
83 | { | 129 | { |
@@ -86,128 +132,163 @@ static unsigned int rest_of_page(void *data) | |||
86 | 132 | ||
87 | static int p9_virtio_write(struct p9_trans *trans, void *buf, int count) | 133 | static int p9_virtio_write(struct p9_trans *trans, void *buf, int count) |
88 | { | 134 | { |
89 | struct virtio_chan *chan = (struct virtio_chan *) trans->priv; | 135 | /* Only use the rpc mechanism for now */ |
90 | struct virtqueue *out_vq = chan->out_vq; | 136 | return count; |
91 | struct scatterlist sg[1]; | 137 | } |
92 | unsigned int len; | ||
93 | 138 | ||
94 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio write (%d)\n", count); | 139 | static int p9_virtio_read(struct p9_trans *trans, void *buf, int count) |
140 | { | ||
141 | /* Only use the rpc mechanism for now */ | ||
142 | return 0; | ||
143 | } | ||
95 | 144 | ||
96 | /* keep it simple - make sure we don't overflow a page */ | 145 | /* The poll function is used by 9p transports to determine if there |
97 | if (rest_of_page(buf) < count) | 146 | * is there is activity available on a particular channel. In our case |
98 | count = rest_of_page(buf); | 147 | * we use it to wait for a callback from the input routines. |
148 | */ | ||
149 | static unsigned int | ||
150 | p9_virtio_poll(struct p9_trans *trans, struct poll_table_struct *pt) | ||
151 | { | ||
152 | /* Only use the rpc mechanism for now */ | ||
153 | return 0; | ||
154 | } | ||
99 | 155 | ||
100 | sg_init_one(sg, buf, count); | 156 | static void p9_virtio_close(struct p9_trans *trans) |
157 | { | ||
158 | struct virtio_chan *chan = trans->priv; | ||
159 | int count; | ||
160 | unsigned int flags; | ||
101 | 161 | ||
102 | /* add_buf wants a token to identify this buffer: we hand it any | 162 | spin_lock_irqsave(&chan->lock, flags); |
103 | * non-NULL pointer, since there's only ever one buffer. */ | 163 | p9_idpool_destroy(chan->tagpool); |
104 | if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) == 0) { | 164 | for (count = 0; count < chan->max_tag; count++) |
105 | /* Tell Host to go! */ | 165 | kfree(chan->reqs[count].wq); |
106 | out_vq->vq_ops->kick(out_vq); | 166 | kfree(chan->reqs); |
107 | /* Chill out until it's done with the buffer. */ | 167 | chan->max_tag = 0; |
108 | while (!out_vq->vq_ops->get_buf(out_vq, &len)) | 168 | spin_unlock_irqrestore(&chan->lock, flags); |
109 | cpu_relax(); | ||
110 | } | ||
111 | 169 | ||
112 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio wrote (%d)\n", count); | 170 | down(&virtio_9p_lock); |
171 | chan->inuse = false; | ||
172 | up(&virtio_9p_lock); | ||
113 | 173 | ||
114 | /* We're expected to return the amount of data we wrote: all of it. */ | 174 | kfree(trans); |
115 | return count; | ||
116 | } | 175 | } |
117 | 176 | ||
118 | /* Create a scatter-gather list representing our input buffer and put it in the | 177 | static void req_done(struct virtqueue *vq) |
119 | * queue. */ | ||
120 | static void add_inbuf(struct virtio_chan *chan) | ||
121 | { | 178 | { |
122 | struct scatterlist sg[1]; | 179 | struct virtio_chan *chan = vq->vdev->priv; |
180 | struct p9_fcall *rc; | ||
181 | unsigned int len; | ||
182 | unsigned long flags; | ||
183 | struct p9_req_t *req; | ||
184 | |||
185 | spin_lock_irqsave(&chan->lock, flags); | ||
186 | while ((rc = chan->vq->vq_ops->get_buf(chan->vq, &len)) != NULL) { | ||
187 | req = p9_lookup_tag(chan, rc->tag); | ||
188 | req->status = REQ_STATUS_RCVD; | ||
189 | wake_up(req->wq); | ||
190 | } | ||
191 | /* In case queue is stopped waiting for more buffers. */ | ||
192 | spin_unlock_irqrestore(&chan->lock, flags); | ||
193 | } | ||
123 | 194 | ||
124 | sg_init_one(sg, chan->inbuf, PAGE_SIZE); | 195 | static int |
196 | pack_sg_list(struct scatterlist *sg, int start, int limit, char *data, | ||
197 | int count) | ||
198 | { | ||
199 | int s; | ||
200 | int index = start; | ||
201 | |||
202 | while (count) { | ||
203 | s = rest_of_page(data); | ||
204 | if (s > count) | ||
205 | s = count; | ||
206 | sg_set_buf(&sg[index++], data, s); | ||
207 | count -= s; | ||
208 | data += s; | ||
209 | if (index > limit) | ||
210 | BUG(); | ||
211 | } | ||
125 | 212 | ||
126 | /* We should always be able to add one buffer to an empty queue. */ | 213 | return index-start; |
127 | if (chan->in_vq->vq_ops->add_buf(chan->in_vq, sg, 0, 1, chan->inbuf)) | ||
128 | BUG(); | ||
129 | chan->in_vq->vq_ops->kick(chan->in_vq); | ||
130 | } | 214 | } |
131 | 215 | ||
132 | static int p9_virtio_read(struct p9_trans *trans, void *buf, int count) | 216 | static int |
217 | p9_virtio_rpc(struct p9_trans *t, struct p9_fcall *tc, struct p9_fcall **rc, | ||
218 | int msize, int dotu) | ||
133 | { | 219 | { |
134 | struct virtio_chan *chan = (struct virtio_chan *) trans->priv; | 220 | int in, out; |
135 | struct virtqueue *in_vq = chan->in_vq; | 221 | int n, err, size; |
136 | 222 | struct virtio_chan *chan = t->priv; | |
137 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio read (%d)\n", count); | 223 | char *rdata; |
224 | struct p9_req_t *req; | ||
225 | unsigned long flags; | ||
226 | |||
227 | if (*rc == NULL) { | ||
228 | *rc = kmalloc(sizeof(struct p9_fcall) + msize, GFP_KERNEL); | ||
229 | if (!*rc) | ||
230 | return -ENOMEM; | ||
231 | } | ||
138 | 232 | ||
139 | /* If we don't have an input queue yet, we can't get input. */ | 233 | rdata = (char *)*rc+sizeof(struct p9_fcall); |
140 | BUG_ON(!in_vq); | ||
141 | 234 | ||
142 | /* No buffer? Try to get one. */ | 235 | spin_lock_irqsave(&chan->lock, flags); |
143 | if (!chan->in_len) { | 236 | n = P9_NOTAG; |
144 | chan->in = in_vq->vq_ops->get_buf(in_vq, &chan->in_len); | 237 | if (tc->id != P9_TVERSION) { |
145 | if (!chan->in) | 238 | n = p9_idpool_get(chan->tagpool); |
146 | return 0; | 239 | if (n < 0) |
240 | return -ENOMEM; | ||
147 | } | 241 | } |
148 | 242 | ||
149 | /* You want more than we have to give? Well, try wanting less! */ | 243 | req = p9_lookup_tag(chan, n); |
150 | if (chan->in_len < count) | 244 | spin_unlock_irqrestore(&chan->lock, flags); |
151 | count = chan->in_len; | ||
152 | 245 | ||
153 | /* Copy across to their buffer and increment offset. */ | 246 | p9_set_tag(tc, n); |
154 | memcpy(buf, chan->in, count); | ||
155 | chan->in += count; | ||
156 | chan->in_len -= count; | ||
157 | 247 | ||
158 | /* Finished? Re-register buffer so Host will use it again. */ | 248 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio rpc tag %d\n", n); |
159 | if (chan->in_len == 0) | ||
160 | add_inbuf(chan); | ||
161 | 249 | ||
162 | P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio finished read (%d)\n", | 250 | out = pack_sg_list(chan->sg, 0, VIRTQUEUE_NUM, tc->sdata, tc->size); |
163 | count); | 251 | in = pack_sg_list(chan->sg, out, VIRTQUEUE_NUM-out, rdata, msize); |
164 | 252 | ||
165 | return count; | 253 | req->status = REQ_STATUS_SENT; |
166 | } | ||
167 | 254 | ||
168 | /* The poll function is used by 9p transports to determine if there | 255 | if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, tc)) { |
169 | * is there is activity available on a particular channel. In our case | 256 | P9_DPRINTK(P9_DEBUG_TRANS, |
170 | * we use it to wait for a callback from the input routines. | 257 | "9p debug: virtio rpc add_buf returned failure"); |
171 | */ | 258 | return -EIO; |
172 | static unsigned int | 259 | } |
173 | p9_virtio_poll(struct p9_trans *trans, struct poll_table_struct *pt) | ||
174 | { | ||
175 | struct virtio_chan *chan = (struct virtio_chan *)trans->priv; | ||
176 | struct virtqueue *in_vq = chan->in_vq; | ||
177 | int ret = POLLOUT; /* we can always handle more output */ | ||
178 | 260 | ||
179 | poll_wait(NULL, &chan->wq, pt); | 261 | chan->vq->vq_ops->kick(chan->vq); |
180 | 262 | ||
181 | /* No buffer? Try to get one. */ | 263 | wait_event(*req->wq, req->status == REQ_STATUS_RCVD); |
182 | if (!chan->in_len) | ||
183 | chan->in = in_vq->vq_ops->get_buf(in_vq, &chan->in_len); | ||
184 | 264 | ||
185 | if (chan->in_len) | 265 | size = le32_to_cpu(*(__le32 *) rdata); |
186 | ret |= POLLIN; | ||
187 | 266 | ||
188 | return ret; | 267 | err = p9_deserialize_fcall(rdata, size, *rc, dotu); |
189 | } | 268 | if (err < 0) { |
269 | P9_DPRINTK(P9_DEBUG_TRANS, | ||
270 | "9p debug: virtio rpc deserialize returned %d\n", err); | ||
271 | return err; | ||
272 | } | ||
190 | 273 | ||
191 | static void p9_virtio_close(struct p9_trans *trans) | 274 | #ifdef CONFIG_NET_9P_DEBUG |
192 | { | 275 | if ((p9_debug_level&P9_DEBUG_FCALL) == P9_DEBUG_FCALL) { |
193 | struct virtio_chan *chan = trans->priv; | 276 | char buf[150]; |
194 | 277 | ||
195 | down(&virtio_9p_lock); | 278 | p9_printfcall(buf, sizeof(buf), *rc, dotu); |
196 | chan->inuse = false; | 279 | printk(KERN_NOTICE ">>> %p %s\n", t, buf); |
197 | up(&virtio_9p_lock); | 280 | } |
281 | #endif | ||
198 | 282 | ||
199 | kfree(trans); | 283 | if (n != P9_NOTAG && p9_idpool_check(n, chan->tagpool)) |
200 | } | 284 | p9_idpool_put(n, chan->tagpool); |
201 | 285 | ||
202 | static void p9_virtio_intr(struct virtqueue *q) | 286 | req->status = REQ_STATUS_IDLE; |
203 | { | ||
204 | struct virtio_chan *chan = q->vdev->priv; | ||
205 | 287 | ||
206 | P9_DPRINTK(P9_DEBUG_TRANS, "9p poll_wakeup: %p\n", &chan->wq); | 288 | return 0; |
207 | wake_up_interruptible(&chan->wq); | ||
208 | } | 289 | } |
209 | 290 | ||
210 | static int p9_virtio_probe(struct virtio_device *dev) | 291 | static int p9_virtio_probe(struct virtio_device *vdev) |
211 | { | 292 | { |
212 | int err; | 293 | int err; |
213 | struct virtio_chan *chan; | 294 | struct virtio_chan *chan; |
@@ -221,44 +302,29 @@ static int p9_virtio_probe(struct virtio_device *dev) | |||
221 | if (chan_index > MAX_9P_CHAN) { | 302 | if (chan_index > MAX_9P_CHAN) { |
222 | printk(KERN_ERR "9p: virtio: Maximum channels exceeded\n"); | 303 | printk(KERN_ERR "9p: virtio: Maximum channels exceeded\n"); |
223 | BUG(); | 304 | BUG(); |
224 | } | ||
225 | |||
226 | chan->vdev = dev; | ||
227 | |||
228 | /* This is the scratch page we use to receive console input */ | ||
229 | chan->inbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
230 | if (!chan->inbuf) { | ||
231 | err = -ENOMEM; | 305 | err = -ENOMEM; |
232 | goto fail; | 306 | goto fail; |
233 | } | 307 | } |
234 | 308 | ||
235 | /* Find the input queue. */ | 309 | chan->vdev = vdev; |
236 | dev->priv = chan; | ||
237 | chan->in_vq = dev->config->find_vq(dev, 0, p9_virtio_intr); | ||
238 | if (IS_ERR(chan->in_vq)) { | ||
239 | err = PTR_ERR(chan->in_vq); | ||
240 | goto free; | ||
241 | } | ||
242 | 310 | ||
243 | chan->out_vq = dev->config->find_vq(dev, 1, NULL); | 311 | /* We expect one virtqueue, for requests. */ |
244 | if (IS_ERR(chan->out_vq)) { | 312 | chan->vq = vdev->config->find_vq(vdev, 0, req_done); |
245 | err = PTR_ERR(chan->out_vq); | 313 | if (IS_ERR(chan->vq)) { |
246 | goto free_in_vq; | 314 | err = PTR_ERR(chan->vq); |
315 | goto out_free_vq; | ||
247 | } | 316 | } |
317 | chan->vq->vdev->priv = chan; | ||
318 | spin_lock_init(&chan->lock); | ||
248 | 319 | ||
249 | init_waitqueue_head(&chan->wq); | 320 | sg_init_table(chan->sg, VIRTQUEUE_NUM); |
250 | 321 | ||
251 | /* Register the input buffer the first time. */ | ||
252 | add_inbuf(chan); | ||
253 | chan->inuse = false; | 322 | chan->inuse = false; |
254 | chan->initialized = true; | 323 | chan->initialized = true; |
255 | |||
256 | return 0; | 324 | return 0; |
257 | 325 | ||
258 | free_in_vq: | 326 | out_free_vq: |
259 | dev->config->del_vq(chan->in_vq); | 327 | vdev->config->del_vq(chan->vq); |
260 | free: | ||
261 | kfree(chan->inbuf); | ||
262 | fail: | 328 | fail: |
263 | down(&virtio_9p_lock); | 329 | down(&virtio_9p_lock); |
264 | chan_index--; | 330 | chan_index--; |
@@ -274,8 +340,8 @@ fail: | |||
274 | static struct p9_trans *p9_virtio_create(const char *devname, char *args) | 340 | static struct p9_trans *p9_virtio_create(const char *devname, char *args) |
275 | { | 341 | { |
276 | struct p9_trans *trans; | 342 | struct p9_trans *trans; |
277 | int index = 0; | ||
278 | struct virtio_chan *chan = channels; | 343 | struct virtio_chan *chan = channels; |
344 | int index = 0; | ||
279 | 345 | ||
280 | down(&virtio_9p_lock); | 346 | down(&virtio_9p_lock); |
281 | while (index < MAX_9P_CHAN) { | 347 | while (index < MAX_9P_CHAN) { |
@@ -290,9 +356,18 @@ static struct p9_trans *p9_virtio_create(const char *devname, char *args) | |||
290 | up(&virtio_9p_lock); | 356 | up(&virtio_9p_lock); |
291 | 357 | ||
292 | if (index >= MAX_9P_CHAN) { | 358 | if (index >= MAX_9P_CHAN) { |
293 | printk(KERN_ERR "9p: virtio: couldn't find a free channel\n"); | 359 | printk(KERN_ERR "9p: no channels available\n"); |
294 | return NULL; | 360 | return ERR_PTR(-ENODEV); |
361 | } | ||
362 | |||
363 | chan->tagpool = p9_idpool_create(); | ||
364 | if (IS_ERR(chan->tagpool)) { | ||
365 | printk(KERN_ERR "9p: couldn't allocate tagpool\n"); | ||
366 | return ERR_PTR(-ENOMEM); | ||
295 | } | 367 | } |
368 | p9_idpool_get(chan->tagpool); /* reserve tag 0 */ | ||
369 | chan->max_tag = 0; | ||
370 | chan->reqs = NULL; | ||
296 | 371 | ||
297 | trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL); | 372 | trans = kmalloc(sizeof(struct p9_trans), GFP_KERNEL); |
298 | if (!trans) { | 373 | if (!trans) { |
@@ -304,6 +379,7 @@ static struct p9_trans *p9_virtio_create(const char *devname, char *args) | |||
304 | trans->read = p9_virtio_read; | 379 | trans->read = p9_virtio_read; |
305 | trans->close = p9_virtio_close; | 380 | trans->close = p9_virtio_close; |
306 | trans->poll = p9_virtio_poll; | 381 | trans->poll = p9_virtio_poll; |
382 | trans->rpc = p9_virtio_rpc; | ||
307 | trans->priv = chan; | 383 | trans->priv = chan; |
308 | 384 | ||
309 | return trans; | 385 | return trans; |
@@ -327,7 +403,7 @@ static struct virtio_driver p9_virtio_drv = { | |||
327 | static struct p9_trans_module p9_virtio_trans = { | 403 | static struct p9_trans_module p9_virtio_trans = { |
328 | .name = "virtio", | 404 | .name = "virtio", |
329 | .create = p9_virtio_create, | 405 | .create = p9_virtio_create, |
330 | .maxsize = PAGE_SIZE, | 406 | .maxsize = PAGE_SIZE*16, |
331 | .def = 0, | 407 | .def = 0, |
332 | }; | 408 | }; |
333 | 409 | ||