Subversion Repositories HelenOS

Rev

Rev 4572 | Rev 4611 | 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 read_block(devcon_t *devcon, bn_t boff, size_t block_size);
  84. static int write_block(devcon_t *devcon, bn_t boff, size_t block_size);
  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.     rc = read_block(devcon, 0, size);
  215.     if (rc != EOK) {
  216.             free(bb_buf);
  217.         return rc;
  218.     }
  219.  
  220.     memcpy(bb_buf, devcon->com_area, size);
  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.         bool sync = false;
  343.  
  344.         if (cache_can_grow(cache)) {
  345.             /*
  346.              * We can grow the cache by allocating new blocks.
  347.              * Should the allocation fail, we fail over and try to
  348.              * recycle a block from the cache.
  349.              */
  350.             b = malloc(sizeof(block_t));
  351.             if (!b)
  352.                 goto recycle;
  353.             b->data = malloc(cache->block_size);
  354.             if (!b->data) {
  355.                 free(b);
  356.                 goto recycle;
  357.             }
  358.         } else {
  359.             /*
  360.              * Try to recycle a block from the free list.
  361.              */
  362.             unsigned long temp_key;
  363. recycle:
  364.             assert(!list_empty(&cache->free_head));
  365.             l = cache->free_head.next;
  366.             list_remove(l);
  367.             b = hash_table_get_instance(l, block_t, hash_link);
  368.             sync = b->dirty;
  369.             temp_key = b->boff;
  370.             hash_table_remove(&cache->block_hash, &temp_key, 1);
  371.         }
  372.  
  373.         block_initialize(b);
  374.         b->dev_handle = dev_handle;
  375.         b->size = cache->block_size;
  376.         b->boff = boff;
  377.         hash_table_insert(&cache->block_hash, &key, &b->hash_link);
  378.  
  379.         /*
  380.          * Lock the block before releasing the cache lock. Thus we don't
  381.          * kill concurent operations on the cache while doing I/O on the
  382.          * block.
  383.          */
  384.         fibril_mutex_lock(&b->lock);
  385.         fibril_mutex_unlock(&cache->lock);
  386.  
  387.         if (sync) {
  388.             /*
  389.              * The block is dirty and needs to be written back to
  390.              * the device before we can read in the new contents.
  391.              */
  392.             abort();    /* TODO: block_write() */
  393.         }
  394.         if (!(flags & BLOCK_FLAGS_NOREAD)) {
  395.             /*
  396.              * The block contains old or no data. We need to read
  397.              * the new contents from the device.
  398.              */
  399.             rc = read_block(devcon, b->boff, cache->block_size);
  400.             assert(rc == EOK);
  401.             memcpy(b->data, devcon->com_area, cache->block_size);
  402.         }
  403.  
  404.         fibril_mutex_unlock(&b->lock);
  405.     }
  406.     return b;
  407. }
  408.  
  409. /** Release a reference to a block.
  410.  *
  411.  * If the last reference is dropped, the block is put on the free list.
  412.  *
  413.  * @param block     Block of which a reference is to be released.
  414.  */
  415. void block_put(block_t *block)
  416. {
  417.     devcon_t *devcon = devcon_search(block->dev_handle);
  418.     cache_t *cache;
  419.     int rc;
  420.  
  421.     assert(devcon);
  422.     assert(devcon->cache);
  423.  
  424.     cache = devcon->cache;
  425.     fibril_mutex_lock(&cache->lock);
  426.     fibril_mutex_lock(&block->lock);
  427.     if (!--block->refcnt) {
  428.         /*
  429.          * Last reference to the block was dropped, put the block on the
  430.          * free list.
  431.          */
  432.         list_append(&block->free_link, &cache->free_head);
  433.         if (cache->mode != CACHE_MODE_WB && block->dirty) {
  434.             memcpy(devcon->com_area, block->data, block->size);
  435.             rc = write_block(devcon, block->boff, block->size);
  436.             assert(rc == EOK);
  437.  
  438.             block->dirty = false;
  439.         }
  440.     }
  441.     fibril_mutex_unlock(&block->lock);
  442.     fibril_mutex_unlock(&cache->lock);
  443. }
  444.  
  445. /** Read sequential data from a block device.
  446.  *
  447.  * @param dev_handle    Device handle of the block device.
  448.  * @param bufpos    Pointer to the first unread valid offset within the
  449.  *          communication buffer.
  450.  * @param buflen    Pointer to the number of unread bytes that are ready in
  451.  *          the communication buffer.
  452.  * @param pos       Device position to be read.
  453.  * @param dst       Destination buffer.
  454.  * @param size      Size of the destination buffer.
  455.  * @param block_size    Block size to be used for the transfer.
  456.  *
  457.  * @return      EOK on success or a negative return code on failure.
  458.  */
  459. int block_seqread(dev_handle_t dev_handle, off_t *bufpos, size_t *buflen,
  460.     off_t *pos, void *dst, size_t size, size_t block_size)
  461. {
  462.     off_t offset = 0;
  463.     size_t left = size;
  464.     devcon_t *devcon = devcon_search(dev_handle);
  465.     assert(devcon);
  466.    
  467.     while (left > 0) {
  468.         size_t rd;
  469.        
  470.         if (*bufpos + left < *buflen)
  471.             rd = left;
  472.         else
  473.             rd = *buflen - *bufpos;
  474.        
  475.         if (rd > 0) {
  476.             /*
  477.              * Copy the contents of the communication buffer to the
  478.              * destination buffer.
  479.              */
  480.             memcpy(dst + offset, devcon->com_area + *bufpos, rd);
  481.             offset += rd;
  482.             *bufpos += rd;
  483.             *pos += rd;
  484.             left -= rd;
  485.         }
  486.        
  487.         if (*bufpos == (off_t) *buflen) {
  488.             /* Refill the communication buffer with a new block. */
  489.             int rc;
  490.  
  491.             rc = read_block(devcon, *pos / block_size, block_size);
  492.             if (rc != EOK)
  493.                 return rc;
  494.            
  495.             *bufpos = 0;
  496.             *buflen = block_size;
  497.         }
  498.     }
  499.    
  500.     return EOK;
  501. }
  502.  
  503. /** Read block from block device.
  504.  *
  505.  * @param devcon    Device connection.
  506.  * @param boff      Block index.
  507.  * @param block_size    Block size.
  508.  * @param src       Buffer for storing the data.
  509.  *
  510.  * @return      EOK on success or negative error code on failure.
  511.  */
  512. static int read_block(devcon_t *devcon, bn_t boff, size_t block_size)
  513. {
  514.     ipcarg_t retval;
  515.     int rc;
  516.  
  517.     assert(devcon);
  518.     rc = async_req_2_1(devcon->dev_phone, BD_READ_BLOCK, boff, block_size,
  519.         &retval);
  520.     if ((rc != EOK) || (retval != EOK))
  521.         return (rc != EOK ? rc : (int) retval);
  522.  
  523.     return EOK;
  524. }
  525.  
  526. /** Write block to block device.
  527.  *
  528.  * @param devcon    Device connection.
  529.  * @param boff      Block index.
  530.  * @param block_size    Block size.
  531.  * @param src       Buffer containing the data to write.
  532.  *
  533.  * @return      EOK on success or negative error code on failure.
  534.  */
  535. static int write_block(devcon_t *devcon, bn_t boff, size_t block_size)
  536. {
  537.     ipcarg_t retval;
  538.     int rc;
  539.  
  540.     assert(devcon);
  541.     rc = async_req_2_1(devcon->dev_phone, BD_WRITE_BLOCK, boff, block_size,
  542.         &retval);
  543.     if ((rc != EOK) || (retval != EOK))
  544.         return (rc != EOK ? rc : (int) retval);
  545.  
  546.     return EOK;
  547. }
  548.  
  549. /** @}
  550.  */
  551.