Subversion Repositories HelenOS

Rev

Rev 4420 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright (c) 2008 Jakub Jermar
  3.  * Copyright (c) 2008 Martin Decky
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions
  8.  * are met:
  9.  *
  10.  * - Redistributions of source code must retain the above copyright
  11.  *   notice, this list of conditions and the following disclaimer.
  12.  * - Redistributions in binary form must reproduce the above copyright
  13.  *   notice, this list of conditions and the following disclaimer in the
  14.  *   documentation and/or other materials provided with the distribution.
  15.  * - The name of the author may not be used to endorse or promote products
  16.  *   derived from this software without specific prior written permission.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  19.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  20.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  21.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  22.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  23.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  24.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  25.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28.  */
  29.  
  30. /** @addtogroup libblock
  31.  * @{
  32.  */
  33. /**
  34.  * @file
  35.  * @brief
  36.  */
  37.  
  38. #include "libblock.h"
  39. #include "../../srv/vfs/vfs.h"
  40. #include <ipc/devmap.h>
  41. #include <ipc/bd.h>
  42. #include <ipc/services.h>
  43. #include <errno.h>
  44. #include <sys/mman.h>
  45. #include <async.h>
  46. #include <ipc/ipc.h>
  47. #include <as.h>
  48. #include <assert.h>
  49. #include <futex.h>
  50. #include <libadt/list.h>
  51. #include <libadt/hash_table.h>
  52.  
  53. /** Lock protecting the device connection list */
  54. static futex_t dcl_lock = FUTEX_INITIALIZER;
  55. /** Device connection list head. */
  56. static LIST_INITIALIZE(dcl_head);
  57.  
  58. #define CACHE_BUCKETS_LOG2      10
  59. #define CACHE_BUCKETS           (1 << CACHE_BUCKETS_LOG2)
  60.  
  61. typedef struct {
  62.     futex_t lock;
  63.     size_t block_size;      /**< Block size. */
  64.     unsigned block_count;       /**< Total number of blocks. */
  65.     hash_table_t block_hash;
  66.     link_t free_head;
  67. } cache_t;
  68.  
  69. typedef struct {
  70.     link_t link;
  71.     dev_handle_t dev_handle;
  72.     int dev_phone;
  73.     void *com_area;
  74.     size_t com_size;
  75.     void *bb_buf;
  76.     off_t bb_off;
  77.     size_t bb_size;
  78.     cache_t *cache;
  79. } devcon_t;
  80.  
  81. static devcon_t *devcon_search(dev_handle_t dev_handle)
  82. {
  83.     link_t *cur;
  84.  
  85.     futex_down(&dcl_lock);
  86.     for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) {
  87.         devcon_t *devcon = list_get_instance(cur, devcon_t, link);
  88.         if (devcon->dev_handle == dev_handle) {
  89.             futex_up(&dcl_lock);
  90.             return devcon;
  91.         }
  92.     }
  93.     futex_up(&dcl_lock);
  94.     return NULL;
  95. }
  96.  
  97. static int devcon_add(dev_handle_t dev_handle, int dev_phone, void *com_area,
  98.    size_t com_size)
  99. {
  100.     link_t *cur;
  101.     devcon_t *devcon;
  102.  
  103.     devcon = malloc(sizeof(devcon_t));
  104.     if (!devcon)
  105.         return ENOMEM;
  106.    
  107.     link_initialize(&devcon->link);
  108.     devcon->dev_handle = dev_handle;
  109.     devcon->dev_phone = dev_phone;
  110.     devcon->com_area = com_area;
  111.     devcon->com_size = com_size;
  112.     devcon->bb_buf = NULL;
  113.     devcon->bb_off = 0;
  114.     devcon->bb_size = 0;
  115.     devcon->cache = NULL;
  116.  
  117.     futex_down(&dcl_lock);
  118.     for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) {
  119.         devcon_t *d = list_get_instance(cur, devcon_t, link);
  120.         if (d->dev_handle == dev_handle) {
  121.             futex_up(&dcl_lock);
  122.             free(devcon);
  123.             return EEXIST;
  124.         }
  125.     }
  126.     list_append(&devcon->link, &dcl_head);
  127.     futex_up(&dcl_lock);
  128.     return EOK;
  129. }
  130.  
  131. static void devcon_remove(devcon_t *devcon)
  132. {
  133.     futex_down(&dcl_lock);
  134.     list_remove(&devcon->link);
  135.     futex_up(&dcl_lock);
  136. }
  137.  
  138. int block_init(dev_handle_t dev_handle, size_t com_size)
  139. {
  140.     int rc;
  141.     int dev_phone;
  142.     void *com_area;
  143.    
  144.     com_area = mmap(NULL, com_size, PROTO_READ | PROTO_WRITE,
  145.         MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  146.     if (!com_area) {
  147.         return ENOMEM;
  148.     }
  149.  
  150.     dev_phone = devmap_device_connect(dev_handle, IPC_FLAG_BLOCKING);
  151.     if (dev_phone < 0) {
  152.         munmap(com_area, com_size);
  153.         return dev_phone;
  154.     }
  155.  
  156.     rc = ipc_share_out_start(dev_phone, com_area,
  157.         AS_AREA_READ | AS_AREA_WRITE);
  158.     if (rc != EOK) {
  159.             munmap(com_area, com_size);
  160.         ipc_hangup(dev_phone);
  161.         return rc;
  162.     }
  163.    
  164.     rc = devcon_add(dev_handle, dev_phone, com_area, com_size);
  165.     if (rc != EOK) {
  166.         munmap(com_area, com_size);
  167.         ipc_hangup(dev_phone);
  168.         return rc;
  169.     }
  170.  
  171.     return EOK;
  172. }
  173.  
  174. void block_fini(dev_handle_t dev_handle)
  175. {
  176.     devcon_t *devcon = devcon_search(dev_handle);
  177.     assert(devcon);
  178.    
  179.     devcon_remove(devcon);
  180.  
  181.     if (devcon->bb_buf)
  182.         free(devcon->bb_buf);
  183.  
  184.     if (devcon->cache) {
  185.         hash_table_destroy(&devcon->cache->block_hash);
  186.         free(devcon->cache);
  187.     }
  188.  
  189.     munmap(devcon->com_area, devcon->com_size);
  190.     ipc_hangup(devcon->dev_phone);
  191.  
  192.     free(devcon);  
  193. }
  194.  
  195. int block_bb_read(dev_handle_t dev_handle, off_t off, size_t size)
  196. {
  197.     void *bb_buf;
  198.     int rc;
  199.  
  200.     devcon_t *devcon = devcon_search(dev_handle);
  201.     if (!devcon)
  202.         return ENOENT;
  203.     if (devcon->bb_buf)
  204.         return EEXIST;
  205.     bb_buf = malloc(size);
  206.     if (!bb_buf)
  207.         return ENOMEM;
  208.    
  209.     off_t bufpos = 0;
  210.     size_t buflen = 0;
  211.     rc = block_read(dev_handle, &bufpos, &buflen, &off,
  212.         bb_buf, size, size);
  213.     if (rc != EOK) {
  214.             free(bb_buf);
  215.         return rc;
  216.     }
  217.     devcon->bb_buf = bb_buf;
  218.     devcon->bb_off = off;
  219.     devcon->bb_size = size;
  220.  
  221.     return EOK;
  222. }
  223.  
  224. void *block_bb_get(dev_handle_t dev_handle)
  225. {
  226.     devcon_t *devcon = devcon_search(dev_handle);
  227.     assert(devcon);
  228.     return devcon->bb_buf;
  229. }
  230.  
  231. static hash_index_t cache_hash(unsigned long *key)
  232. {
  233.     return *key & (CACHE_BUCKETS - 1);
  234. }
  235.  
  236. static int cache_compare(unsigned long *key, hash_count_t keys, link_t *item)
  237. {
  238.     block_t *b = hash_table_get_instance(item, block_t, hash_link);
  239.     return b->boff == *key;
  240. }
  241.  
  242. static void cache_remove_callback(link_t *item)
  243. {
  244. }
  245.  
  246. static hash_table_operations_t cache_ops = {
  247.     .hash = cache_hash,
  248.     .compare = cache_compare,
  249.     .remove_callback = cache_remove_callback
  250. };
  251.  
  252. int block_cache_init(dev_handle_t dev_handle, size_t size, unsigned blocks)
  253. {
  254.     devcon_t *devcon = devcon_search(dev_handle);
  255.     cache_t *cache;
  256.     if (!devcon)
  257.         return ENOENT;
  258.     if (devcon->cache)
  259.         return EEXIST;
  260.     cache = malloc(sizeof(cache_t));
  261.     if (!cache)
  262.         return ENOMEM;
  263.    
  264.     futex_initialize(&cache->lock, 1);
  265.     list_initialize(&cache->free_head);
  266.     cache->block_size = size;
  267.     cache->block_count = blocks;
  268.  
  269.     if (!hash_table_create(&cache->block_hash, CACHE_BUCKETS, 1,
  270.         &cache_ops)) {
  271.         free(cache);
  272.         return ENOMEM;
  273.     }
  274.  
  275.     devcon->cache = cache;
  276.     return EOK;
  277. }
  278.  
  279. static bool cache_can_grow(cache_t *cache)
  280. {
  281.     return true;
  282. }
  283.  
  284. static void block_initialize(block_t *b)
  285. {
  286.     futex_initialize(&b->lock, 1);
  287.     b->refcnt = 1;
  288.     b->dirty = false;
  289.     rwlock_initialize(&b->contents_lock);
  290.     link_initialize(&b->free_link);
  291.     link_initialize(&b->hash_link);
  292. }
  293.  
  294. /** Instantiate a block in memory and get a reference to it.
  295.  *
  296.  * @param dev_handle        Device handle of the block device.
  297.  * @param boff          Block offset.
  298.  * @param flags         If BLOCK_FLAGS_NOREAD is specified, block_get()
  299.  *              will not read the contents of the block from the
  300.  *              device.
  301.  *
  302.  * @return          Block structure.
  303.  */
  304. block_t *block_get(dev_handle_t dev_handle, bn_t boff, int flags)
  305. {
  306.     devcon_t *devcon;
  307.     cache_t *cache;
  308.     block_t *b;
  309.     link_t *l;
  310.     unsigned long key = boff;
  311.    
  312.     devcon = devcon_search(dev_handle);
  313.  
  314.     assert(devcon);
  315.     assert(devcon->cache);
  316.    
  317.     cache = devcon->cache;
  318.     futex_down(&cache->lock);
  319.     l = hash_table_find(&cache->block_hash, &key);
  320.     if (l) {
  321.         /*
  322.          * We found the block in the cache.
  323.          */
  324.         b = hash_table_get_instance(l, block_t, hash_link);
  325.         futex_down(&b->lock);
  326.         if (b->refcnt++ == 0)
  327.             list_remove(&b->free_link);
  328.         futex_up(&b->lock);
  329.         futex_up(&cache->lock);
  330.     } else {
  331.         /*
  332.          * The block was not found in the cache.
  333.          */
  334.         int rc;
  335.         off_t bufpos = 0;
  336.         size_t buflen = 0;
  337.         off_t pos = boff * cache->block_size;
  338.         bool sync = false;
  339.  
  340.         if (cache_can_grow(cache)) {
  341.             /*
  342.              * We can grow the cache by allocating new blocks.
  343.              * Should the allocation fail, we fail over and try to
  344.              * recycle a block from the cache.
  345.              */
  346.             b = malloc(sizeof(block_t));
  347.             if (!b)
  348.                 goto recycle;
  349.             b->data = malloc(cache->block_size);
  350.             if (!b->data) {
  351.                 free(b);
  352.                 goto recycle;
  353.             }
  354.         } else {
  355.             /*
  356.              * Try to recycle a block from the free list.
  357.              */
  358.             unsigned long temp_key;
  359. recycle:
  360.             assert(!list_empty(&cache->free_head));
  361.             l = cache->free_head.next;
  362.             list_remove(l);
  363.             b = hash_table_get_instance(l, block_t, hash_link);
  364.             sync = b->dirty;
  365.             temp_key = b->boff;
  366.             hash_table_remove(&cache->block_hash, &temp_key, 1);
  367.         }
  368.  
  369.         block_initialize(b);
  370.         b->dev_handle = dev_handle;
  371.         b->size = cache->block_size;
  372.         b->boff = boff;
  373.         hash_table_insert(&cache->block_hash, &key, &b->hash_link);
  374.  
  375.         /*
  376.          * Lock the block before releasing the cache lock. Thus we don't
  377.          * kill concurent operations on the cache while doing I/O on the
  378.          * block.
  379.          */
  380.         futex_down(&b->lock);
  381.         futex_up(&cache->lock);
  382.  
  383.         if (sync) {
  384.             /*
  385.              * The block is dirty and needs to be written back to
  386.              * the device before we can read in the new contents.
  387.              */
  388.             abort();    /* TODO: block_write() */
  389.         }
  390.         if (!(flags & BLOCK_FLAGS_NOREAD)) {
  391.             /*
  392.              * The block contains old or no data. We need to read
  393.              * the new contents from the device.
  394.              */
  395.             rc = block_read(dev_handle, &bufpos, &buflen, &pos,
  396.                 b->data, cache->block_size, cache->block_size);
  397.             assert(rc == EOK);
  398.         }
  399.  
  400.         futex_up(&b->lock);
  401.     }
  402.     return b;
  403. }
  404.  
  405. /** Release a reference to a block.
  406.  *
  407.  * If the last reference is dropped, the block is put on the free list.
  408.  *
  409.  * @param block     Block of which a reference is to be released.
  410.  */
  411. void block_put(block_t *block)
  412. {
  413.     devcon_t *devcon = devcon_search(block->dev_handle);
  414.     cache_t *cache;
  415.  
  416.     assert(devcon);
  417.     assert(devcon->cache);
  418.  
  419.     cache = devcon->cache;
  420.     futex_down(&cache->lock);
  421.     futex_down(&block->lock);
  422.     if (!--block->refcnt) {
  423.         /*
  424.          * Last reference to the block was dropped, put the block on the
  425.          * free list.
  426.          */
  427.         list_append(&block->free_link, &cache->free_head);
  428.     }
  429.     futex_up(&block->lock);
  430.     futex_up(&cache->lock);
  431. }
  432.  
  433. /** Read data from a block device.
  434.  *
  435.  * @param dev_handle    Device handle of the block device.
  436.  * @param bufpos    Pointer to the first unread valid offset within the
  437.  *          communication buffer.
  438.  * @param buflen    Pointer to the number of unread bytes that are ready in
  439.  *          the communication buffer.
  440.  * @param pos       Device position to be read.
  441.  * @param dst       Destination buffer.
  442.  * @param size      Size of the destination buffer.
  443.  * @param block_size    Block size to be used for the transfer.
  444.  *
  445.  * @return      EOK on success or a negative return code on failure.
  446.  */
  447. int
  448. block_read(dev_handle_t dev_handle, off_t *bufpos, size_t *buflen, off_t *pos,
  449.     void *dst, size_t size, size_t block_size)
  450. {
  451.     off_t offset = 0;
  452.     size_t left = size;
  453.     devcon_t *devcon = devcon_search(dev_handle);
  454.     assert(devcon);
  455.    
  456.     while (left > 0) {
  457.         size_t rd;
  458.        
  459.         if (*bufpos + left < *buflen)
  460.             rd = left;
  461.         else
  462.             rd = *buflen - *bufpos;
  463.        
  464.         if (rd > 0) {
  465.             /*
  466.              * Copy the contents of the communication buffer to the
  467.              * destination buffer.
  468.              */
  469.             memcpy(dst + offset, devcon->com_area + *bufpos, rd);
  470.             offset += rd;
  471.             *bufpos += rd;
  472.             *pos += rd;
  473.             left -= rd;
  474.         }
  475.        
  476.         if (*bufpos == *buflen) {
  477.             /* Refill the communication buffer with a new block. */
  478.             ipcarg_t retval;
  479.             int rc = async_req_2_1(devcon->dev_phone, BD_READ_BLOCK,
  480.                 *pos / block_size, block_size, &retval);
  481.             if ((rc != EOK) || (retval != EOK))
  482.                 return (rc != EOK ? rc : retval);
  483.            
  484.             *bufpos = 0;
  485.             *buflen = block_size;
  486.         }
  487.     }
  488.    
  489.     return EOK;
  490. }
  491.  
  492. /** @}
  493.  */
  494.