mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-28 11:22:15 +03:00
1570e76233
Add a "device index" (di) for each device, and use this in the bcache api to the rest of lvm. This replaces the file descriptor (fd) in the api. The rest of lvm uses new functions bcache_set_fd(), bcache_clear_fd(), and bcache_change_fd() to control which fd bcache uses for io to a particular device. . lvm opens a dev and gets and fd. fd = open(dev); . lvm passes fd to the bcache layer and gets a di to use in the bcache api for the dev. di = bcache_set_fd(fd); . lvm uses bcache functions, passing di for the dev. bcache_write_bytes(di, ...), etc. . bcache translates di to fd to do io. . lvm closes the device and clears the di/fd bcache state. close(fd); bcache_clear_fd(di); In the bcache layer, a di-to-fd translation table (int *_fd_table) is added. When bcache needs to perform io on a di, it uses _fd_table[di]. In the following commit, lvm will make use of the new bcache_change_fd() function to change the fd that bcache uses for the dev, without dropping cached blocks.
171 lines
5.2 KiB
C
171 lines
5.2 KiB
C
/*
|
|
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
|
*
|
|
* This file is part of LVM2.
|
|
*
|
|
* This copyrighted material is made available to anyone wishing to use,
|
|
* modify, copy, or redistribute it subject to the terms and conditions
|
|
* of the GNU Lesser General Public License v.2.1.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#ifndef BCACHE_H
|
|
#define BCACHE_H
|
|
|
|
#include "device_mapper/all.h"
|
|
#include "base/memory/container_of.h"
|
|
|
|
#include <linux/fs.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
|
|
enum dir {
|
|
DIR_READ,
|
|
DIR_WRITE
|
|
};
|
|
|
|
typedef uint64_t block_address;
|
|
typedef uint64_t sector_t;
|
|
|
|
typedef void io_complete_fn(void *context, int io_error);
|
|
|
|
struct io_engine {
|
|
void (*destroy)(struct io_engine *e);
|
|
bool (*issue)(struct io_engine *e, enum dir d, int di,
|
|
sector_t sb, sector_t se, void *data, void *context);
|
|
bool (*wait)(struct io_engine *e, io_complete_fn fn);
|
|
unsigned (*max_io)(struct io_engine *e);
|
|
};
|
|
|
|
struct io_engine *create_async_io_engine(void);
|
|
struct io_engine *create_sync_io_engine(void);
|
|
|
|
/*----------------------------------------------------------------*/
|
|
|
|
struct bcache;
|
|
struct block {
|
|
/* clients may only access these three fields */
|
|
int di;
|
|
uint64_t index;
|
|
void *data;
|
|
|
|
struct bcache *cache;
|
|
struct dm_list list;
|
|
|
|
unsigned flags;
|
|
unsigned ref_count;
|
|
int error;
|
|
enum dir io_dir;
|
|
};
|
|
|
|
/*
|
|
* Ownership of engine passes. Engine will be destroyed even if this fails.
|
|
*/
|
|
struct bcache *bcache_create(sector_t block_size, unsigned nr_cache_blocks,
|
|
struct io_engine *engine);
|
|
void bcache_destroy(struct bcache *cache);
|
|
|
|
enum bcache_get_flags {
|
|
/*
|
|
* The block will be zeroed before get_block returns it. This
|
|
* potentially avoids a read if the block is not already in the cache.
|
|
* GF_DIRTY is implicit.
|
|
*/
|
|
GF_ZERO = (1 << 0),
|
|
|
|
/*
|
|
* Indicates the caller is intending to change the data in the block, a
|
|
* writeback will occur after the block is released.
|
|
*/
|
|
GF_DIRTY = (1 << 1)
|
|
};
|
|
|
|
sector_t bcache_block_sectors(struct bcache *cache);
|
|
unsigned bcache_nr_cache_blocks(struct bcache *cache);
|
|
unsigned bcache_max_prefetches(struct bcache *cache);
|
|
|
|
/*
|
|
* Use the prefetch method to take advantage of asynchronous IO. For example,
|
|
* if you wanted to read a block from many devices concurrently you'd do
|
|
* something like this:
|
|
*
|
|
* dm_list_iterate_items (dev, &devices)
|
|
* bcache_prefetch(cache, dev->fd, block);
|
|
*
|
|
* dm_list_iterate_items (dev, &devices) {
|
|
* if (!bcache_get(cache, dev->fd, block, &b))
|
|
* fail();
|
|
*
|
|
* process_block(b);
|
|
* }
|
|
*
|
|
* It's slightly sub optimal, since you may not run the gets in the order that
|
|
* they complete. But we're talking a very small difference, and it's worth it
|
|
* to keep callbacks out of this interface.
|
|
*/
|
|
void bcache_prefetch(struct bcache *cache, int di, block_address index);
|
|
|
|
/*
|
|
* Returns true on success.
|
|
*/
|
|
bool bcache_get(struct bcache *cache, int di, block_address index,
|
|
unsigned flags, struct block **result);
|
|
void bcache_put(struct block *b);
|
|
|
|
/*
|
|
* flush() does not attempt to writeback locked blocks. flush will fail
|
|
* (return false), if any unlocked dirty data cannot be written back.
|
|
*/
|
|
bool bcache_flush(struct bcache *cache);
|
|
|
|
/*
|
|
* Removes a block from the cache.
|
|
*
|
|
* If the block is dirty it will be written back first. If the writeback fails
|
|
* false will be returned.
|
|
*
|
|
* If the block is currently held false will be returned.
|
|
*/
|
|
bool bcache_invalidate(struct bcache *cache, int di, block_address index);
|
|
|
|
/*
|
|
* Invalidates all blocks on the given descriptor. Call this before closing
|
|
* the descriptor to make sure everything is written back.
|
|
*/
|
|
bool bcache_invalidate_di(struct bcache *cache, int di);
|
|
|
|
/*
|
|
* Call this function if flush, or invalidate fail and you do not
|
|
* wish to retry the writes. This will throw away any dirty data
|
|
* not written. If any blocks for di are held, then it will call
|
|
* abort().
|
|
*/
|
|
void bcache_abort_di(struct bcache *cache, int di);
|
|
|
|
//----------------------------------------------------------------
|
|
// The next four functions are utilities written in terms of the above api.
|
|
|
|
// Prefetches the blocks neccessary to satisfy a byte range.
|
|
void bcache_prefetch_bytes(struct bcache *cache, int di, uint64_t start, size_t len);
|
|
|
|
// Reads, writes and zeroes bytes. Returns false if errors occur.
|
|
bool bcache_read_bytes(struct bcache *cache, int di, uint64_t start, size_t len, void *data);
|
|
bool bcache_write_bytes(struct bcache *cache, int di, uint64_t start, size_t len, void *data);
|
|
bool bcache_zero_bytes(struct bcache *cache, int di, uint64_t start, size_t len);
|
|
bool bcache_set_bytes(struct bcache *cache, int di, uint64_t start, size_t len, uint8_t val);
|
|
bool bcache_invalidate_bytes(struct bcache *cache, int di, uint64_t start, size_t len);
|
|
|
|
void bcache_set_last_byte(struct bcache *cache, int di, uint64_t offset, int sector_size);
|
|
void bcache_unset_last_byte(struct bcache *cache, int di);
|
|
|
|
//----------------------------------------------------------------
|
|
|
|
int bcache_set_fd(int fd); /* returns di */
|
|
void bcache_clear_fd(int di);
|
|
int bcache_change_fd(int di, int fd);
|
|
|
|
#endif
|