summaryrefslogtreecommitdiffstats
path: root/src/spdk/lib/ftl/ftl_core.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdk/lib/ftl/ftl_core.h')
-rw-r--r--src/spdk/lib/ftl/ftl_core.h552
1 files changed, 552 insertions, 0 deletions
diff --git a/src/spdk/lib/ftl/ftl_core.h b/src/spdk/lib/ftl/ftl_core.h
new file mode 100644
index 000000000..b782ba731
--- /dev/null
+++ b/src/spdk/lib/ftl/ftl_core.h
@@ -0,0 +1,552 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FTL_CORE_H
+#define FTL_CORE_H
+
+#include "spdk/stdinc.h"
+#include "spdk/uuid.h"
+#include "spdk/thread.h"
+#include "spdk/util.h"
+#include "spdk_internal/log.h"
+#include "spdk/likely.h"
+#include "spdk/queue.h"
+#include "spdk/ftl.h"
+#include "spdk/bdev.h"
+#include "spdk/bdev_zone.h"
+
+#include "ftl_addr.h"
+#include "ftl_io.h"
+#include "ftl_trace.h"
+
+#ifdef SPDK_CONFIG_PMDK
+#include "libpmem.h"
+#endif /* SPDK_CONFIG_PMDK */
+
+struct spdk_ftl_dev;
+struct ftl_band;
+struct ftl_zone;
+struct ftl_io;
+struct ftl_restore;
+struct ftl_wptr;
+struct ftl_flush;
+struct ftl_reloc;
+struct ftl_anm_event;
+struct ftl_band_flush;
+
+struct ftl_stats {
+ /* Number of writes scheduled directly by the user */
+ uint64_t write_user;
+
+ /* Total number of writes */
+ uint64_t write_total;
+
+ /* Traces */
+ struct ftl_trace trace;
+
+ /* Number of limits applied */
+ uint64_t limits[SPDK_FTL_LIMIT_MAX];
+};
+
+struct ftl_global_md {
+ /* Device instance */
+ struct spdk_uuid uuid;
+ /* Size of the l2p table */
+ uint64_t num_lbas;
+};
+
+struct ftl_nv_cache {
+ /* Write buffer cache bdev */
+ struct spdk_bdev_desc *bdev_desc;
+ /* Write pointer */
+ uint64_t current_addr;
+ /* Number of available blocks left */
+ uint64_t num_available;
+ /* Maximum number of blocks */
+ uint64_t num_data_blocks;
+ /*
+ * Phase of the current cycle of writes. Each time whole cache area is filled, the phase is
+ * advanced. Current phase is saved in every IO's metadata, as well as in the header saved
+ * in the first sector. By looking at the phase of each block, it's possible to find the
+ * oldest block and replay the order of the writes when recovering the data from the cache.
+ */
+ unsigned int phase;
+ /* Indicates that the data can be written to the cache */
+ bool ready;
+ /* Metadata pool */
+ struct spdk_mempool *md_pool;
+ /* DMA buffer for writing the header */
+ void *dma_buf;
+ /* Cache lock */
+ pthread_spinlock_t lock;
+};
+
+struct ftl_batch {
+ /* Queue of write buffer entries, can reach up to xfer_size entries */
+ TAILQ_HEAD(, ftl_wbuf_entry) entries;
+ /* Number of entries in the queue above */
+ uint32_t num_entries;
+ /* Index within spdk_ftl_dev.batch_array */
+ uint32_t index;
+ struct iovec *iov;
+ void *metadata;
+ TAILQ_ENTRY(ftl_batch) tailq;
+};
+
+struct spdk_ftl_dev {
+ /* Device instance */
+ struct spdk_uuid uuid;
+ /* Device name */
+ char *name;
+ /* Configuration */
+ struct spdk_ftl_conf conf;
+
+ /* Indicates the device is fully initialized */
+ int initialized;
+ /* Indicates the device is about to be stopped */
+ int halt;
+ /* Indicates the device is about to start stopping - use to handle multiple stop request */
+ bool halt_started;
+
+ /* Underlying device */
+ struct spdk_bdev_desc *base_bdev_desc;
+
+ /* Non-volatile write buffer cache */
+ struct ftl_nv_cache nv_cache;
+
+ /* LBA map memory pool */
+ struct spdk_mempool *lba_pool;
+
+ /* LBA map requests pool */
+ struct spdk_mempool *lba_request_pool;
+
+ /* Media management events pool */
+ struct spdk_mempool *media_events_pool;
+
+ /* Statistics */
+ struct ftl_stats stats;
+
+ /* Current sequence number */
+ uint64_t seq;
+
+ /* Array of bands */
+ struct ftl_band *bands;
+ /* Number of operational bands */
+ size_t num_bands;
+ /* Next write band */
+ struct ftl_band *next_band;
+ /* Free band list */
+ LIST_HEAD(, ftl_band) free_bands;
+ /* Closed bands list */
+ LIST_HEAD(, ftl_band) shut_bands;
+ /* Number of free bands */
+ size_t num_free;
+
+ /* List of write pointers */
+ LIST_HEAD(, ftl_wptr) wptr_list;
+
+ /* Logical -> physical table */
+ void *l2p;
+ /* Size of the l2p table */
+ uint64_t num_lbas;
+ /* Size of pages mmapped for l2p, valid only for mapping on persistent memory */
+ size_t l2p_pmem_len;
+
+ /* Address size */
+ size_t addr_len;
+
+ /* Flush list */
+ LIST_HEAD(, ftl_flush) flush_list;
+ /* List of band flush requests */
+ LIST_HEAD(, ftl_band_flush) band_flush_list;
+
+ /* Device specific md buffer */
+ struct ftl_global_md global_md;
+
+ /* Metadata size */
+ size_t md_size;
+ void *md_buf;
+
+ /* Transfer unit size */
+ size_t xfer_size;
+
+ /* Current user write limit */
+ int limit;
+
+ /* Inflight IO operations */
+ uint32_t num_inflight;
+
+ /* Manages data relocation */
+ struct ftl_reloc *reloc;
+
+ /* Thread on which the poller is running */
+ struct spdk_thread *core_thread;
+ /* IO channel */
+ struct spdk_io_channel *ioch;
+ /* Poller */
+ struct spdk_poller *core_poller;
+
+ /* IO channel array provides means for retrieving write buffer entries
+ * from their address stored in L2P. The address is divided into two
+ * parts - IO channel offset poining at specific IO channel (within this
+ * array) and entry offset pointing at specific entry within that IO
+ * channel.
+ */
+ struct ftl_io_channel **ioch_array;
+ TAILQ_HEAD(, ftl_io_channel) ioch_queue;
+ uint64_t num_io_channels;
+ /* Value required to shift address of a write buffer entry to retrieve
+ * the IO channel it's part of. The other part of the address describes
+ * the offset of an entry within the IO channel's entry array.
+ */
+ uint64_t ioch_shift;
+
+ /* Write buffer batches */
+#define FTL_BATCH_COUNT 4096
+ struct ftl_batch batch_array[FTL_BATCH_COUNT];
+ /* Iovec buffer used by batches */
+ struct iovec *iov_buf;
+ /* Batch currently being filled */
+ struct ftl_batch *current_batch;
+ /* Full and ready to be sent batches. A batch is put on this queue in
+ * case it's already filled, but cannot be sent.
+ */
+ TAILQ_HEAD(, ftl_batch) pending_batches;
+ TAILQ_HEAD(, ftl_batch) free_batches;
+
+ /* Devices' list */
+ STAILQ_ENTRY(spdk_ftl_dev) stailq;
+};
+
+struct ftl_nv_cache_header {
+ /* Version of the header */
+ uint32_t version;
+ /* UUID of the FTL device */
+ struct spdk_uuid uuid;
+ /* Size of the non-volatile cache (in blocks) */
+ uint64_t size;
+ /* Contains the next address to be written after clean shutdown, invalid LBA otherwise */
+ uint64_t current_addr;
+ /* Current phase */
+ uint8_t phase;
+ /* Checksum of the header, needs to be last element */
+ uint32_t checksum;
+} __attribute__((packed));
+
+struct ftl_media_event {
+ /* Owner */
+ struct spdk_ftl_dev *dev;
+ /* Media event */
+ struct spdk_bdev_media_event event;
+};
+
+typedef void (*ftl_restore_fn)(struct ftl_restore *, int, void *cb_arg);
+
+void ftl_apply_limits(struct spdk_ftl_dev *dev);
+void ftl_io_read(struct ftl_io *io);
+void ftl_io_write(struct ftl_io *io);
+int ftl_flush_wbuf(struct spdk_ftl_dev *dev, spdk_ftl_fn cb_fn, void *cb_arg);
+int ftl_current_limit(const struct spdk_ftl_dev *dev);
+int ftl_invalidate_addr(struct spdk_ftl_dev *dev, struct ftl_addr addr);
+int ftl_task_core(void *ctx);
+int ftl_task_read(void *ctx);
+void ftl_process_anm_event(struct ftl_anm_event *event);
+size_t ftl_tail_md_num_blocks(const struct spdk_ftl_dev *dev);
+size_t ftl_tail_md_hdr_num_blocks(void);
+size_t ftl_vld_map_num_blocks(const struct spdk_ftl_dev *dev);
+size_t ftl_lba_map_num_blocks(const struct spdk_ftl_dev *dev);
+size_t ftl_head_md_num_blocks(const struct spdk_ftl_dev *dev);
+int ftl_restore_md(struct spdk_ftl_dev *dev, ftl_restore_fn cb, void *cb_arg);
+int ftl_restore_device(struct ftl_restore *restore, ftl_restore_fn cb, void *cb_arg);
+void ftl_restore_nv_cache(struct ftl_restore *restore, ftl_restore_fn cb, void *cb_arg);
+int ftl_band_set_direct_access(struct ftl_band *band, bool access);
+bool ftl_addr_is_written(struct ftl_band *band, struct ftl_addr addr);
+int ftl_flush_active_bands(struct spdk_ftl_dev *dev, spdk_ftl_fn cb_fn, void *cb_arg);
+int ftl_nv_cache_write_header(struct ftl_nv_cache *nv_cache, bool shutdown,
+ spdk_bdev_io_completion_cb cb_fn, void *cb_arg);
+int ftl_nv_cache_scrub(struct ftl_nv_cache *nv_cache, spdk_bdev_io_completion_cb cb_fn,
+ void *cb_arg);
+void ftl_get_media_events(struct spdk_ftl_dev *dev);
+int ftl_io_channel_poll(void *arg);
+void ftl_evict_cache_entry(struct spdk_ftl_dev *dev, struct ftl_wbuf_entry *entry);
+struct spdk_io_channel *ftl_get_io_channel(const struct spdk_ftl_dev *dev);
+struct ftl_io_channel *ftl_io_channel_get_ctx(struct spdk_io_channel *ioch);
+
+
+#define ftl_to_addr(address) \
+ (struct ftl_addr) { .offset = (uint64_t)(address) }
+
+#define ftl_to_addr_packed(address) \
+ (struct ftl_addr) { .pack.offset = (uint32_t)(address) }
+
+static inline struct spdk_thread *
+ftl_get_core_thread(const struct spdk_ftl_dev *dev)
+{
+ return dev->core_thread;
+}
+
+static inline size_t
+ftl_get_num_bands(const struct spdk_ftl_dev *dev)
+{
+ return dev->num_bands;
+}
+
+static inline size_t
+ftl_get_num_punits(const struct spdk_ftl_dev *dev)
+{
+ return spdk_bdev_get_optimal_open_zones(spdk_bdev_desc_get_bdev(dev->base_bdev_desc));
+}
+
+static inline size_t
+ftl_get_num_zones(const struct spdk_ftl_dev *dev)
+{
+ return ftl_get_num_bands(dev) * ftl_get_num_punits(dev);
+}
+
+static inline size_t
+ftl_get_num_blocks_in_zone(const struct spdk_ftl_dev *dev)
+{
+ return spdk_bdev_get_zone_size(spdk_bdev_desc_get_bdev(dev->base_bdev_desc));
+}
+
+static inline uint64_t
+ftl_get_num_blocks_in_band(const struct spdk_ftl_dev *dev)
+{
+ return ftl_get_num_punits(dev) * ftl_get_num_blocks_in_zone(dev);
+}
+
+static inline uint64_t
+ftl_addr_get_zone_slba(const struct spdk_ftl_dev *dev, struct ftl_addr addr)
+{
+ return addr.offset -= (addr.offset % ftl_get_num_blocks_in_zone(dev));
+}
+
+static inline uint64_t
+ftl_addr_get_band(const struct spdk_ftl_dev *dev, struct ftl_addr addr)
+{
+ return addr.offset / ftl_get_num_blocks_in_band(dev);
+}
+
+static inline uint64_t
+ftl_addr_get_punit(const struct spdk_ftl_dev *dev, struct ftl_addr addr)
+{
+ return (addr.offset / ftl_get_num_blocks_in_zone(dev)) % ftl_get_num_punits(dev);
+}
+
+static inline uint64_t
+ftl_addr_get_zone_offset(const struct spdk_ftl_dev *dev, struct ftl_addr addr)
+{
+ return addr.offset % ftl_get_num_blocks_in_zone(dev);
+}
+
+static inline size_t
+ftl_vld_map_size(const struct spdk_ftl_dev *dev)
+{
+ return (size_t)spdk_divide_round_up(ftl_get_num_blocks_in_band(dev), CHAR_BIT);
+}
+
+static inline int
+ftl_addr_packed(const struct spdk_ftl_dev *dev)
+{
+ return dev->addr_len < 32;
+}
+
+static inline void
+ftl_l2p_lba_persist(const struct spdk_ftl_dev *dev, uint64_t lba)
+{
+#ifdef SPDK_CONFIG_PMDK
+ size_t ftl_addr_size = ftl_addr_packed(dev) ? 4 : 8;
+ pmem_persist((char *)dev->l2p + (lba * ftl_addr_size), ftl_addr_size);
+#else /* SPDK_CONFIG_PMDK */
+ SPDK_ERRLOG("Libpmem not available, cannot flush l2p to pmem\n");
+ assert(0);
+#endif /* SPDK_CONFIG_PMDK */
+}
+
+static inline int
+ftl_addr_invalid(struct ftl_addr addr)
+{
+ return addr.offset == ftl_to_addr(FTL_ADDR_INVALID).offset;
+}
+
+static inline int
+ftl_addr_cached(struct ftl_addr addr)
+{
+ return !ftl_addr_invalid(addr) && addr.cached;
+}
+
+static inline struct ftl_addr
+ftl_addr_to_packed(const struct spdk_ftl_dev *dev, struct ftl_addr addr)
+{
+ struct ftl_addr p = {};
+
+ if (ftl_addr_invalid(addr)) {
+ p = ftl_to_addr_packed(FTL_ADDR_INVALID);
+ } else if (ftl_addr_cached(addr)) {
+ p.pack.cached = 1;
+ p.pack.cache_offset = (uint32_t) addr.cache_offset;
+ } else {
+ p.pack.offset = (uint32_t) addr.offset;
+ }
+
+ return p;
+}
+
+static inline struct ftl_addr
+ftl_addr_from_packed(const struct spdk_ftl_dev *dev, struct ftl_addr p)
+{
+ struct ftl_addr addr = {};
+
+ if (p.pack.offset == (uint32_t)FTL_ADDR_INVALID) {
+ addr = ftl_to_addr(FTL_ADDR_INVALID);
+ } else if (p.pack.cached) {
+ addr.cached = 1;
+ addr.cache_offset = p.pack.cache_offset;
+ } else {
+ addr = p;
+ }
+
+ return addr;
+}
+
+#define _ftl_l2p_set(l2p, off, val, bits) \
+ __atomic_store_n(((uint##bits##_t *)(l2p)) + (off), val, __ATOMIC_SEQ_CST)
+
+#define _ftl_l2p_set32(l2p, off, val) \
+ _ftl_l2p_set(l2p, off, val, 32)
+
+#define _ftl_l2p_set64(l2p, off, val) \
+ _ftl_l2p_set(l2p, off, val, 64)
+
+#define _ftl_l2p_get(l2p, off, bits) \
+ __atomic_load_n(((uint##bits##_t *)(l2p)) + (off), __ATOMIC_SEQ_CST)
+
+#define _ftl_l2p_get32(l2p, off) \
+ _ftl_l2p_get(l2p, off, 32)
+
+#define _ftl_l2p_get64(l2p, off) \
+ _ftl_l2p_get(l2p, off, 64)
+
+#define ftl_addr_cmp(p1, p2) \
+ ((p1).offset == (p2).offset)
+
+static inline void
+ftl_l2p_set(struct spdk_ftl_dev *dev, uint64_t lba, struct ftl_addr addr)
+{
+ assert(dev->num_lbas > lba);
+
+ if (ftl_addr_packed(dev)) {
+ _ftl_l2p_set32(dev->l2p, lba, ftl_addr_to_packed(dev, addr).offset);
+ } else {
+ _ftl_l2p_set64(dev->l2p, lba, addr.offset);
+ }
+
+ if (dev->l2p_pmem_len != 0) {
+ ftl_l2p_lba_persist(dev, lba);
+ }
+}
+
+static inline struct ftl_addr
+ftl_l2p_get(struct spdk_ftl_dev *dev, uint64_t lba)
+{
+ assert(dev->num_lbas > lba);
+
+ if (ftl_addr_packed(dev)) {
+ return ftl_addr_from_packed(dev, ftl_to_addr_packed(
+ _ftl_l2p_get32(dev->l2p, lba)));
+ } else {
+ return ftl_to_addr(_ftl_l2p_get64(dev->l2p, lba));
+ }
+}
+
+static inline bool
+ftl_dev_has_nv_cache(const struct spdk_ftl_dev *dev)
+{
+ return dev->nv_cache.bdev_desc != NULL;
+}
+
+#define FTL_NV_CACHE_HEADER_VERSION (1)
+#define FTL_NV_CACHE_DATA_OFFSET (1)
+#define FTL_NV_CACHE_PHASE_OFFSET (62)
+#define FTL_NV_CACHE_PHASE_COUNT (4)
+#define FTL_NV_CACHE_PHASE_MASK (3ULL << FTL_NV_CACHE_PHASE_OFFSET)
+#define FTL_NV_CACHE_LBA_INVALID (FTL_LBA_INVALID & ~FTL_NV_CACHE_PHASE_MASK)
+
+static inline bool
+ftl_nv_cache_phase_is_valid(unsigned int phase)
+{
+ return phase > 0 && phase <= 3;
+}
+
+static inline unsigned int
+ftl_nv_cache_next_phase(unsigned int current)
+{
+ static const unsigned int phases[] = { 0, 2, 3, 1 };
+ assert(ftl_nv_cache_phase_is_valid(current));
+ return phases[current];
+}
+
+static inline unsigned int
+ftl_nv_cache_prev_phase(unsigned int current)
+{
+ static const unsigned int phases[] = { 0, 3, 1, 2 };
+ assert(ftl_nv_cache_phase_is_valid(current));
+ return phases[current];
+}
+
+static inline uint64_t
+ftl_nv_cache_pack_lba(uint64_t lba, unsigned int phase)
+{
+ assert(ftl_nv_cache_phase_is_valid(phase));
+ return (lba & ~FTL_NV_CACHE_PHASE_MASK) | ((uint64_t)phase << FTL_NV_CACHE_PHASE_OFFSET);
+}
+
+static inline void
+ftl_nv_cache_unpack_lba(uint64_t in_lba, uint64_t *out_lba, unsigned int *phase)
+{
+ *out_lba = in_lba & ~FTL_NV_CACHE_PHASE_MASK;
+ *phase = (in_lba & FTL_NV_CACHE_PHASE_MASK) >> FTL_NV_CACHE_PHASE_OFFSET;
+
+ /* If the phase is invalid the block wasn't written yet, so treat the LBA as invalid too */
+ if (!ftl_nv_cache_phase_is_valid(*phase) || *out_lba == FTL_NV_CACHE_LBA_INVALID) {
+ *out_lba = FTL_LBA_INVALID;
+ }
+}
+
+static inline bool
+ftl_is_append_supported(const struct spdk_ftl_dev *dev)
+{
+ return dev->conf.use_append;
+}
+
+#endif /* FTL_CORE_H */