Subversion Repositories HelenOS

Rev

Rev 4435 | Rev 4549 | 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. #include <mem.h>
  53.  
  54. /** Lock protecting the device connection list */
  55. static futex_t dcl_lock = FUTEX_INITIALIZER;
  56. /** Device connection list head. */
  57. static LIST_INITIALIZE(dcl_head);
  58.  
  59. #define CACHE_BUCKETS_LOG2      10
  60. #define CACHE_BUCKETS           (1 << CACHE_BUCKETS_LOG2)
  61.  
  62. typedef struct {
  63.     futex_t lock;
  64.     size_t block_size;      /**< Block size. */
  65.     unsigned block_count;       /**< Total number of blocks. */
  66.     hash_table_t block_hash;
  67.     link_t free_head;
  68. } cache_t;
  69.  
  70. typedef struct {
  71.     link_t link;
  72.     dev_handle_t dev_handle;
  73.     int dev_phone;
  74.     void *com_area;
  75.     size_t com_size;
  76.     void *bb_buf;
  77.     off_t bb_off;
  78.     size_t bb_size;
  79.     cache_t *cache;
  80. } devcon_t;
  81.  
  82. static devcon_t *devcon_search(dev_handle_t dev_handle)
  83. {
  84.     link_t *cur;
  85.  
  86.     futex_down(&dcl_lock);
  87.     for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) {
  88.         devcon_t *devcon = list_get_instance(cur, devcon_t, link);
  89.         if (devcon->dev_handle == dev_handle) {
  90.             futex_up(&dcl_lock);
  91.             return devcon;
  92.         }
  93.     }
  94.     futex_up(&dcl_lock);
  95.     return NULL;
  96. }
  97.  
  98. static int devcon_add(dev_handle_t dev_handle, int dev_phone, void *com_area,
  99.    size_t com_size)
  100. {
  101.     link_t *cur;
  102.     devcon_t *devcon;
  103.  
  104.     devcon = malloc(sizeof(devcon_t));
  105.     if (!devcon)
  106.         return ENOMEM;
  107.    
  108.     link_initialize(&devcon->link);
  109.     devcon->dev_handle = dev_handle;
  110.     devcon->dev_phone = dev_phone;
  111.     devcon->com_area = com_area;
  112.     devcon->com_size = com_size;
  113.     devcon->bb_buf = NULL;
  114.     devcon->bb_off = 0;
  115.     devcon->bb_size = 0;
  116.     devcon->cache = NULL;
  117.  
  118.     futex_down(&dcl_lock);
  119.     for (cur = dcl_head.next; cur != &dcl_head; cur = cur->next) {
  120.         devcon_t *d = list_get_instance(cur, devcon_t, link);
  121.         if (d->dev_handle == dev_handle) {
  122.             futex_up(&dcl_lock);
  123.             free(devcon);
  124.             return EEXIST;
  125.         }
  126.     }
  127.     list_append(&devcon->link, &dcl_head);
  128.     futex_up(&dcl_lock);
  129.     return EOK;
  130. }
  131.  
  132. static void devcon_remove(devcon_t *devcon)
  133. {
  134.     futex_down(&dcl_lock);
  135.     list_remove(&devcon->link);
  136.     futex_up(&dcl_lock);
  137. }
  138.  
  139. int block_init(dev_handle_t dev_handle, size_t com_size)
  140. {
  141.     int rc;
  142.     int dev_phone;
  143.     void *com_area;
  144.    
  145.     com_area = mmap(NULL, com_size, PROTO_READ | PROTO_WRITE,
  146.         MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  147.     if (!com_area) {
  148.         return ENOMEM;
  149.     }
  150.  
  151.     dev_phone = devmap_device_connect(dev_handle, IPC_FLAG_BLOCKING);
  152.     if (dev_phone < 0) {
  153.         munmap(com_area, com_size);
  154.         return dev_phone;
  155.     }
  156.  
  157.     rc = ipc_share_out_start(dev_phone, com_area,
  158.         AS_AREA_READ | AS_AREA_WRITE);
  159.     if (rc != EOK) {
  160.             munmap(com_area, com_size);
  161.         ipc_hangup(dev_phone);
  162.         return rc;
  163.     }
  164.    
  165.     rc = devcon_add(dev_handle, dev_phone, com_area, com_size);
  166.     if (rc != EOK) {
  167.         munmap(com_area, com_size);
  168.         ipc_hangup(dev_phone);
  169.         return rc;
  170.     }
  171.  
  172.     return EOK;
  173. }
  174.  
  175. void block_fini(dev_handle_t dev_handle)
  176. {
  177.     devcon_t *devcon = devcon_search(dev_handle);
  178.     assert(devcon);
  179.    
  180.     devcon_remove(devcon);
  181.  
  182.     if (devcon->bb_buf)
  183.         free(devcon->bb_buf);
  184.  
  185.     if (devcon->cache) {
  186.         hash_table_destroy(&devcon->cache->block_hash);
  187.         free(devcon->cache);
  188.     }
  189.  
  190.     munmap(devcon->com_area, devcon->com_size);
  191.     ipc_hangup(devcon->dev_phone);
  192.  
  193.     free(devcon);  
  194. }
  195.  
  196. int block_bb_read(dev_handle_t dev_handle, off_t off, size_t size)
  197. {
  198.     void *bb_buf;
  199.     int rc;
  200.  
  201.     devcon_t *devcon = devcon_search(dev_handle);
  202.     if (!devcon)
  203.         return ENOENT;
  204.     if (devcon->bb_buf)
  205.         return EEXIST;
  206.     bb_buf = malloc(size);
  207.     if (!bb_buf)
  208.         return ENOMEM;
  209.    
  210.     off_t bufpos = 0;
  211.     size_t buflen = 0;
  212.     rc = block_read(dev_handle, &bufpos, &buflen, &off,
  213.         bb_buf, size, size);
  214.     if (rc != EOK) {
  215.             free(bb_buf);
  216.         return rc;
  217.     }
  218.     devcon->bb_buf = bb_buf;
  219.     devcon->bb_off = off;
  220.     devcon->bb_size = size;
  221.  
  222.     return EOK;
  223. }
  224.  
  225. void *block_bb_get(dev_handle_t dev_handle)
  226. {
  227.     devcon_t *devcon = devcon_search(dev_handle);
  228.     assert(devcon);
  229.     return devcon->bb_buf;
  230. }
  231.  
  232. static hash_index_t cache_hash(unsigned long *key)
  233. {
  234.     return *key & (CACHE_BUCKETS - 1);
  235. }
  236.  
  237. static int cache_compare(unsigned long *key, hash_count_t keys, link_t *item)
  238. {
  239.     block_t *b = hash_table_get_instance(item, block_t, hash_link);
  240.     return b->boff == *key;
  241. }
  242.  
  243. static void cache_remove_callback(link_t *item)
  244. {
  245. }
  246.  
  247. static hash_table_operations_t cache_ops = {
  248.     .hash = cache_hash,
  249.     .compare = cache_compare,
  250.     .remove_callback = cache_remove_callback
  251. };
  252.  
  253. int block_cache_init(dev_handle_t dev_handle, size_t size, unsigned blocks)
  254. {
  255.     devcon_t *devcon = devcon_search(dev_handle);
  256.     cache_t *cache;
  257.     if (!devcon)
  258.         return ENOENT;
  259.     if (devcon->cache)
  260.         return EEXIST;
  261.     cache = malloc(sizeof(cache_t));
  262.     if (!cache)
  263.         return ENOMEM;
  264.    
  265.     futex_initialize(&cache->lock, 1);
  266.     list_initialize(&cache->free_head);
  267.     cache->block_size = size;
  268.     cache->block_count = blocks;
  269.  
  270.     if (!hash_table_create(&cache->block_hash, CACHE_BUCKETS, 1,
  271.         &cache_ops)) {
  272.         free(cache);
  273.         return ENOMEM;
  274.     }
  275.  
  276.     devcon->cache = cache;
  277.     return EOK;
  278. }
  279.  
  280. static bool cache_can_grow(cache_t *cache)
  281. {
  282.     return true;
  283. }
  284.  
  285. static void block_initialize(block_t *b)
  286. {
  287.     futex_initialize(&b->lock, 1);
  288.     b->refcnt = 1;
  289.     b->dirty = false;
  290.     rwlock_initialize(&b->contents_lock);
  291.     link_initialize(&b->free_link);
  292.     link_initialize(&b->hash_link);
  293. }
  294.  
  295. /** Instantiate a block in memory and get a reference to it.
  296.  *
  297.  * @param dev_handle        Device handle of the block device.
  298.  * @param boff          Block offset.
  299.  * @param flags         If BLOCK_FLAGS_NOREAD is specified, block_get()
  300.  *              will not read the contents of the block from the
  301.  *              device.
  302.  *
  303.  * @return          Block structure.
  304.  */
  305. block_t *block_get(dev_handle_t dev_handle, bn_t boff, int flags)
  306. {
  307.     devcon_t *devcon;
  308.     cache_t *cache;
  309.     block_t *b;
  310.     link_t *l;
  311.     unsigned long key = boff;
  312.    
  313.     devcon = devcon_search(dev_handle);
  314.  
  315.     assert(devcon);
  316.     assert(devcon->cache);
  317.    
  318.     cache = devcon->cache;
  319.     futex_down(&cache->lock);
  320.     l = hash_table_find(&cache->block_hash, &key);
  321.     if (l) {
  322.         /*
  323.          * We found the block in the cache.
  324.          */
  325.         b = hash_table_get_instance(l, block_t, hash_link);
  326.         futex_down(&b->lock);
  327.         if (b->refcnt++ == 0)
  328.             list_remove(&b->free_link);
  329.         futex_up(&b->lock);
  330.         futex_up(&cache->lock);
  331.     } else {
  332.         /*
  333.          * The block was not found in the cache.
  334.          */
  335.         int rc;
  336.         off_t bufpos = 0;
  337.         size_t buflen = 0;
  338.         off_t pos = boff * cache->block_size;
  339.         bool sync = false;
  340.  
  341.         if (cache_can_grow(cache)) {
  342.             /*
  343.              * We can grow the cache by allocating new blocks.
  344.              * Should the allocation fail, we fail over and try to
  345.              * recycle a block from the cache.
  346.              */
  347.             b = malloc(sizeof(block_t));
  348.             if (!b)
  349.                 goto recycle;
  350.             b->data = malloc(cache->block_size);
  351.             if (!b->data) {
  352.                 free(b);
  353.                 goto recycle;
  354.             }
  355.         } else {
  356.             /*
  357.              * Try to recycle a block from the free list.
  358.              */
  359.             unsigned long temp_key;
  360. recycle:
  361.             assert(!list_empty(&cache->free_head));
  362.             l = cache->free_head.next;
  363.             list_remove(l);
  364.             b = hash_table_get_instance(l, block_t, hash_link);
  365.             sync = b->dirty;
  366.             temp_key = b->boff;
  367.             hash_table_remove(&cache->block_hash, &temp_key, 1);
  368.         }
  369.  
  370.         block_initialize(b);
  371.         b->dev_handle = dev_handle;
  372.         b->size = cache->block_size;
  373.         b->boff = boff;
  374.         hash_table_insert(&cache->block_hash, &key, &b->hash_link);
  375.  
  376.         /*
  377.          * Lock the block before releasing the cache lock. Thus we don't
  378.          * kill concurent operations on the cache while doing I/O on the
  379.          * block.
  380.          */
  381.         futex_down(&b->lock);
  382.         futex_up(&cache->lock);
  383.  
  384.         if (sync) {
  385.             /*
  386.              * The block is dirty and needs to be written back to
  387.              * the device before we can read in the new contents.
  388.              */
  389.             abort();    /* TODO: block_write() */
  390.         }
  391.         if (!(flags & BLOCK_FLAGS_NOREAD)) {
  392.             /*
  393.              * The block contains old or no data. We need to read
  394.              * the new contents from the device.
  395.              */
  396.             rc = block_read(dev_handle, &bufpos, &buflen, &pos,
  397.                 b->data, cache->block_size, cache->block_size);
  398.             assert(rc == EOK);
  399.         }
  400.  
  401.         futex_up(&b->lock);
  402.     }
  403.     return b;
  404. }
  405.  
  406. /** Release a reference to a block.
  407.  *
  408.  * If the last reference is dropped, the block is put on the free list.
  409.  *
  410.  * @param block     Block of which a reference is to be released.
  411.  */
  412. void block_put(block_t *block)
  413. {
  414.     devcon_t *devcon = devcon_search(block->dev_handle);
  415.     cache_t *cache;
  416.  
  417.     assert(devcon);
  418.     assert(devcon->cache);
  419.  
  420.     cache = devcon->cache;
  421.     futex_down(&cache->lock);
  422.     futex_down(&block->lock);
  423.     if (!--block->refcnt) {
  424.         /*
  425.          * Last reference to the block was dropped, put the block on the
  426.          * free list.
  427.          */
  428.         list_append(&block->free_link, &cache->free_head);
  429.     }
  430.     futex_up(&block->lock);
  431.     futex_up(&cache->lock);
  432. }
  433.  
  434. /** Read data from a block device.
  435.  *
  436.  * @param dev_handle    Device handle of the block device.
  437.  * @param bufpos    Pointer to the first unread valid offset within the
  438.  *          communication buffer.
  439.  * @param buflen    Pointer to the number of unread bytes that are ready in
  440.  *          the communication buffer.
  441.  * @param pos       Device position to be read.
  442.  * @param dst       Destination buffer.
  443.  * @param size      Size of the destination buffer.
  444.  * @param block_size    Block size to be used for the transfer.
  445.  *
  446.  * @return      EOK on success or a negative return code on failure.
  447.  */
  448. int
  449. block_read(dev_handle_t dev_handle, off_t *bufpos, size_t *buflen, off_t *pos,
  450.     void *dst, size_t size, size_t block_size)
  451. {
  452.     off_t offset = 0;
  453.     size_t left = size;
  454.     devcon_t *devcon = devcon_search(dev_handle);
  455.     assert(devcon);
  456.    
  457.     while (left > 0) {
  458.         size_t rd;
  459.        
  460.         if (*bufpos + left < *buflen)
  461.             rd = left;
  462.         else
  463.             rd = *buflen - *bufpos;
  464.        
  465.         if (rd > 0) {
  466.             /*
  467.              * Copy the contents of the communication buffer to the
  468.              * destination buffer.
  469.              */
  470.             memcpy(dst + offset, devcon->com_area + *bufpos, rd);
  471.             offset += rd;
  472.             *bufpos += rd;
  473.             *pos += rd;
  474.             left -= rd;
  475.         }
  476.        
  477.         if (*bufpos == *buflen) {
  478.             /* Refill the communication buffer with a new block. */
  479.             ipcarg_t retval;
  480.             int rc = async_req_2_1(devcon->dev_phone, BD_READ_BLOCK,
  481.                 *pos / block_size, block_size, &retval);
  482.             if ((rc != EOK) || (retval != EOK))
  483.                 return (rc != EOK ? rc : retval);
  484.            
  485.             *bufpos = 0;
  486.             *buflen = block_size;
  487.         }
  488.     }
  489.    
  490.     return EOK;
  491. }
  492.  
  493. /** @}
  494.  */
  495.