LCOV - code coverage report
Current view: top level - drivers - block-lcache.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 148 0
Test Date: 2026-06-26 00:00:00 Functions: 0.0 % 15 0
Branches: 0.0 % 48 0

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * Write any sectors not found in the leaf back to the leaf.
       3                 :             :  * Copyright (c) 2016, Citrix Systems, Inc.
       4                 :             :  *
       5                 :             :  * All rights reserved.
       6                 :             :  *
       7                 :             :  * Redistribution and use in source and binary forms, with or without
       8                 :             :  * modification, are permitted provided that the following conditions are met:
       9                 :             :  * 
      10                 :             :  *  1. Redistributions of source code must retain the above copyright
      11                 :             :  *     notice, this list of conditions and the following disclaimer.
      12                 :             :  *  2. 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                 :             :  *  3. Neither the name of the copyright holder nor the names of its 
      16                 :             :  *     contributors may be used to endorse or promote products derived from 
      17                 :             :  *     this software without specific prior written permission.
      18                 :             :  *
      19                 :             :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      20                 :             :  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      21                 :             :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      22                 :             :  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
      23                 :             :  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
      24                 :             :  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      25                 :             :  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
      26                 :             :  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
      27                 :             :  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
      28                 :             :  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
      29                 :             :  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      30                 :             :  */
      31                 :             : 
      32                 :             : #ifdef HAVE_CONFIG_H
      33                 :             : #include "config.h"
      34                 :             : #endif
      35                 :             : 
      36                 :             : #include <errno.h>
      37                 :             : #include <fcntl.h>
      38                 :             : #include <unistd.h>
      39                 :             : #include <stdlib.h>
      40                 :             : #include <limits.h>
      41                 :             : #include <sys/mman.h>
      42                 :             : #include <sys/vfs.h>
      43                 :             : 
      44                 :             : #include "vhd.h"
      45                 :             : #include "td-req.h"
      46                 :             : #include "tapdisk.h"
      47                 :             : #include "tapdisk-utils.h"
      48                 :             : #include "tapdisk-driver.h"
      49                 :             : #include "tapdisk-server.h"
      50                 :             : #include "tapdisk-interface.h"
      51                 :             : #include "tapdisk-disktype.h"
      52                 :             : 
      53                 :             : #define DEBUG 1
      54                 :             : 
      55                 :             : #ifdef DEBUG
      56                 :             : #define DBG(_f, _a...) tlog_write(TLOG_DBG, _f, ##_a)
      57                 :             : #else
      58                 :             : #define DBG(_f, _a...) ((void)0)
      59                 :             : #endif
      60                 :             : #define WARN(_f, _a...) tlog_syslog(TLOG_WARN, "WARNING: "_f "in %s:%d", \
      61                 :             :                                     ##_a, __func__, __LINE__)
      62                 :             : #define INFO(_f, _a...) tlog_syslog(TLOG_INFO, _f, ##_a)
      63                 :             : #define BUG()           td_panic()
      64                 :             : #define BUG_ON(_cond)   if (unlikely(_cond)) { td_panic(); }
      65                 :             : #define WARN_ON(_p)     if (unlikely(_cond)) { WARN(_cond); }
      66                 :             : 
      67                 :             : #define MIN(a, b)       ((a) < (b) ? (a) : (b))
      68                 :             : 
      69                 :             : #define TD_LCACHE_MAX_REQ               (MAX_REQUESTS*2)
      70                 :             : #define TD_LCACHE_BUFSZ                 (BLKIF_MAX_BUFFER_SEGMENTS_PER_REQUEST << PAGE_SHIFT)
      71                 :             : 
      72                 :             : 
      73                 :             : typedef struct lcache                   td_lcache_t;
      74                 :             : typedef struct lcache_request           td_lcache_req_t;
      75                 :             : 
      76                 :             : struct lcache_request {
      77                 :             :         char                           *buf;
      78                 :             :         int                             err;
      79                 :             : 
      80                 :             :         td_request_t                    treq;
      81                 :             :         int                             secs;
      82                 :             : 
      83                 :             :         td_vbd_request_t                vreq;
      84                 :             :         struct td_iovec                 iov;
      85                 :             : 
      86                 :             :         td_lcache_t                    *cache;
      87                 :             : };
      88                 :             : 
      89                 :             : struct lcache {
      90                 :             :         char                           *name;
      91                 :             : 
      92                 :             :         td_lcache_req_t                 reqv[TD_LCACHE_MAX_REQ];
      93                 :             :         td_lcache_req_t                *free[TD_LCACHE_MAX_REQ];
      94                 :             :         int                             n_free;
      95                 :             : 
      96                 :             :         char                           *buf;
      97                 :             :         size_t                          bufsz;
      98                 :             : 
      99                 :             :         int                             wr_en;
     100                 :             :         struct timeval                  ts;
     101                 :             : };
     102                 :             : 
     103                 :             : static td_lcache_req_t *
     104                 :           0 : lcache_alloc_request(td_lcache_t *cache)
     105                 :             : {
     106                 :           0 :         td_lcache_req_t *req = NULL;
     107                 :             : 
     108                 :           0 :         if (likely(cache->n_free))
     109                 :           0 :                 req = cache->free[--cache->n_free];
     110                 :             : 
     111                 :           0 :         return req;
     112                 :             : }
     113                 :             : 
     114                 :             : static void
     115                 :           0 : lcache_free_request(td_lcache_t *cache, td_lcache_req_t *req)
     116                 :             : {
     117         [ #  # ]:           0 :         BUG_ON(cache->n_free >= TD_LCACHE_MAX_REQ);
     118                 :           0 :         cache->free[cache->n_free++] = req;
     119                 :           0 : }
     120                 :             : 
     121                 :             : static void
     122                 :           0 : lcache_destroy_buffers(td_lcache_t *cache)
     123                 :             : {
     124                 :           0 :         td_lcache_req_t *req;
     125                 :             : 
     126                 :           0 :         do {
     127         [ #  # ]:           0 :                 req = lcache_alloc_request(cache);
     128         [ #  # ]:           0 :                 if (req)
     129                 :           0 :                         munmap(req->buf, (size_t)TD_REQ_BUFFER_SIZE);
     130         [ #  # ]:           0 :         } while (req);
     131                 :           0 : }
     132                 :             : 
     133                 :             : static int
     134                 :           0 : lcache_create_buffers(td_lcache_t *cache)
     135                 :             : {
     136                 :           0 :         int prot, flags, i, err;
     137                 :             : 
     138                 :           0 :         prot  = PROT_READ|PROT_WRITE;
     139                 :           0 :         flags = MAP_ANONYMOUS|MAP_PRIVATE|MAP_LOCKED;
     140                 :             : 
     141                 :           0 :         cache->n_free = 0;
     142                 :             : 
     143         [ #  # ]:           0 :         for (i = 0; i < TD_LCACHE_MAX_REQ; i++) {
     144                 :           0 :                 td_lcache_req_t *req = &cache->reqv[i];
     145                 :             : 
     146                 :           0 :                 req->buf = mmap(NULL, (size_t)TD_REQ_BUFFER_SIZE, prot, flags, -1, 0);
     147         [ #  # ]:           0 :                 if (req->buf == MAP_FAILED) {
     148                 :           0 :                         req->buf = NULL;
     149                 :           0 :                         err = -errno;
     150                 :           0 :                         goto fail;
     151                 :             :                 }
     152                 :             : 
     153                 :           0 :                 lcache_free_request(cache, req);
     154                 :             :         }
     155                 :             : 
     156                 :             :         return 0;
     157                 :             : 
     158                 :           0 : fail:
     159                 :           0 :         EPRINTF("Buffer init failure: %d", err);
     160                 :           0 :         lcache_destroy_buffers(cache);
     161                 :           0 :         return err;
     162                 :             : }
     163                 :             : 
     164                 :             : static int
     165                 :           0 : lcache_close(td_driver_t *driver)
     166                 :             : {
     167                 :           0 :         td_lcache_t *cache = driver->data;
     168                 :             : 
     169                 :           0 :         lcache_destroy_buffers(cache);
     170                 :             : 
     171                 :           0 :         free(cache->name);
     172                 :             : 
     173                 :           0 :         return 0;
     174                 :             : }
     175                 :             : 
     176                 :             : static int
     177                 :           0 : lcache_open(td_driver_t *driver, const char *name,
     178                 :             :             struct td_vbd_encryption *encryption, td_flag_t flags)
     179                 :             : {
     180                 :           0 :         td_lcache_t *cache = driver->data;
     181                 :           0 :         int err;
     182                 :             : 
     183                 :           0 :         err  = tapdisk_namedup(&cache->name, (char *)name);
     184         [ #  # ]:           0 :         if (err)
     185                 :           0 :                 goto fail;
     186                 :             : 
     187                 :           0 :         err = lcache_create_buffers(cache);
     188         [ #  # ]:           0 :         if (err)
     189                 :           0 :                 goto fail;
     190                 :             : 
     191                 :           0 :         timerclear(&cache->ts);
     192                 :           0 :         cache->wr_en = 1;
     193                 :             : 
     194                 :           0 :         return 0;
     195                 :             : 
     196                 :           0 : fail:
     197                 :           0 :         lcache_close(driver);
     198                 :           0 :         return err;
     199                 :             : }
     200                 :             : 
     201                 :             : /*
     202                 :             :  * NB. lcache->{wr_en,ts}: test free space in the caching SR before
     203                 :             :  * attempting to store our reads. VHD block allocation writes on Ext3
     204                 :             :  * have the nasty property of blocking excessively after running out
     205                 :             :  * of space. We therefore enable/disable ourselves at a 1/s
     206                 :             :  * granularity, querying free space through statfs beforehand.
     207                 :             :  */
     208                 :             : 
     209                 :             : static long
     210                 :           0 : lcache_fs_bfree(const td_lcache_t *cache, long *bsize)
     211                 :             : {
     212                 :           0 :         struct statfs fst;
     213                 :           0 :         int err;
     214                 :             : 
     215                 :           0 :         err = statfs(cache->name, &fst);
     216         [ #  # ]:           0 :         if (err)
     217                 :           0 :                 return err;
     218                 :             : 
     219         [ #  # ]:           0 :         if (likely(bsize))
     220                 :           0 :                 *bsize = fst.f_bsize;
     221                 :             : 
     222                 :           0 :         return MIN(fst.f_bfree, LONG_MAX);
     223                 :             : }
     224                 :             : 
     225                 :             : static int
     226                 :           0 : __lcache_wr_enabled(const td_lcache_t *cache)
     227                 :             : {
     228                 :           0 :         long threshold = 2<<20; /* B */
     229                 :           0 :         long bfree, bsz = 1;
     230                 :           0 :         int enable;
     231                 :             : 
     232                 :           0 :         bfree  = lcache_fs_bfree(cache, &bsz);
     233                 :           0 :         enable = bfree > threshold / bsz;
     234                 :             : 
     235                 :           0 :         return enable;
     236                 :             : }
     237                 :             : 
     238                 :             : static int
     239                 :           0 : lcache_wr_enabled(td_lcache_t *cache)
     240                 :             : {
     241                 :           0 :         const int timeout = 1; /* s */
     242                 :           0 :         struct timeval now, delta;
     243                 :             : 
     244                 :           0 :         gettimeofday(&now, NULL);
     245         [ #  # ]:           0 :         timersub(&now, &cache->ts, &delta);
     246                 :             : 
     247         [ #  # ]:           0 :         if (delta.tv_sec >= timeout) {
     248                 :           0 :                 cache->wr_en = __lcache_wr_enabled(cache);
     249                 :           0 :                 cache->ts    = now;
     250                 :             :         }
     251                 :             : 
     252                 :           0 :         return cache->wr_en;
     253                 :             : }
     254                 :             : 
     255                 :             : static void
     256                 :           0 : __lcache_write_cb(td_vbd_request_t *vreq, int error,
     257                 :             :                   void *token, int final)
     258                 :             : {
     259                 :           0 :         td_lcache_req_t *req = container_of(vreq, td_lcache_req_t, vreq);
     260                 :           0 :         td_lcache_t *cache = token;
     261                 :             : 
     262         [ #  # ]:           0 :         if (error == -ENOSPC)
     263                 :           0 :                 cache->wr_en = 0;
     264                 :             : 
     265                 :           0 :         lcache_free_request(cache, req);
     266                 :           0 : }
     267                 :             : 
     268                 :             : static void
     269                 :           0 : lcache_store_read(td_lcache_t *cache, td_lcache_req_t *req)
     270                 :             : {
     271                 :           0 :         td_vbd_request_t *vreq;
     272                 :           0 :         struct td_iovec *iov;
     273                 :           0 :         td_vbd_t *vbd;
     274                 :           0 :         int err;
     275                 :             : 
     276                 :           0 :         iov          = &req->iov;
     277                 :           0 :         iov->base    = req->buf;
     278                 :           0 :         iov->secs    = req->treq.secs;
     279                 :             : 
     280                 :           0 :         vreq         = &req->vreq;
     281                 :           0 :         vreq->op     = TD_OP_WRITE;
     282                 :           0 :         vreq->sec    = req->treq.sec;
     283                 :           0 :         vreq->iov    = iov;
     284                 :           0 :         vreq->iovcnt = 1;
     285                 :           0 :         vreq->cb     = __lcache_write_cb;
     286                 :           0 :         vreq->token  = cache;
     287                 :           0 :         vreq->skip_mirror = true;
     288                 :             : 
     289                 :           0 :         vbd = req->treq.vreq->vbd;
     290                 :             : 
     291                 :           0 :         err = tapdisk_vbd_queue_request(vbd, vreq);
     292         [ #  # ]:           0 :         BUG_ON(err);
     293                 :           0 : }
     294                 :             : 
     295                 :             : static void
     296                 :           0 : lcache_complete_read(td_lcache_t *cache, td_lcache_req_t *req)
     297                 :             : {
     298         [ #  # ]:           0 :         if (likely(!req->err)) {
     299                 :           0 :                 size_t sz = (size_t)req->treq.secs << SECTOR_SHIFT;
     300                 :           0 :                 memcpy(req->treq.buf, req->buf, sz);
     301                 :             :         }
     302                 :             : 
     303                 :           0 :         td_complete_request(req->treq, req->err);
     304                 :             : 
     305   [ #  #  #  # ]:           0 :         if (unlikely(req->err) || !lcache_wr_enabled(cache)) {
     306                 :           0 :                 lcache_free_request(cache, req);
     307                 :           0 :                 return;
     308                 :             :         }
     309                 :             : 
     310                 :           0 :         lcache_store_read(cache, req);
     311                 :             : }
     312                 :             : 
     313                 :             : static void
     314                 :           0 : __lcache_read_cb(td_request_t treq, int err)
     315                 :             : {
     316                 :           0 :         td_lcache_req_t *req = treq.cb_data;
     317                 :           0 :         td_lcache_t *cache = req->cache;
     318                 :             : 
     319         [ #  # ]:           0 :         BUG_ON(req->secs < treq.secs);
     320                 :           0 :         req->secs -= treq.secs;
     321         [ #  # ]:           0 :         req->err   = req->err ? : err;
     322                 :             : 
     323         [ #  # ]:           0 :         if (!req->secs)
     324                 :           0 :                 lcache_complete_read(cache, req);
     325                 :           0 : }
     326                 :             : 
     327                 :             : static void
     328                 :           0 : lcache_queue_read(td_driver_t *driver, td_request_t treq)
     329                 :             : {
     330                 :           0 :         td_lcache_t *cache = driver->data;
     331                 :           0 :         td_request_t clone;
     332                 :           0 :         td_lcache_req_t *req;
     333                 :             : 
     334         [ #  # ]:           0 :         req = lcache_alloc_request(cache);
     335         [ #  # ]:           0 :         if (!req) {
     336                 :           0 :                 td_complete_request(treq, -EBUSY);
     337                 :           0 :                 return;
     338                 :             :         }
     339                 :             : 
     340                 :           0 :         req->treq    = treq;
     341                 :           0 :         req->cache   = cache;
     342                 :             : 
     343                 :           0 :         req->secs    = req->treq.secs;
     344                 :           0 :         req->err     = 0;
     345                 :             : 
     346                 :           0 :         clone         = treq;
     347                 :           0 :         clone.buf     = req->buf;
     348                 :           0 :         clone.cb      = __lcache_read_cb;
     349                 :           0 :         clone.cb_data = req;
     350                 :             : 
     351                 :           0 :         td_forward_request(clone);
     352                 :             : }
     353                 :             : 
     354                 :             : static int
     355                 :           0 : lcache_get_parent_id(td_driver_t *driver, td_disk_id_t *id)
     356                 :             : {
     357                 :           0 :         return -EINVAL;
     358                 :             : }
     359                 :             : 
     360                 :             : static int
     361                 :           0 : lcache_validate_parent(td_driver_t *driver,
     362                 :             :                        td_driver_t *pdriver, td_flag_t flags)
     363                 :             : {
     364                 :             :         /* Check types for both */
     365         [ #  # ]:           0 :         if (driver->type != DISK_TYPE_LCACHE ||
     366         [ #  # ]:           0 :             pdriver->type != DISK_TYPE_VHD) {
     367                 :           0 :                 EPRINTF("Unexpected driver types. %s:%d, parent %s:%d",
     368                 :             :                         tapdisk_disk_types[driver->type]->name,
     369                 :             :                         driver->type,
     370                 :             :                         tapdisk_disk_types[pdriver->type]->name,
     371                 :             :                         pdriver->type);
     372                 :           0 :                 return -EINVAL;
     373                 :             :         }
     374                 :             : 
     375                 :             :         /*
     376                 :             :           Checking names needs to be more selective if required
     377                 :             :         if (strcmp(driver->name, pdriver->name))
     378                 :             :                 return -EINVAL;
     379                 :             :         */
     380                 :             : 
     381                 :             :         return 0;
     382                 :             : }
     383                 :             : 
     384                 :             : struct tap_disk tapdisk_lcache = {
     385                 :             :         .disk_type                  = "tapdisk_lcache",
     386                 :             :         .flags                      = 0,
     387                 :             :         .private_data_size          = sizeof(td_lcache_t),
     388                 :             :         .td_open                    = lcache_open,
     389                 :             :         .td_close                   = lcache_close,
     390                 :             :         .td_queue_read              = lcache_queue_read,
     391                 :             :         .td_get_parent_id           = lcache_get_parent_id,
     392                 :             :         .td_validate_parent         = lcache_validate_parent,
     393                 :             : };
        

Generated by: LCOV version 2.0-1