Subversion Repositories HelenOS

Rev

Rev 4581 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 4581 Rev 4718
Line 61... Line 61...
61
 
61
 
62
typedef struct {
62
typedef struct {
63
    fibril_mutex_t lock;
63
    fibril_mutex_t lock;
64
    size_t block_size;      /**< Block size. */
64
    size_t block_size;      /**< Block size. */
65
    unsigned block_count;       /**< Total number of blocks. */
65
    unsigned block_count;       /**< Total number of blocks. */
-
 
66
    unsigned blocks_cached;     /**< Number of cached blocks. */
66
    hash_table_t block_hash;
67
    hash_table_t block_hash;
67
    link_t free_head;
68
    link_t free_head;
68
    enum cache_mode mode;
69
    enum cache_mode mode;
69
} cache_t;
70
} cache_t;
70
 
71
 
71
typedef struct {
72
typedef struct {
72
    link_t link;
73
    link_t link;
73
    dev_handle_t dev_handle;
74
    dev_handle_t dev_handle;
74
    int dev_phone;
75
    int dev_phone;
-
 
76
    fibril_mutex_t com_area_lock;
75
    void *com_area;
77
    void *com_area;
76
    size_t com_size;
78
    size_t com_size;
77
    void *bb_buf;
79
    void *bb_buf;
78
    off_t bb_off;
80
    off_t bb_off;
79
    size_t bb_size;
81
    size_t bb_size;
80
    cache_t *cache;
82
    cache_t *cache;
81
} devcon_t;
83
} devcon_t;
82
 
84
 
83
static int write_block(devcon_t *devcon, bn_t boff, size_t block_size,
85
static int read_block(devcon_t *devcon, bn_t boff, size_t block_size);
84
    const void *src);
86
static int write_block(devcon_t *devcon, bn_t boff, size_t block_size);
85
 
87
 
