/*- * 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_IO_H #define FTL_IO_H #include "spdk/stdinc.h" #include "spdk/nvme.h" #include "spdk/ftl.h" #include "ftl_addr.h" #include "ftl_trace.h" struct spdk_ftl_dev; struct ftl_band; struct ftl_batch; struct ftl_io; typedef int (*ftl_md_pack_fn)(struct ftl_band *); typedef void (*ftl_io_fn)(struct ftl_io *, void *, int); /* IO flags */ enum ftl_io_flags { /* Indicates whether IO is already initialized */ FTL_IO_INITIALIZED = (1 << 0), /* Internal based IO (defrag, metadata etc.) */ FTL_IO_INTERNAL = (1 << 1), /* Indicates that the IO should not go through if there's */ /* already another one scheduled to the same LBA */ FTL_IO_WEAK = (1 << 2), /* Indicates that the IO is used for padding */ FTL_IO_PAD = (1 << 3), /* The IO operates on metadata */ FTL_IO_MD = (1 << 4), /* Using physical instead of logical address */ FTL_IO_PHYSICAL_MODE = (1 << 5), /* Indicates that IO contains noncontiguous LBAs */ FTL_IO_VECTOR_LBA = (1 << 6), /* The IO is directed to non-volatile cache */ FTL_IO_CACHE = (1 << 7), /* Indicates that physical address should be taken from IO struct, */ /* not assigned by wptr, only works if wptr is also in direct mode */ FTL_IO_DIRECT_ACCESS = (1 << 8), /* Bypass the non-volatile cache */ FTL_IO_BYPASS_CACHE = (1 << 9), }; enum ftl_io_type { FTL_IO_READ, FTL_IO_WRITE, FTL_IO_ERASE, }; #define FTL_IO_MAX_IOVEC 64 struct ftl_io_init_opts { struct spdk_ftl_dev *dev; /* IO descriptor */ struct ftl_io *io; /* Parent request */ struct ftl_io *parent; /* Size of IO descriptor */ size_t size; /* IO flags */ int flags; /* IO type */ enum ftl_io_type type; /* Transfer batch, set for IO going through the write buffer */ struct ftl_batch *batch; /* Band to which the IO is directed */ struct ftl_band *band; /* Number of logical blocks */ size_t num_blocks; /* Data */ struct iovec iovs[FTL_IO_MAX_IOVEC]; int iovcnt; /* Metadata */ void *md; /* Callback's function */ ftl_io_fn cb_fn; /* Callback's context */ void *cb_ctx; }; struct ftl_io_channel; struct ftl_wbuf_entry { /* IO channel that owns the write bufer entry */ struct ftl_io_channel *ioch; /* Data payload (single block) */ void *payload; /* Index within the IO channel's wbuf_entries array */ uint32_t index; uint32_t io_flags; /* Points at the band the data is copied from. Only valid for internal * requests coming from reloc. */ struct ftl_band *band; /* Physical address of that particular block. Valid once the data has * been written out. */ struct ftl_addr addr; /* Logical block address */ uint64_t lba; /* Trace ID of the requests the entry is part of */ uint64_t trace; /* Indicates that the entry was written out and is still present in the * L2P table. */ bool valid; /* Lock that protects the entry from being evicted from the L2P */ pthread_spinlock_t lock; TAILQ_ENTRY(ftl_wbuf_entry) tailq; }; #define FTL_IO_CHANNEL_INDEX_INVALID ((uint64_t)-1) struct ftl_io_channel { /* Device */ struct spdk_ftl_dev *dev; /* IO pool element size */ size_t elem_size; /* Index within the IO channel array */ uint64_t index; /* IO pool */ struct spdk_mempool *io_pool; /* Underlying device IO channel */ struct spdk_io_channel *base_ioch; /* Persistent cache IO channel */ struct spdk_io_channel *cache_ioch; /* Poller used for completing write requests and retrying IO */ struct spdk_poller *poller; /* Write completion queue */ TAILQ_HEAD(, ftl_io) write_cmpl_queue; TAILQ_HEAD(, ftl_io) retry_queue; TAILQ_ENTRY(ftl_io_channel) tailq; /* Array of write buffer entries */ struct ftl_wbuf_entry *wbuf_entries; /* Write buffer data payload */ void *wbuf_payload; /* Number of write buffer entries */ uint32_t num_entries; /* Write buffer queues */ struct spdk_ring *free_queue; struct spdk_ring *submit_queue; /* Maximum number of concurrent user writes */ uint32_t qdepth_limit; /* Current number of concurrent user writes */ uint32_t qdepth_current; /* Means that the IO channel is being flushed */ bool flush; }; /* General IO descriptor */ struct ftl_io { /* Device */ struct spdk_ftl_dev *dev; /* IO channel */ struct spdk_io_channel *ioch; union { /* LBA table */ uint64_t *vector; /* First LBA */ uint64_t single; } lba; /* First block address */ struct ftl_addr addr; /* Number of processed blocks */ size_t pos; /* Number of blocks */ size_t num_blocks; /* IO vector pointer */ struct iovec *iov; /* IO vector buffer for internal requests */ struct iovec iov_buf[FTL_IO_MAX_IOVEC]; /* Metadata */ void *md; /* Number of IO vectors */ size_t iov_cnt; /* Position within the iovec */ size_t iov_pos; /* Offset within the iovec (in blocks) */ size_t iov_off; /* Transfer batch (valid only for writes going through the write buffer) */ struct ftl_batch *batch; /* Band this IO is being written to */ struct ftl_band *band; /* Request status */ int status; /* Number of split requests */ size_t req_cnt; /* Callback's function */ ftl_io_fn cb_fn; /* Callback's context */ void *cb_ctx; /* User callback function */ spdk_ftl_fn user_fn; /* Flags */ int flags; /* IO type */ enum ftl_io_type type; /* Done flag */ bool done; /* Parent request */ struct ftl_io *parent; /* Child requests list */ LIST_HEAD(, ftl_io) children; /* Child list link */ LIST_ENTRY(ftl_io) child_entry; /* Children lock */ pthread_spinlock_t lock; /* Trace group id */ uint64_t trace; /* Used by retry and write completion queues */ TAILQ_ENTRY(ftl_io) ioch_entry; }; /* Metadata IO */ struct ftl_md_io { /* Parent IO structure */ struct ftl_io io; /* Serialization/deserialization callback */ ftl_md_pack_fn pack_fn; /* Callback's function */ ftl_io_fn cb_fn; /* Callback's context */ void *cb_ctx; }; static inline bool ftl_io_mode_physical(const struct ftl_io *io) { return io->flags & FTL_IO_PHYSICAL_MODE; } static inline bool ftl_io_mode_logical(const struct ftl_io *io) { return !ftl_io_mode_physical(io); } static inline bool ftl_io_done(const struct ftl_io *io) { return io->req_cnt == 0 && io->pos == io->num_blocks; } struct ftl_io *ftl_io_alloc(struct spdk_io_channel *ch); struct ftl_io *ftl_io_alloc_child(struct ftl_io *parent); void ftl_io_fail(struct ftl_io *io, int status); void ftl_io_free(struct ftl_io *io); struct ftl_io *ftl_io_init_internal(const struct ftl_io_init_opts *opts); void ftl_io_reinit(struct ftl_io *io, ftl_io_fn cb, void *ctx, int flags, int type); void ftl_io_clear(struct ftl_io *io); void ftl_io_inc_req(struct ftl_io *io); void ftl_io_dec_req(struct ftl_io *io); struct iovec *ftl_io_iovec(struct ftl_io *io); uint64_t ftl_io_current_lba(const struct ftl_io *io); uint64_t ftl_io_get_lba(const struct ftl_io *io, size_t offset); void ftl_io_advance(struct ftl_io *io, size_t num_blocks); size_t ftl_iovec_num_blocks(struct iovec *iov, size_t iov_cnt); void *ftl_io_iovec_addr(struct ftl_io *io); size_t ftl_io_iovec_len_left(struct ftl_io *io); struct ftl_io *ftl_io_wbuf_init(struct spdk_ftl_dev *dev, struct ftl_addr addr, struct ftl_band *band, struct ftl_batch *batch, ftl_io_fn cb); struct ftl_io *ftl_io_erase_init(struct ftl_band *band, size_t num_blocks, ftl_io_fn cb); struct ftl_io *ftl_io_user_init(struct spdk_io_channel *ioch, uint64_t lba, size_t num_blocks, struct iovec *iov, size_t iov_cnt, spdk_ftl_fn cb_fn, void *cb_arg, int type); void *ftl_io_get_md(const struct ftl_io *io); void ftl_io_complete(struct ftl_io *io); void ftl_io_shrink_iovec(struct ftl_io *io, size_t num_blocks); void ftl_io_process_error(struct ftl_io *io, const struct spdk_nvme_cpl *status); void ftl_io_reset(struct ftl_io *io); void ftl_io_call_foreach_child(struct ftl_io *io, int (*callback)(struct ftl_io *)); #endif /* FTL_IO_H */