Subversion Repositories HelenOS

Rev

Rev 3539 | Rev 3542 | 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. /** Read data from a block device.
  281.  *
  282.  * @param dev_handle    Device handle of the block device.
  283.  * @param bufpos    Pointer to the first unread valid offset within the
  284.  *          communication buffer.
  285.  * @param buflen    Pointer to the number of unread bytes that are ready in
  286.  *          the communication buffer.
  287.  * @param pos       Device position to be read.
  288.  * @param dst       Destination buffer.
  289.  * @param size      Size of the destination buffer.
  290.  * @param block_size    Block size to be used for the transfer.
  291.  *
  292.  * @return      EOK on success or a negative return code on failure.
  293.  */
  294. int
  295. block_read(int dev_handle, off_t *bufpos, size_t *buflen, off_t *pos, void *dst,
  296.     size_t size, size_t block_size)
  297. {
  298.     off_t offset = 0;
  299.     size_t left = size;
  300.     devcon_t *devcon = devcon_search(dev_handle);
  301.     assert(devcon);
  302.    
  303.     while (left > 0) {
  304.         size_t rd;
  305.        
  306.         if (*bufpos + left < *buflen)
  307.             rd = left;
  308.         else
  309.             rd = *buflen - *bufpos;
  310.        
  311.         if (rd > 0) {
  312.             /*
  313.              * Copy the contents of the communication buffer to the
  314.              * destination buffer.
  315.              */
  316.             memcpy(dst + offset, devcon->com_area + *bufpos, rd);
  317.             offset += rd;
  318.             *bufpos += rd;
  319.             *pos += rd;
  320.             left -= rd;
  321.         }
  322.        
  323.         if (*bufpos == *buflen) {
  324.             /* Refill the communication buffer with a new block. */
  325.             ipcarg_t retval;
  326.             int rc = async_req_2_1(devcon->dev_phone, RD_READ_BLOCK,
  327.                 *pos / block_size, block_size, &retval);
  328.             if ((rc != EOK) || (retval != EOK))
  329.                 return (rc != EOK ? rc : retval);
  330.            
  331.             *bufpos = 0;
  332.             *buflen = block_size;
  333.         }
  334.     }
  335.    
  336.     return EOK;
  337. }
  338.  
  339. static bool cache_can_grow(cache_t *cache)
  340. {
  341.     return true;
  342. }
  343.  
  344. static void block_initialize(block_t *b)
  345. {
  346.     futex_initialize(&b->lock, 1);
  347.     b->refcnt = 1;
  348.     b->dirty = false;
  349.     rwlock_initialize(&b->contents_lock);
  350.     link_initialize(&b->free_link);
  351.     link_initialize(&b->hash_link);
  352. }
  353.  
  354. /** Instantiate a block in memory and get a reference to it.
  355.  *
  356.  * @param dev_handle        Device handle of the block device.
  357.  * @param boff          Block offset.
  358.  *
  359.  * @return          Block structure.
  360.  */
  361. block_t *block_get(dev_handle_t dev_handle, off_t boff, size_t bs)
  362. {
  363.     devcon_t *devcon;
  364.     cache_t *cache;
  365.     block_t *b;
  366.     link_t *l;
  367.     unsigned long key = boff;
  368.    
  369.     devcon = devcon_search(dev_handle);
  370.  
  371.     assert(devcon);
  372.     assert(devcon->cache);
  373.    
  374.     cache = devcon->cache;
  375.     futex_down(&cache->lock);
  376.     l = hash_table_find(&cache->block_hash, &key);
  377.     if (l) {
  378.         /*
  379.          * We found the block in the cache.
  380.          */
  381.         b = hash_table_get_instance(l, block_t, hash_link);
  382.         futex_down(&b->lock);
  383.         if (b->refcnt++ == 0)
  384.             list_remove(&b->free_link);
  385.         futex_up(&b->lock);
  386.     } else {
  387.         /*
  388.          * The block was not found in the cache.
  389.          */
  390.         int rc;
  391.         off_t bufpos = 0;
  392.         size_t buflen = 0;
  393.         off_t pos = boff * cache->block_size;
  394.  
  395.         if (cache_can_grow(cache)) {
  396.             /*
  397.              * We can grow the cache by allocating new blocks.
  398.              * Should the allocation fail, we fail over and try to
  399.              * recycle a block from the cache.
  400.              */
  401.             b = malloc(sizeof(block_t));
  402.             if (!b)
  403.                 goto recycle;
  404.             b->data = malloc(cache->block_size);
  405.             if (!b->data) {
  406.                 free(b);
  407.                 goto recycle;
  408.             }
  409.         } else {
  410.             /*
  411.              * Try to recycle a block from the free list.
  412.              */
  413.             unsigned long temp_key;
  414. recycle:
  415.             assert(!list_empty(&cache->free_head));
  416.             l = cache->free_head.next;
  417.             list_remove(l);
  418.             b = hash_table_get_instance(l, block_t, hash_link);
  419.             assert(!b->dirty);
  420.             temp_key = b->boff;
  421.             hash_table_remove(&cache->block_hash, &temp_key, 1);
  422.         }
  423.  
  424.         block_initialize(b);
  425.         b->dev_handle = dev_handle;
  426.         b->size = cache->block_size;
  427.         b->boff = boff;
  428.         /* read block from the device */
  429.         rc = block_read(dev_handle, &bufpos, &buflen, &pos, b->data,
  430.             cache->block_size, cache->block_size);
  431.         assert(rc == EOK);
  432.         hash_table_insert(&cache->block_hash, &key, &b->hash_link);
  433.     }
  434.  
  435.     futex_up(&cache->lock);
  436.     return b;
  437. }
  438.  
  439. void block_put(block_t *block)
  440. {
  441.    
  442. }
  443.  
  444. /** @}
  445.  */
  446.