86
static devcon_t *devcon_search(dev_handle_t dev_handle)
88
static devcon_t *devcon_search(dev_handle_t dev_handle)
87
{
89
{
88
    link_t *cur;
90
    link_t *cur;
89
 
91
 
Line 110... Line 112...
110
        return ENOMEM;
112
        return ENOMEM;
111
   
113
   
112
    link_initialize(&devcon->link);
114
    link_initialize(&devcon->link);
113
    devcon->dev_handle = dev_handle;
115
    devcon->dev_handle = dev_handle;
114
    devcon->dev_phone = dev_phone;
116
    devcon->dev_phone = dev_phone;
-
 
117
    fibril_mutex_initialize(&devcon->com_area_lock);
115
    devcon->com_area = com_area;
118
    devcon->com_area = com_area;
116
    devcon->com_size = com_size;
119
    devcon->com_size = com_size;
117
    devcon->bb_buf = NULL;
120
    devcon->bb_buf = NULL;
118
    devcon->bb_off = 0;
121
    devcon->bb_off = 0;
119
    devcon->bb_size = 0;
122
    devcon->bb_size = 0;
Line 209... Line 212...
209
        return EEXIST;
212
        return EEXIST;
210
    bb_buf = malloc(size);
213
    bb_buf = malloc(size);
211
    if (!bb_buf)
214
    if (!bb_buf)
212
        return ENOMEM;
215
        return ENOMEM;
213
   
216
   
214
    off_t bufpos = 0;
-
 
215
    size_t buflen = 0;
-
 
216
    rc = block_read(dev_handle, &bufpos, &buflen, &off,
217
    fibril_mutex_lock(&devcon->com_area_lock);
217
        bb_buf, size, size);
218
    rc = read_block(devcon, 0, size);
218
    if (rc != EOK) {
219
    if (rc != EOK) {
-
 
220
        fibril_mutex_unlock(&devcon->com_area_lock);
219
            free(bb_buf);
221
            free(bb_buf);
220
        return rc;
222
        return rc;
221
    }
223
    }
-
 
224
    memcpy(bb_buf, devcon->com_area, size);
-
 
225
    fibril_mutex_unlock(&devcon->com_area_lock);
-
 
226
 
222
    devcon->bb_buf = bb_buf;
227
    devcon->bb_buf = bb_buf;
223
    devcon->bb_off = off;
228
    devcon->bb_off = off;
224
    devcon->bb_size = size;
229
    devcon->bb_size = size;
225
 
230
 
226
    return EOK;
231
    return EOK;
Line 269... Line 274...
269
   
274
   
270
    fibril_mutex_initialize(&cache->lock);
275
    fibril_mutex_initialize(&cache->lock);
271
    list_initialize(&cache->free_head);
276
    list_initialize(&cache->free_head);
272
    cache->block_size = size;
277
    cache->block_size = size;
273
    cache->block_count = blocks;
278
    cache->block_count = blocks;
-
 
279
    cache->blocks_cached = 0;
274
    cache->mode = mode;
280
    cache->mode = mode;
275
 
281
 
276
    if (!hash_table_create(&cache->block_hash, CACHE_BUCKETS, 1,
282
    if (!hash_table_create(&cache->block_hash, CACHE_BUCKETS, 1,
277
        &cache_ops)) {
283
        &cache_ops)) {
278
        free(cache);
284
        free(cache);
Line 281... Line 287...
281
 
287
 
282
    devcon->cache = cache;
288
    devcon->cache = cache;
283
    return EOK;
289
    return EOK;
284
}
290
}
285
 
291
 
-
 
292
#define CACHE_LO_WATERMARK  10  
-
 
293
#define CACHE_HI_WATERMARK  20  
286
static bool cache_can_grow(cache_t *cache)
294
static bool cache_can_grow(cache_t *cache)
287
{
295
{
-
 
296
    if (cache->blocks_cached < CACHE_LO_WATERMARK)
-
 
297
        return true;
-
 
298
    if (!list_empty(&cache->free_head))
-
 
299
        return false;
288
    return true;
300
    return true;
289
}
301
}
290
 
302
 
291
static void block_initialize(block_t *b)
303
static void block_initialize(block_t *b)
292
{
304
{
Line 313... Line 325...
313
    devcon_t *devcon;
325
    devcon_t *devcon;
314
    cache_t *cache;
326
    cache_t *cache;
315
    block_t *b;
327
    block_t *b;
316
    link_t *l;
328
    link_t *l;
317
    unsigned long key = boff;
329
    unsigned long key = boff;
-
 
330
    bn_t oboff;
318
   
331
   
319
    devcon = devcon_search(dev_handle);
332
    devcon = devcon_search(dev_handle);
320
 
333
 
321
    assert(devcon);
334
    assert(devcon);
322
    assert(devcon->cache);
335
    assert(devcon->cache);
Line 337... Line 350...
337
    } else {
350
    } else {
338
        /*
351
        /*
339
         * The block was not found in the cache.
352
         * The block was not found in the cache.
340
         */
353
         */
341
        int rc;
354
        int rc;
342
        off_t bufpos = 0;
-
 
343
        size_t buflen = 0;
-
 
344
        off_t pos = boff * cache->block_size;
-
 
345
        bool sync = false;
355
        bool sync = false;
346
 
356
 
347
        if (cache_can_grow(cache)) {
357
        if (cache_can_grow(cache)) {
348
            /*
358
            /*
349
             * We can grow the cache by allocating new blocks.
359
             * We can grow the cache by allocating new blocks.
Line 356... Line 366...
356
            b->data = malloc(cache->block_size);
366
            b->data = malloc(cache->block_size);
357
            if (!b->data) {
367
            if (!b->data) {
358
                free(b);
368
                free(b);
359
                goto recycle;
369
                goto recycle;
360
            }
370
            }
-
 
371
            cache->blocks_cached++;
361
        } else {
372
        } else {
362
            /*
373
            /*
363
             * Try to recycle a block from the free list.
374
             * Try to recycle a block from the free list.
364
             */
375
             */
365
            unsigned long temp_key;
376
            unsigned long temp_key;
366
recycle:
377
recycle:
367
            assert(!list_empty(&cache->free_head));
378
            assert(!list_empty(&cache->free_head));
368
            l = cache->free_head.next;
379
            l = cache->free_head.next;
369
            list_remove(l);
380
            list_remove(l);
370
            b = hash_table_get_instance(l, block_t, hash_link);
381
            b = list_get_instance(l, block_t, free_link);
371
            sync = b->dirty;
382
            sync = b->dirty;
-
 
383
            oboff = b->boff;
372
            temp_key = b->boff;
384
            temp_key = b->boff;
373
            hash_table_remove(&cache->block_hash, &temp_key, 1);
385
            hash_table_remove(&cache->block_hash, &temp_key, 1);
374
        }
386
        }
375
 
387
 
376
        block_initialize(b);
388
        block_initialize(b);
Line 390... Line 402...
390
        if (sync) {
402
        if (sync) {
391
            /*
403
            /*
392
             * The block is dirty and needs to be written back to
404
             * The block is dirty and needs to be written back to
393
             * the device before we can read in the new contents.
405
             * the device before we can read in the new contents.
394
             */
406
             */
395
            abort();    /* TODO: block_write() */
407
            fibril_mutex_lock(&devcon->com_area_lock);
-
 
408
            memcpy(devcon->com_area, b->data, b->size);
-
 
409
            rc = write_block(devcon, oboff, cache->block_size);
-
 
410
            assert(rc == EOK);
-
 
411
            fibril_mutex_unlock(&devcon->com_area_lock);
396
        }
412
        }
397
        if (!(flags & BLOCK_FLAGS_NOREAD)) {
413
        if (!(flags & BLOCK_FLAGS_NOREAD)) {
398
            /*
414
            /*
399
             * The block contains old or no data. We need to read
415
             * The block contains old or no data. We need to read
400
             * the new contents from the device.
416
             * the new contents from the device.
401
             */
417
             */
402
            rc = block_read(dev_handle, &bufpos, &buflen, &pos,
418
            fibril_mutex_lock(&devcon->com_area_lock);
403
                b->data, cache->block_size, cache->block_size);
419
            rc = read_block(devcon, b->boff, cache->block_size);
404
            assert(rc == EOK);
420
            assert(rc == EOK);
-
 
421
            memcpy(b->data, devcon->com_area, cache->block_size);
-
 
422
            fibril_mutex_unlock(&devcon->com_area_lock);
405
        }
423
        }
406
 
424
 
407
        fibril_mutex_unlock(&b->lock);
425
        fibril_mutex_unlock(&b->lock);
408
    }
426
    }
409
    return b;
427
    return b;
Line 427... Line 445...
427
    cache = devcon->cache;
445
    cache = devcon->cache;
428
    fibril_mutex_lock(&cache->lock);
446
    fibril_mutex_lock(&cache->lock);
429
    fibril_mutex_lock(&block->lock);
447
    fibril_mutex_lock(&block->lock);
430
    if (!--block->refcnt) {
448
    if (!--block->refcnt) {
431
        /*
449
        /*
432
         * Last reference to the block was dropped, put the block on the
450
         * Last reference to the block was dropped. Either free the
-
 
451
         * block or put it on the free list.
-
 
452
         */
-
 
453
        if (cache->blocks_cached > CACHE_HI_WATERMARK) {
-
 
454
            /*
-
 
455
             * Currently there are too many cached blocks.
-
 
456
             */
-
 
457
            if (block->dirty) {
-
 
458
                fibril_mutex_lock(&devcon->com_area_lock);
-
 
459
                memcpy(devcon->com_area, block->data,
-
 
460
                    block->size);
-
 
461
                rc = write_block(devcon, block->boff,
-
 
462
                    block->size);
-
 
463
                assert(rc == EOK);
-
 
464
                fibril_mutex_unlock(&devcon->com_area_lock);
-
 
465
            }
-
 
466
            /*
-
 
467
             * Take the block out of the cache and free it.
-
 
468
             */
-
 
469
            unsigned long key = block->boff;
-
 
470
            hash_table_remove(&cache->block_hash, &key, 1);
433
         * free list.
471
            free(block);
-
 
472
            free(block->data);
-
 
473
            cache->blocks_cached--;
-
 
474
            fibril_mutex_unlock(&cache->lock);
-
 
475
            return;
-
 
476
        }
-
 
477
        /*
-
 
478
         * Put the block on the free list.
434
         */
479
         */
435
        list_append(&block->free_link, &cache->free_head);
480
        list_append(&block->free_link, &cache->free_head);
436
        if (cache->mode != CACHE_MODE_WB && block->dirty) {
481
        if (cache->mode != CACHE_MODE_WB && block->dirty) {
-
 
482
            fibril_mutex_lock(&devcon->com_area_lock);
437
            rc = write_block(devcon, block->boff, block->size,
483
            memcpy(devcon->com_area, block->data, block->size);
438
                block->data);
484
            rc = write_block(devcon, block->boff, block->size);
439
            assert(rc == EOK);
485
            assert(rc == EOK);
-
 
486
            fibril_mutex_unlock(&devcon->com_area_lock);
440
 
487
 
441
            block->dirty = false;
488
            block->dirty = false;
442
        }
489
        }
443
    }
490
    }
444
    fibril_mutex_unlock(&block->lock);
491
    fibril_mutex_unlock(&block->lock);
445
    fibril_mutex_unlock(&cache->lock);
492
    fibril_mutex_unlock(&cache->lock);
446
}
493
}
447
 
