Subversion Repositories HelenOS

Rev

Rev 3571 | 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 "../../srv/rd/rd.h"
  41. #include <ipc/devmap.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.     int 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.     dev_phone = ipc_connect_me_to(PHONE_NS, SERVICE_DEVMAP,
  150.         DEVMAP_CONNECT_TO_DEVICE, dev_handle);
  151.  
  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.  *
  300.  * @return          Block structure.
  301.  */
  302. block_t *block_get(dev_handle_t dev_handle, bn_t boff)
  303. {
  304.     devcon_t *devcon;
  305.     cache_t *cache;
  306.     block_t *b;
  307.     link_t *l;
  308.     unsigned long key = boff;
  309.    
  310.     devcon = devcon_search(dev_handle);
  311.  
  312.     assert(devcon);
  313.     assert(devcon->cache);
  314.    
  315.     cache = devcon->cache;
  316.     futex_down(&cache->lock);
  317.     l = hash_table_find(&cache->block_hash, &key);
  318.     if (l) {
  319.         /*
  320.          * We found the block in the cache.
  321.          */
  322.         b = hash_table_get_instance(l, block_t, hash_link);
  323.         futex_down(&b->lock);
  324.         if (b->refcnt++ == 0)
  325.             list_remove(&b->free_link);
  326.         futex_up(&b->lock);
  327.         futex_up(&cache->lock);
  328.     } else {
  329.         /*
  330.          * The block was not found in the cache.
  331.          */
  332.         int rc;
  333.         off_t bufpos = 0;
  334.         size_t buflen = 0;
  335.         off_t pos = boff * cache->block_size;
  336.         bool sync = false;
  337.  
  338.         if (cache_can_grow(cache)) {
  339.             /*
  340.              * We can grow the cache by allocating new blocks.
  341.              * Should the allocation fail, we fail over and try to
  342.              * recycle a block from the cache.
  343.              */
  344.             b = malloc(sizeof(block_t));
  345.             if (!b)
  346.                 goto recycle;
  347.             b->data = malloc(cache->block_size);
  348.             if (!b->data) {
  349.                 free(b);
  350.                 goto recycle;
  351.             }
  352.         } else {
  353.             /*
  354.              * Try to recycle a block from the free list.
  355.              */
  356.             unsigned long temp_key;
  357. recycle:
  358.             assert(!list_empty(&cache->free_head));
  359.             l = cache->free_head.next;
  360.             list_remove(l);
  361.             b = hash_table_get_instance(l, block_t, hash_link);
  362.             sync = b->dirty;
  363.             temp_key = b->boff;
  364.             hash_table_remove(&cache->block_hash, &temp_key, 1);
  365.         }
  366.  
  367.         block_initialize(b);
  368.         b->dev_handle = dev_handle;
  369.         b->size = cache->block_size;
  370.         b->boff = boff;
  371.         hash_table_insert(&cache->block_hash, &key, &b->hash_link);
  372.  
  373.         /*
  374.          * Lock the block before releasing the cache lock. Thus we don't
  375.          * kill concurent operations on the cache while doing I/O on the
  376.          * block.
  377.          */
  378.         futex_down(&b->lock);
  379.         futex_up(&cache->lock);
  380.  
  381.         if (sync) {
  382.             /*
  383.              * The block is dirty and needs to be written back to
  384.              * the device before we can read in the new contents.
  385.              */
  386.             abort();    /* TODO: block_write() */
  387.         }
  388.         /*
  389.          * The block contains old or no data. We need to read the new
  390.          * contents from the device.
  391.          */
  392.         rc = block_read(dev_handle, &bufpos, &buflen, &pos, b->data,
  393.             cache->block_size, cache->block_size);
  394.         assert(rc == EOK);
  395.  
  396.         futex_up(&b->lock);
  397.     }
  398.     return b;
  399. }
  400.  
  401. /** Release a reference to a block.
  402.  *
  403.  * If the last reference is dropped, the block is put on the free list.
  404.  *
  405.  * @param block     Block of which a reference is to be released.
  406.  */
  407. void block_put(block_t *block)
  408. {
  409.     devcon_t *devcon = devcon_search(block->dev_handle);
  410.     cache_t *cache;
  411.  
  412.     assert(devcon);
  413.     assert(devcon->cache);
  414.  
  415.     cache = devcon->cache;
  416.     futex_down(&cache->lock);
  417.     futex_down(&block->lock);
  418.     if (!--block->refcnt) {
  419.         /*
  420.          * Last reference to the block was dropped, put the block on the
  421.          * free list.
  422.          */
  423.         list_append(&block->free_link, &cache->free_head);
  424.     }
  425.     futex_up(&block->lock);
  426.     futex_up(&cache->lock);
  427. }
  428.  
  429. /** Read data from a block device.
  430.  *
  431.  * @param dev_handle    Device handle of the block device.
  432.  * @param bufpos    Pointer to the first unread valid offset within the
  433.  *          communication buffer.
  434.  * @param buflen    Pointer to the number of unread bytes that are ready in
  435.  *          the communication buffer.
  436.  * @param pos       Device position to be read.
  437.  * @param dst       Destination buffer.
  438.  * @param size      Size of the destination buffer.
  439.  * @param block_size    Block size to be used for the transfer.
  440.  *
  441.  * @return      EOK on success or a negative return code on failure.
  442.  */
  443. int
  444. block_read(int dev_handle, off_t *bufpos, size_t *buflen, off_t *pos, void *dst,
  445.     size_t size, size_t block_size)
  446. {
  447.     off_t offset = 0;
  448.     size_t left = size;
  449.     devcon_t *devcon = devcon_search(dev_handle);
  450.     assert(devcon);
  451.    
  452.     while (left > 0) {
  453.         size_t rd;
  454.        
  455.         if (*bufpos + left < *buflen)
  456.             rd = left;
  457.         else
  458.             rd = *buflen - *bufpos;
  459.        
  460.         if (rd > 0) {
  461.             /*
  462.              * Copy the contents of the communication buffer to the
  463.              * destination buffer.
  464.              */
  465.             memcpy(dst + offset, devcon->com_area + *bufpos, rd);
  466.             offset += rd;
  467.             *bufpos += rd;
  468.             *pos += rd;
  469.             left -= rd;
  470.         }
  471.        
  472.         if (*bufpos == *buflen) {
  473.             /* Refill the communication buffer with a new block. */
  474.             ipcarg_t retval;
  475.             int rc = async_req_2_1(devcon->dev_phone, RD_READ_BLOCK,
  476.                 *pos / block_size, block_size, &retval);
  477.             if ((rc != EOK) || (retval != EOK))
  478.                 return (rc != EOK ? rc : retval);
  479.            
  480.             *bufpos = 0;
  481.             *buflen = block_size;
  482.         }
  483.     }
  484.    
  485.     return EOK;
  486. }
  487.  
  488. /** @}
  489.  */
  490.