Statistics
| Branch: | Revision:

root / drivers / block-vindex.c @ abdb293f

History | View | Annotate | Download (20.8 kB)

1
/*
2
 * Copyright (c) 2008, XenSource Inc.
3
 * Copyright (c) 2010, Citrix Systems, Inc.
4
 *
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions are met:
9
 *     * Redistributions of source code must retain the above copyright
10
 *       notice, this list of conditions and the following disclaimer.
11
 *     * Redistributions in binary form must reproduce the above copyright
12
 *       notice, this list of conditions and the following disclaimer in the
13
 *       documentation and/or other materials provided with the distribution.
14
 *     * Neither the name of XenSource Inc. nor the names of its contributors
15
 *       may be used to endorse or promote products derived from this software
16
 *       without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 */
30

    
31
#ifdef HAVE_CONFIG_H
32
#include "config.h"
33
#endif
34

    
35
#include <errno.h>
36
#include <fcntl.h>
37
#include <unistd.h>
38
#include <stdlib.h>
39

    
40
#include "tapdisk.h"
41
#include "tapdisk-utils.h"
42
#include "tapdisk-driver.h"
43
#include "tapdisk-server.h"
44
#include "tapdisk-interface.h"
45

    
46
#include "libvhd.h"
47
#include "libvhd-index.h"
48

    
49
#define DBG(_level, _f, _a...)       tlog_write(_level, _f, ##_a)
50
#define ERR(_err, _f, _a...)         tlog_error(_err, _f, ##_a)
51
#define WARN(_f, _a...)              tlog_write(TLOG_WARN, _f, ##_a)
52

    
53
#define ASSERT(condition)                                        \
54
        if (!(condition)) {                                        \
55
                WARN("FAILED ASSERTION: '%s'\n", #condition);        \
56
                td_panic();                                        \
57
        }
58

    
59
#define VHD_INDEX_FILE_POOL_SIZE     12
60
#define VHD_INDEX_CACHE_SIZE         4
61
#define VHD_INDEX_REQUESTS           (TAPDISK_DATA_REQUESTS + VHD_INDEX_CACHE_SIZE)
62

    
63
#define VHD_INDEX_BLOCK_READ_PENDING 0x0001
64
#define VHD_INDEX_BLOCK_VALID        0x0002
65

    
66
#define VHD_INDEX_BAT_CLEAR          0
67
#define VHD_INDEX_BIT_CLEAR          1
68
#define VHD_INDEX_BIT_SET            2
69
#define VHD_INDEX_CACHE_MISS         3
70
#define VHD_INDEX_META_READ_PENDING  4
71

    
72
typedef struct vhd_index             vhd_index_t;
73
typedef struct vhd_index_block       vhd_index_block_t;
74
typedef struct vhd_index_request     vhd_index_request_t;
75
typedef struct vhd_index_file_ref    vhd_index_file_ref_t;
76

    
77
struct vhd_index_request {
78
        off64_t                      off;
79
        td_request_t                 treq;
80
        vhd_index_t                 *index;
81
        struct tiocb                 tiocb;
82
        struct list_head             next;
83
        vhd_index_file_ref_t        *file;
84
};
85

    
86
struct vhd_index_block {
87
        uint64_t                     blk;
88
        uint32_t                     seqno;
89
        td_flag_t                    state;
90
        vhdi_block_t                 vhdi_block;
91
        int                          table_size;
92
        struct list_head             queue;
93
        vhd_index_request_t          req;
94
};
95

    
96
struct vhd_index_file_ref {
97
        int                          fd;
98
        vhdi_file_id_t               fid;
99
        uint32_t                     seqno;
100
        uint32_t                     refcnt;
101
};
102

    
103
struct vhd_index {
104
        char                        *name;
105

    
106
        vhdi_bat_t                   bat;
107
        vhdi_context_t               vhdi;
108
        vhdi_file_table_t            files;
109

    
110
        vhd_index_file_ref_t         fds[VHD_INDEX_FILE_POOL_SIZE];
111

    
112
        vhd_index_block_t           *cache[VHD_INDEX_CACHE_SIZE];
113

    
114
        int                          cache_free_cnt;
115
        vhd_index_block_t           *cache_free_list[VHD_INDEX_CACHE_SIZE];
116
        vhd_index_block_t            cache_list[VHD_INDEX_CACHE_SIZE];
117

    
118
        int                          requests_free_cnt;
119
        vhd_index_request_t         *requests_free_list[VHD_INDEX_REQUESTS];
120
        vhd_index_request_t          requests_list[VHD_INDEX_REQUESTS];
121

    
122
        td_driver_t                 *driver;
123
};
124

    
125
static void vhd_index_complete_meta_read(void *, struct tiocb *, int);
126
static void vhd_index_complete_data_read(void *, struct tiocb *, int);
127

    
128
#define vhd_index_block_for_each_request(_block, _req, _tmp)                \
129
        list_for_each_entry_safe((_req), (_tmp), &(_block)->queue, next)
130

    
131
static inline void
132
vhd_index_initialize_request(vhd_index_request_t *req)
133
{
134
        memset(req, 0, sizeof(vhd_index_request_t));
135
        INIT_LIST_HEAD(&req->next);
136
}
137

    
138
static inline void
139
vhd_index_initialize_block(vhd_index_block_t *block)
140
{
141
        block->blk   = 0;
142
        block->state = 0;
143
        INIT_LIST_HEAD(&block->queue);
144
        vhd_index_initialize_request(&block->req);
145
        memset(block->vhdi_block.table, 0, block->table_size);
146
}
147

    
148
static void
149
vhd_index_init(vhd_index_t *index)
150
{
151
        int i;
152

    
153
        memset(index, 0, sizeof(vhd_index_t));
154

    
155
        index->cache_free_cnt = VHD_INDEX_CACHE_SIZE;
156
        for (i = 0; i < VHD_INDEX_CACHE_SIZE; i++) {
157
                index->cache_free_list[i] = index->cache_list + i;
158
                vhd_index_initialize_block(index->cache_free_list[i]);
159
        }
160

    
161
        index->requests_free_cnt = VHD_INDEX_REQUESTS;
162
        for (i = 0; i < VHD_INDEX_REQUESTS; i++) {
163
                index->requests_free_list[i] = index->requests_list + i;
164
                vhd_index_initialize_request(index->requests_free_list[i]);
165
        }
166

    
167
        for (i = 0; i < VHD_INDEX_FILE_POOL_SIZE; i++)
168
                index->fds[i].fd = -1;
169
}
170

    
171
static int
172
vhd_index_allocate_cache(vhd_index_t *index)
173
{
174
        void *buf;
175
        int i, err;
176
        size_t size;
177

    
178
        size = vhd_bytes_padded(index->vhdi.spb * sizeof(vhdi_entry_t));
179

    
180
        for (i = 0; i < VHD_INDEX_CACHE_SIZE; i++) {
181
                err = posix_memalign(&buf, VHD_SECTOR_SIZE, size);
182
                if (err)
183
                        goto fail;
184

    
185
                memset(buf, 0, size);
186
                index->cache_list[i].vhdi_block.table   = (vhdi_entry_t *)buf;
187
                index->cache_list[i].vhdi_block.entries = index->vhdi.spb;
188
                index->cache_list[i].table_size         = size;
189
        }
190

    
191
        return 0;
192

    
193
fail:
194
        for (i = 0; i < VHD_INDEX_CACHE_SIZE; i++) {
195
                free(index->cache_list[i].vhdi_block.table);
196
                index->cache_list[i].vhdi_block.table = NULL;
197
        }
198

    
199
        return -ENOMEM;
200
}
201

    
202
static void
203
vhd_index_free(vhd_index_t *index)
204
{
205
        int i;
206

    
207
        for (i = 0; i < VHD_INDEX_CACHE_SIZE; i++)
208
                free(index->cache_list[i].vhdi_block.table);
209

    
210
        for (i = 0; i < VHD_INDEX_FILE_POOL_SIZE; i++)
211
                if (index->fds[i].fd != -1)
212
                        close(index->fds[i].fd);
213

    
214
        vhdi_file_table_free(&index->files);
215
        free(index->bat.table);
216
        free(index->name);
217
}
218

    
219
static int
220
vhd_index_load(vhd_index_t *index)
221
{
222
        int err;
223

    
224
        err = vhdi_bat_load(index->name, &index->bat);
225
        if (err)
226
                return err;
227

    
228
        err = vhdi_open(&index->vhdi,
229
                        index->bat.index_path,
230
                        O_RDONLY | O_DIRECT | O_LARGEFILE);
231
        if (err)
232
                goto fail;
233

    
234
        err = vhdi_file_table_load(index->bat.file_table_path, &index->files);
235
        if (err) {
236
                vhdi_close(&index->vhdi);
237
                goto fail;
238
        }
239

    
240
        return 0;
241

    
242
fail:
243
        free(index->bat.table);
244
        memset(&index->bat, 0, sizeof(vhdi_bat_t));
245
        memset(&index->vhdi, 0, sizeof(vhdi_context_t));
246
        memset(&index->files, 0, sizeof(vhdi_file_table_t));
247
        return err;
248
}
249

    
250
static int
251
vhd_index_open(td_driver_t *driver, const char *name, td_flag_t flags)
252
{
253
        int err;
254
        vhd_index_t *index;
255

    
256
        index = (vhd_index_t *)driver->data;
257

    
258
        vhd_index_init(index);
259

    
260
        index->name = strdup(name);
261
        if (!index->name)
262
                return -ENOMEM;
263

    
264
        err = vhd_index_load(index);
265
        if (err) {
266
                free(index->name);
267
                return err;
268
        }
269

    
270
        err = vhd_index_allocate_cache(index);
271
        if (err) {
272
                vhd_index_free(index);
273
                return err;
274
        }
275

    
276
        driver->info.size = index->bat.vhd_blocks * index->bat.vhd_block_size;
277
        driver->info.sector_size = VHD_SECTOR_SIZE;
278
        driver->info.info = 0;
279

    
280
        index->driver = driver;
281

    
282
        DPRINTF("opened vhd index %s\n", name);
283

    
284
        return 0;
285
}
286

    
287
static int
288
vhd_index_close(td_driver_t *driver)
289
{
290
        vhd_index_t *index;
291

    
292
        index = (vhd_index_t *)driver->data;
293
        vhdi_close(&index->vhdi);
294

    
295
        DPRINTF("closed vhd index %s\n", index->name);
296

    
297
        vhd_index_free(index);
298

    
299
        return 0;
300
}
301

    
302
static inline void
303
vhd_index_touch_file_ref(vhd_index_t *index, vhd_index_file_ref_t *ref)
304
{
305
        int i;
306

    
307
        if (++ref->seqno == 0xFFFFFFFF)
308
                for (i = 0; i < VHD_INDEX_FILE_POOL_SIZE; i++)
309
                        index->fds[i].seqno >>= 1;
310
}
311

    
312
static inline void
313
vhd_index_get_file_ref(vhd_index_file_ref_t *ref)
314
{
315
        ++ref->refcnt;
316
}
317

    
318
static inline void
319
vhd_index_put_file_ref(vhd_index_file_ref_t *ref)
320
{
321
        --ref->refcnt;
322
}
323

    
324
static inline vhd_index_file_ref_t *
325
vhd_index_find_lru_file_ref(vhd_index_t *index)
326
{
327
        int i;
328
        uint32_t min;
329
        vhd_index_file_ref_t *lru;
330

    
331
        lru = NULL;
332
        min = (uint32_t)-1;
333

    
334
        for (i = 1; i < VHD_INDEX_FILE_POOL_SIZE; i++) {
335
                if (index->fds[i].refcnt)
336
                        continue;
337

    
338
                if (!lru || index->fds[i].seqno < min) {
339
                        min = index->fds[i].seqno;
340
                        lru = index->fds + i;
341
                }
342
        }
343

    
344
        return lru;
345
}
346

    
347
static inline int
348
vhd_index_open_file(vhd_index_t *index,
349
                    vhdi_file_id_t id, vhd_index_file_ref_t *ref)
350
{
351
        int i;
352
        char *path;
353

    
354
        path = NULL;
355

    
356
        for (i = 0; i < index->files.entries; i++)
357
                if (index->files.table[i].file_id == id) {
358
                        path = index->files.table[i].path;
359
                        break;
360
                }
361

    
362
        if (!path)
363
                return -ENOENT;
364

    
365
        ref->fd = open(path, O_RDONLY | O_DIRECT | O_LARGEFILE);
366
        if (ref->fd == -1)
367
                return -errno;
368

    
369
        ref->fid    = id;
370
        ref->refcnt = 0;
371

    
372
        return 0;
373
}
374

    
375
static int
376
vhd_index_get_file(vhd_index_t *index,
377
                   vhdi_file_id_t id, vhd_index_file_ref_t **ref)
378
{
379
        int i, err;
380
        vhd_index_file_ref_t *lru;
381

    
382
        *ref = NULL;
383

    
384
        for (i = 0; i < VHD_INDEX_FILE_POOL_SIZE; i++)
385
                if (id == index->fds[i].fid) {
386
                        *ref = index->fds + i;
387
                        vhd_index_touch_file_ref(index, *ref);
388
                        vhd_index_get_file_ref(*ref);
389
                        return 0;
390
                }
391

    
392
        lru = vhd_index_find_lru_file_ref(index);
393
        if (!lru)
394
                return -EBUSY;
395

    
396
        if (lru->fd != -1)
397
                close(lru->fd);
398

    
399
        err = vhd_index_open_file(index, id, lru);
400
        if (err)
401
                goto fail;
402

    
403
        vhd_index_touch_file_ref(index, lru);
404
        vhd_index_get_file_ref(lru);
405
        *ref = lru;
406
        return 0;
407

    
408
fail:
409
        lru->fd     = -1;
410
        lru->fid    = 0;
411
        lru->refcnt = 0;
412
        return err;
413
}
414

    
415
static inline vhd_index_request_t *
416
vhd_index_allocate_request(vhd_index_t *index)
417
{
418
        vhd_index_request_t *req;
419

    
420
        if (index->requests_free_cnt <= 0)
421
                return NULL;
422

    
423
        req = index->requests_free_list[--index->requests_free_cnt];
424
        ASSERT(!req->index);
425

    
426
        return req;
427
}
428

    
429
static inline void
430
vhd_index_free_request(vhd_index_t *index, vhd_index_request_t *req)
431
{
432
        list_del(&req->next);
433
        vhd_index_initialize_request(req);
434
        index->requests_free_list[index->requests_free_cnt++] = req;
435
}
436

    
437
static inline int
438
vhd_index_block_valid(vhd_index_block_t *block)
439
{
440
        return (!td_flag_test(block->state, VHD_INDEX_BLOCK_READ_PENDING) &&
441
                td_flag_test(block->state, VHD_INDEX_BLOCK_VALID));
442
}
443

    
444
static inline void
445
vhd_index_touch_block(vhd_index_t *index, vhd_index_block_t *block)
446
{
447
        int i;
448

    
449
        if (++block->seqno == 0xFFFFFFFF)
450
                for (i = 0; i < VHD_INDEX_CACHE_SIZE; i++)
451
                        index->cache_list[i].seqno >>= 1;
452
}
453

    
454
static inline vhd_index_block_t *
455
vhd_index_get_lru_block(vhd_index_t *index)
456
{
457
        int i, idx;
458
        uint32_t min;
459
        vhd_index_block_t *block, *lru;
460

    
461
        lru = NULL;
462
        min = (uint32_t)-1;
463
        idx = 0;
464

    
465
        for (i = 0; i < VHD_INDEX_CACHE_SIZE; i++) {
466
                block = index->cache[i];
467

    
468
                if (!block)
469
                        continue;
470

    
471
                if (td_flag_test(block->state, VHD_INDEX_BLOCK_READ_PENDING))
472
                        continue;
473

    
474
                if (!lru || block->seqno < min) {
475
                        lru = block;
476
                        min = block->seqno;
477
                        idx = i;
478
                }
479
        }
480

    
481
        if (lru)
482
                index->cache[idx] = NULL;
483

    
484
        return lru;
485
}
486

    
487
static inline int
488
vhd_index_allocate_block(vhd_index_t *index, vhd_index_block_t **block)
489
{
490
        vhd_index_block_t *b;
491

    
492
        *block = NULL;
493

    
494
        if (index->cache_free_cnt > 0)
495
                b = index->cache_free_list[--index->cache_free_cnt];
496
        else {
497
                b = vhd_index_get_lru_block(index);
498
                if (!b)
499
                        return -EBUSY;
500
        }
501

    
502
        vhd_index_initialize_block(b);
503
        vhd_index_touch_block(index, b);
504
        *block = b;
505

    
506
        return 0;
507
}
508

    
509
static int
510
vhd_index_install_block(vhd_index_t *index,
511
                        vhd_index_block_t **block, uint32_t blk)
512
{
513
        int i, err;
514
        vhd_index_block_t *b;
515

    
516
        *block = NULL;
517

    
518
        err = vhd_index_allocate_block(index, &b);
519
        if (err)
520
                return err;
521

    
522
        b->blk = blk;
523

    
524
        for (i = 0; i < VHD_INDEX_CACHE_SIZE; i++)
525
                if (!index->cache[i]) {
526
                        index->cache[i] = b;
527
                        break;
528
                }
529

    
530
        ASSERT(i < VHD_INDEX_CACHE_SIZE);
531
        *block = b;
532

    
533
        return 0;
534
}
535

    
536
static inline vhd_index_block_t *
537
vhd_index_get_block(vhd_index_t *index, uint32_t blk)
538
{
539
        int i;
540
        vhd_index_block_t *block;
541

    
542
        for (i = 0; i < VHD_INDEX_CACHE_SIZE; i++) {
543
                block = index->cache[i];
544
                if (!block)
545
                        continue;
546

    
547
                if (block->blk == blk)
548
                        return block;
549
        }
550

    
551
        return NULL;
552
}
553

    
554
static int
555
vhd_index_read_cache(vhd_index_t *index, uint64_t sector)
556
{
557
        uint32_t blk, sec;
558
        vhd_index_block_t *block;
559

    
560
        blk = sector / index->vhdi.spb;
561

    
562
        if (blk >= index->bat.vhd_blocks)
563
                return -EINVAL;
564

    
565
        if (index->bat.table[blk] == DD_BLK_UNUSED)
566
                return VHD_INDEX_BAT_CLEAR;
567

    
568
        block = vhd_index_get_block(index, blk);
569
        if (!block)
570
                return VHD_INDEX_CACHE_MISS;
571

    
572
        vhd_index_touch_block(index, block);
573

    
574
        if (td_flag_test(block->state, VHD_INDEX_BLOCK_READ_PENDING))
575
                return VHD_INDEX_META_READ_PENDING;
576

    
577
        sec = sector % index->vhdi.spb;
578
        if (block->vhdi_block.table[sec].offset == DD_BLK_UNUSED)
579
                return VHD_INDEX_BIT_CLEAR;
580

    
581
        return VHD_INDEX_BIT_SET;
582
}
583

    
584
static int
585
vhd_index_read_cache_span(vhd_index_t *index,
586
                          uint64_t sector, int secs, int value)
587
{
588
        int i;
589
        uint32_t blk, sec;
590
        vhd_index_block_t *block;
591

    
592
        blk = sector / index->vhdi.spb;
593
        sec = sector % index->vhdi.spb;
594

    
595
        ASSERT(blk < index->bat.vhd_blocks);
596

    
597
        block = vhd_index_get_block(index, blk);
598
        ASSERT(block && vhd_index_block_valid(block));
599

    
600
        for (i = 0; i < secs && i + sec < index->vhdi.spb; i++)
601
                if (value ^
602
                    (block->vhdi_block.table[sec + i].offset != DD_BLK_UNUSED))
603
                        break;
604

    
605
        return i;
606
}
607

    
608
static int
609
vhd_index_schedule_meta_read(vhd_index_t *index, uint32_t blk)
610
{
611
        int err;
612
        off64_t offset;
613
        vhd_index_block_t *block;
614
        vhd_index_request_t *req;
615

    
616
        ASSERT(index->bat.table[blk] != DD_BLK_UNUSED);
617

    
618
        block = vhd_index_get_block(index, blk);
619
        if (!block) {
620
                err = vhd_index_install_block(index, &block, blk);
621
                if (err)
622
                        return err;
623
        }
624

    
625
        offset         = vhd_sectors_to_bytes(index->bat.table[blk]);
626

    
627
        req            = &block->req;
628
        req->index     = index;
629
        req->treq.sec  = blk * index->vhdi.spb;
630
        req->treq.secs = block->table_size >> VHD_SECTOR_SHIFT;
631

    
632
        td_prep_read(&req->tiocb, index->vhdi.fd,
633
                     (char *)block->vhdi_block.table, block->table_size,
634
                     offset, vhd_index_complete_meta_read, req);
635
        td_queue_tiocb(index->driver, &req->tiocb);
636

    
637
        td_flag_set(block->state, VHD_INDEX_BLOCK_READ_PENDING);
638

    
639
        return 0;
640
}
641

    
642
static int
643
vhd_index_schedule_data_read(vhd_index_t *index, td_request_t treq)
644
{
645
        int i, err;
646
        size_t size;
647
        off64_t offset;
648
        uint32_t blk, sec;
649
        vhd_index_block_t *block;
650
        vhd_index_request_t *req;
651
        vhd_index_file_ref_t *file;
652

    
653
        blk   = treq.sec / index->vhdi.spb;
654
        sec   = treq.sec % index->vhdi.spb;
655
        block = vhd_index_get_block(index, blk);
656

    
657
        ASSERT(block && vhd_index_block_valid(block));
658
        for (i = 0; i < treq.secs; i++) {
659
                ASSERT(block->vhdi_block.table[sec + i].file_id != 0);
660
                ASSERT(block->vhdi_block.table[sec + i].offset != DD_BLK_UNUSED);
661
        }
662

    
663
        req = vhd_index_allocate_request(index);
664
        if (!req)
665
                return -EBUSY;
666

    
667
        err = vhd_index_get_file(index,
668
                                 block->vhdi_block.table[sec].file_id, &file);
669
        if (err) {
670
                vhd_index_free_request(index, req);
671
                return err;
672
        }
673

    
674
        size       = vhd_sectors_to_bytes(treq.secs);
675
        offset     = vhd_sectors_to_bytes(block->vhdi_block.table[sec].offset);
676

    
677
        req->file  = file;
678
        req->treq  = treq;
679
        req->index = index;
680
        req->off   = offset;
681

    
682
        td_prep_read(&req->tiocb, file->fd, treq.buf, size, offset,
683
                     vhd_index_complete_data_read, req);
684
        td_queue_tiocb(index->driver, &req->tiocb);
685

    
686
        return 0;
687
}
688

    
689
static int
690
vhd_index_queue_request(vhd_index_t *index, td_request_t treq)
691
{
692
        vhd_index_block_t *block;
693
        vhd_index_request_t *req;
694

    
695
        req = vhd_index_allocate_request(index);
696
        if (!req)
697
                return -EBUSY;
698

    
699
        req->treq = treq;
700

    
701
        block = vhd_index_get_block(index, treq.sec / index->vhdi.spb);
702
        ASSERT(block && td_flag_test(block->state, VHD_INDEX_BLOCK_READ_PENDING));
703

    
704
        list_add_tail(&req->next, &block->queue);
705
        return 0;
706
}
707

    
708
static void
709
vhd_index_queue_read(td_driver_t *driver, td_request_t treq)
710
{
711
        vhd_index_t *index;
712

    
713
        index = (vhd_index_t *)driver->data;
714

    
715
        while (treq.secs) {
716
                int err;
717
                td_request_t clone;
718

    
719
                err   = 0;
720
                clone = treq;
721

    
722
                switch (vhd_index_read_cache(index, clone.sec)) {
723
                case -EINVAL:
724
                        err = -EINVAL;
725
                        goto fail;
726

    
727
                case VHD_INDEX_BAT_CLEAR:
728
                        clone.secs = MIN(clone.secs, index->vhdi.spb - (clone.sec % index->vhdi.spb));
729
                        td_forward_request(clone);
730
                        break;
731

    
732
                case VHD_INDEX_BIT_CLEAR:
733
                        clone.secs = vhd_index_read_cache_span(index, clone.sec, clone.secs, 0);
734
                        td_forward_request(clone);
735
                        break;
736

    
737
                case VHD_INDEX_BIT_SET:
738
                        clone.secs = vhd_index_read_cache_span(index, clone.sec, clone.secs, 1);
739
                        err = vhd_index_schedule_data_read(index, clone);
740
                        if (err)
741
                                goto fail;
742
                        break;
743

    
744
                case VHD_INDEX_CACHE_MISS:
745
                        err = vhd_index_schedule_meta_read(index, clone.sec / index->vhdi.spb);
746
                        if (err)
747
                                goto fail;
748

    
749
                        clone.secs = MIN(clone.secs, index->vhdi.spb - (clone.sec % index->vhdi.spb));
750
                        vhd_index_queue_request(index, clone);
751
                        break;
752

    
753
                case VHD_INDEX_META_READ_PENDING:
754
                        clone.secs = MIN(clone.secs, index->vhdi.spb - (clone.sec % index->vhdi.spb));
755
                        err = vhd_index_queue_request(index, clone);
756
                        if (err)
757
                                goto fail;
758
                        break;
759
                }
760

    
761
                treq.sec  += clone.secs;
762
                treq.secs -= clone.secs;
763
                treq.buf  += vhd_sectors_to_bytes(clone.secs);
764
                continue;
765

    
766
        fail:
767
                clone.secs = treq.secs;
768
                td_complete_request(clone, err);
769
                break;
770
        }
771
}
772

    
773
static void
774
vhd_index_queue_write(td_driver_t *driver, td_request_t treq)
775
{
776
        td_complete_request(treq, -EPERM);
777
}
778

    
779
static inline void
780
vhd_index_signal_completion(vhd_index_t *index,
781
                            vhd_index_request_t *req, int err)
782
{
783
        td_complete_request(req->treq, err);
784
        vhd_index_put_file_ref(req->file);
785
        vhd_index_free_request(index, req);
786
}
787

    
788
static void
789
vhd_index_complete_meta_read(void *arg, struct tiocb *tiocb, int err)
790
{
791
        int i;
792
        uint32_t blk;
793
        td_request_t treq;
794
        vhd_index_t *index;
795
        vhd_index_block_t *block;
796
        vhd_index_request_t *req, *r, *tmp;
797

    
798
        req   = (vhd_index_request_t *)arg;
799
        index = req->index;
800

    
801
        blk   = req->treq.sec / index->vhdi.spb;
802
        block = vhd_index_get_block(index, blk);
803
        ASSERT(block && td_flag_test(block->state, VHD_INDEX_BLOCK_READ_PENDING));
804
        td_flag_clear(block->state, VHD_INDEX_BLOCK_READ_PENDING);
805

    
806
        if (err) {
807
                memset(block->vhdi_block.table, 0, block->table_size);
808
                vhd_index_block_for_each_request(block, r, tmp)
809
                        vhd_index_signal_completion(index, r, err);
810
                return;
811
        }
812

    
813
        for (i = 0; i < block->vhdi_block.entries; i++)
814
                vhdi_entry_in(block->vhdi_block.table + i);
815

    
816
        td_flag_set(block->state, VHD_INDEX_BLOCK_VALID);
817

    
818
        vhd_index_block_for_each_request(block, r, tmp) {
819
                treq = r->treq;
820
                vhd_index_free_request(index, r);
821
                vhd_index_queue_read(index->driver, treq);
822
        }
823
}
824

    
825
static void
826
vhd_index_complete_data_read(void *arg, struct tiocb *tiocb, int err)
827
{
828
        vhd_index_t *index;
829
        vhd_index_request_t *req;
830

    
831
        req   = (vhd_index_request_t *)arg;
832
        index = req->index;
833

    
834
        vhd_index_signal_completion(index, req, err);
835
}
836

    
837
static int
838
vhd_index_get_parent_id(td_driver_t *driver, td_disk_id_t *id)
839
{
840
        return -EINVAL;
841
}
842

    
843
static int
844
vhd_index_validate_parent(td_driver_t *driver,
845
                          td_driver_t *parent, td_flag_t flags)
846
{
847
        return -EINVAL;
848
}
849

    
850
static void
851
vhd_index_debug(td_driver_t *driver)
852
{
853
        int i;
854
        vhd_index_t *index;
855

    
856
        index = (vhd_index_t *)driver->data;
857

    
858
        WARN("VHD INDEX %s\n", index->name);
859
        WARN("FILES:\n");
860
        for (i = 0; i < index->files.entries; i++) {
861
                int j, fd, refcnt;
862

    
863
                fd     = -1;
864
                refcnt = 0;
865

    
866
                for (j = 0; j < VHD_INDEX_FILE_POOL_SIZE; j++)
867
                        if (index->fds[j].fid == index->files.table[i].file_id) {
868
                                fd     = index->fds[j].fd;
869
                                refcnt = index->fds[j].refcnt;
870
                        }
871

    
872
                WARN("%s %u %d %d\n",
873
                     index->files.table[i].path,
874
                     index->files.table[i].file_id,
875
                     fd, refcnt);
876
        }
877

    
878
        WARN("REQUESTS:\n");
879
        for (i = 0; i < VHD_INDEX_REQUESTS; i++) {
880
                vhd_index_request_t *req;
881

    
882
                req = index->requests_list + i;
883

    
884
                if (!req->index)
885
                        continue;
886

    
887
                WARN("%d: buf: %p, sec: 0x%08"PRIx64", secs: 0x%04x, "
888
                     "fid: %u, off: 0x%016"PRIx64"\n", i, req->treq.buf,
889
                     req->treq.sec, req->treq.secs, req->file->fid, req->off);
890
        }
891

    
892
        WARN("BLOCKS:\n");
893
        for (i = 0; i < VHD_INDEX_CACHE_SIZE; i++) {
894
                int queued;
895
                vhd_index_block_t *block;
896
                vhd_index_request_t *req, *tmp;
897

    
898
                queued = 0;
899
                block  = index->cache[i];
900

    
901
                if (!block)
902
                        continue;
903

    
904
                vhd_index_block_for_each_request(block, req, tmp)
905
                        ++queued;
906

    
907
                WARN("%d: blk: 0x%08"PRIx64", state: 0x%08x, queued: %d\n",
908
                     i, block->blk, block->state, queued);
909
        }
910
}
911

    
912
struct tap_disk tapdisk_vhd_index = {
913
        .disk_type                = "tapdisk_vhd_index",
914
        .flags                    = 0,
915
        .private_data_size        = sizeof(vhd_index_t),
916
        .td_open                  = vhd_index_open,
917
        .td_close                 = vhd_index_close,
918
        .td_queue_read            = vhd_index_queue_read,
919
        .td_queue_write           = vhd_index_queue_write,
920
        .td_get_parent_id         = vhd_index_get_parent_id,
921
        .td_validate_parent       = vhd_index_validate_parent,
922
        .td_debug                 = vhd_index_debug,
923
};