494
 
448
/** Read data from a block device.
495
/** Read sequential data from a block device.
449
 *
496
 *
450
 * @param dev_handle    Device handle of the block device.
497
 * @param dev_handle    Device handle of the block device.
451
 * @param bufpos    Pointer to the first unread valid offset within the
498
 * @param bufpos    Pointer to the first unread valid offset within the
452
 *          communication buffer.
499
 *          communication buffer.
453
 * @param buflen    Pointer to the number of unread bytes that are ready in
500
 * @param buflen    Pointer to the number of unread bytes that are ready in
Line 457... Line 504...
457
 * @param size      Size of the destination buffer.
504
 * @param size      Size of the destination buffer.
458
 * @param block_size    Block size to be used for the transfer.
505
 * @param block_size    Block size to be used for the transfer.
459
 *
506
 *
460
 * @return      EOK on success or a negative return code on failure.
507
 * @return      EOK on success or a negative return code on failure.
461
 */
508
 */
462
int
-
 
463
block_read(dev_handle_t dev_handle, off_t *bufpos, size_t *buflen, off_t *pos,
509
int block_seqread(dev_handle_t dev_handle, off_t *bufpos, size_t *buflen,
464
    void *dst, size_t size, size_t block_size)
510
    off_t *pos, void *dst, size_t size, size_t block_size)
