Subversion Repositories HelenOS

Rev

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