465
{
511
{
466
    off_t offset = 0;
512
    off_t offset = 0;
467
    size_t left = size;
513
    size_t left = size;
468
    devcon_t *devcon = devcon_search(dev_handle);
514
    devcon_t *devcon = devcon_search(dev_handle);
469
    assert(devcon);
515
    assert(devcon);
470
   
516
   
-
 
517
    fibril_mutex_lock(&devcon->com_area_lock);
471
    while (left > 0) {
518
    while (left > 0) {
472
        size_t rd;
519
        size_t rd;
473
       
520
       
474
        if (*bufpos + left < *buflen)
521
        if (*bufpos + left < *buflen)
475
            rd = left;
522
            rd = left;
Line 488... Line 535...
488
            left -= rd;
535
            left -= rd;
489
        }
536
        }
490
       
537
       
491
        if (*bufpos == (off_t) *buflen) {
538
        if (*bufpos == (off_t) *buflen) {
492
            /* Refill the communication buffer with a new block. */
539
            /* Refill the communication buffer with a new block. */
493
            ipcarg_t retval;
540
            int rc;
494
            int rc = async_req_2_1(devcon->dev_phone, BD_READ_BLOCK,
-
 
-
 
541
 
495
                *pos / block_size, block_size, &retval);
542
            rc = read_block(devcon, *pos / block_size, block_size);
496
            if ((rc != EOK) || (retval != EOK))
543
            if (rc != EOK) {
497
                return (rc != EOK ? rc : (int) retval);
544
                fibril_mutex_unlock(&devcon->com_area_lock);
-
 
545
                return rc;
-
 
546
            }
498
           
547
           
499
            *bufpos = 0;
548
            *bufpos = 0;
500
            *buflen = block_size;
549
            *buflen = block_size;
501
        }
550
        }
502
    }
551
    }
-
 
552
    fibril_mutex_unlock(&devcon->com_area_lock);
503
   
553
   
504
    return EOK;
554
    return EOK;
505
}
555
}
506
 
556
 
-
 
557
/** Read block from block device.
-
 
558
 *
-
 
559
 * @param devcon    Device connection.
-
 
560
 * @param boff      Block index.
-
 
561
 * @param block_size    Block size.
-
 
562
 * @param src       Buffer for storing the data.
-
 
563
 *
-
 
564
 * @return      EOK on success or negative error code on failure.
-
 
565
 */
-
 
566
static int read_block(devcon_t *devcon, bn_t boff, size_t block_size)
-
 
567
{
-
 
568
    ipcarg_t retval;
-
 
569
    int rc;
-
 
570
 
-
 
571
    assert(devcon);
-
 
572
    rc = async_req_2_1(devcon->dev_phone, BD_READ_BLOCK, boff, block_size,
-
 
573
        &retval);
-
 
574
    if ((rc != EOK) || (retval != EOK))
-
 
575
        return (rc != EOK ? rc : (int) retval);
-
 
576
 
-
 
577
    return EOK;
-
 
578
}
-
 
579
 
507
/** Write block to block device.
580
/** Write block to block device.
508
 *
581
 *
509
 * @param devcon    Device connection.
582
 * @param devcon    Device connection.
510
 * @param boff      Block index.
583
 * @param boff      Block index.
511
 * @param block_size    Block size.
584
 * @param block_size    Block size.
512
 * @param src       Buffer containing the data to write.
585
 * @param src       Buffer containing the data to write.
513
 *
586
 *
514
 * @return      EOK on success or negative error code on failure.
587
 * @return      EOK on success or negative error code on failure.
515
 */
588
 */
516
static int write_block(devcon_t *devcon, bn_t boff, size_t block_size,
589
static int write_block(devcon_t *devcon, bn_t boff, size_t block_size)
517
    const void *src)
-
 
518
{
590
{
519
    ipcarg_t retval;
591
    ipcarg_t retval;
520
    int rc;
592
    int rc;
521
 
593
 
522
    assert(devcon);
594
    assert(devcon);
523
    memcpy(devcon->com_area, src, block_size);
-
 
524
   
-
 
525
    rc = async_req_2_1(devcon->dev_phone, BD_WRITE_BLOCK,
595
    rc = async_req_2_1(devcon->dev_phone, BD_WRITE_BLOCK, boff, block_size,
526
        boff, block_size, &retval);
596
        &retval);
527
    if ((rc != EOK) || (retval != EOK))
597
    if ((rc != EOK) || (retval != EOK))
528
        return (rc != EOK ? rc : (int) retval);
598
        return (rc != EOK ? rc : (int) retval);
529
 
599
 
530
    return EOK;
600
    return EOK;
531
}
601
}