FTL: Add L2P API and flat L2P implementation

Signed-off-by: Kozlowski Mateusz <mateusz.kozlowski@intel.com>
Signed-off-by: Artur Paszkiewicz <artur.paszkiewicz@intel.com>
Change-Id: Ifadc8c6986164584235ee6a67799025fa7703b5d
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/13315
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Mellanox Build Bot
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
Artur Paszkiewicz 2022-07-07 10:53:08 -07:00 committed by Tomasz Zawadzki
parent b6eecb21e5
commit b16bdc6d49
15 changed files with 547 additions and 5 deletions

View File

@ -21,9 +21,9 @@ CFLAGS += -I.
FTL_SUBDIRS := mngt utils
C_SRCS = ftl_core.c ftl_init.c ftl_layout.c ftl_debug.c ftl_io.c ftl_sb.c
C_SRCS = ftl_core.c ftl_init.c ftl_layout.c ftl_debug.c ftl_io.c ftl_sb.c ftl_l2p.c ftl_l2p_flat.c
C_SRCS += mngt/ftl_mngt.c mngt/ftl_mngt_bdev.c mngt/ftl_mngt_shutdown.c mngt/ftl_mngt_startup.c
C_SRCS += mngt/ftl_mngt_md.c mngt/ftl_mngt_misc.c mngt/ftl_mngt_ioch.c
C_SRCS += mngt/ftl_mngt_md.c mngt/ftl_mngt_misc.c mngt/ftl_mngt_ioch.c mngt/ftl_mngt_l2p.c
C_SRCS += utils/ftl_conf.c utils/ftl_md.c utils/ftl_mempool.c
SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_ftl.map)

View File

@ -26,14 +26,27 @@ spdk_ftl_io_size(void)
return sizeof(struct ftl_io);
}
static int
static bool
ftl_shutdown_complete(struct spdk_ftl_dev *dev)
{
if (dev->num_inflight) {
return 0;
return false;
}
return 1;
if (!ftl_l2p_is_halted(dev)) {
ftl_l2p_halt(dev);
return false;
}
return true;
}
void
ftl_invalidate_addr(struct spdk_ftl_dev *dev, ftl_addr addr)
{
if (ftl_addr_in_nvc(dev, addr)) {
return;
}
}
void
@ -128,6 +141,7 @@ ftl_core_poller(void *ctx)
}
ftl_process_io_queue(dev);
ftl_l2p_process(dev);
if (io_activity_total_old != dev->io_activity_total) {
return SPDK_POLLER_BUSY;

View File

@ -20,6 +20,7 @@
#include "ftl_io.h"
#include "ftl_layout.h"
#include "ftl_sb.h"
#include "ftl_l2p.h"
#include "utils/ftl_log.h"
/* When using VSS on nvcache, FTL sometimes doesn't require the contents of metadata.
@ -81,6 +82,12 @@ struct spdk_ftl_dev {
/* Number of free bands */
uint64_t num_free;
/* Logical -> physical table */
void *l2p;
/* l2p deferred pins list */
TAILQ_HEAD(, ftl_l2p_pin_ctx) l2p_deferred_pins;
/* Size of the l2p table */
uint64_t num_lbas;
@ -117,6 +124,8 @@ struct spdk_ftl_dev {
TAILQ_HEAD(, ftl_io) wr_sq;
};
void ftl_invalidate_addr(struct spdk_ftl_dev *dev, ftl_addr addr);
int ftl_core_poller(void *ctx);
int ftl_io_channel_poll(void *arg);

View File

@ -181,12 +181,47 @@ ftl_io_init(struct spdk_io_channel *_ioch, struct ftl_io *io, uint64_t lba, size
return 0;
}
static void
ftl_io_complete_verify(struct ftl_io *io)
{
struct spdk_ftl_dev *dev = io->dev;
uint64_t i;
uint64_t lba = io->lba;
assert(io->num_blocks <= dev->xfer_size);
if (FTL_IO_WRITE == io->type) {
return;
}
if (spdk_unlikely(io->status)) {
return;
}
for (i = 0; i < io->num_blocks; i++, lba++) {
ftl_addr current_addr = ftl_l2p_get(dev, lba);
/* If user read request gets stuck for whatever reason, then it's possible the LBA
* has been relocated by GC or compaction and it may no longer be safe to return data
* from that address */
if (spdk_unlikely(current_addr != io->map[i])) {
io->status = -EAGAIN;
break;
}
}
}
void
ftl_io_complete(struct ftl_io *io)
{
io->flags &= ~FTL_IO_INITIALIZED;
io->done = true;
if (io->flags & FTL_IO_PINNED) {
ftl_io_complete_verify(io);
ftl_l2p_unpin(io->dev, io->lba, io->num_blocks);
}
ftl_io_cb(io, io->cb_ctx, io->status);
}

View File

@ -13,6 +13,7 @@
#include "spdk/util.h"
#include "ftl_internal.h"
#include "ftl_l2p.h"
#include "utils/ftl_md.h"
struct spdk_ftl_dev;
@ -24,6 +25,8 @@ typedef void (*ftl_io_fn)(struct ftl_io *, void *, int);
enum ftl_io_flags {
/* Indicates whether IO is already initialized */
FTL_IO_INITIALIZED = (1 << 0),
/* Indicated whether the user IO pinned the L2P pages containing LBAs */
FTL_IO_PINNED = (1 << 1),
};
enum ftl_io_type {
@ -111,6 +114,9 @@ struct ftl_io {
/* Reference to the chunk within NV cache */
struct ftl_nv_cache_chunk *nv_cache_chunk;
/* For l2p pinning */
struct ftl_l2p_pin_ctx l2p_pin_ctx;
/* Logical to physical mapping for this IO, number of entries equals to
* number of transfer blocks */
ftl_addr *map;
@ -141,6 +147,9 @@ struct ftl_rq_entry {
void *priv;
} owner;
/* For l2p pinning */
struct ftl_l2p_pin_ctx l2p_pin_ctx;
struct {
uint64_t offset_blocks;
uint64_t num_blocks;

148
lib/ftl/ftl_l2p.c Normal file
View File

@ -0,0 +1,148 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) Intel Corporation.
* All rights reserved.
*/
#include "ftl_l2p.h"
#include "ftl_l2p_flat.h"
#include "ftl_core.h"
/* TODO: Verify why function pointers had worse performance than compile time constants */
#define FTL_L2P_OP(name) ftl_l2p_flat_ ## name
int
ftl_l2p_init(struct spdk_ftl_dev *dev)
{
TAILQ_INIT(&dev->l2p_deferred_pins);
return FTL_L2P_OP(init)(dev);
}
void
ftl_l2p_deinit(struct spdk_ftl_dev *dev)
{
FTL_L2P_OP(deinit)(dev);
}
static inline void
ftl_l2p_pin_ctx_init(struct ftl_l2p_pin_ctx *pin_ctx, uint64_t lba, uint64_t count,
ftl_l2p_pin_cb cb, void *cb_ctx)
{
pin_ctx->lba = lba;
pin_ctx->count = count;
pin_ctx->cb = cb;
pin_ctx->cb_ctx = cb_ctx;
}
void
ftl_l2p_pin(struct spdk_ftl_dev *dev, uint64_t lba, uint64_t count, ftl_l2p_pin_cb cb, void *cb_ctx,
struct ftl_l2p_pin_ctx *pin_ctx)
{
ftl_l2p_pin_ctx_init(pin_ctx, lba, count, cb, cb_ctx);
FTL_L2P_OP(pin)(dev, pin_ctx);
}
void
ftl_l2p_unpin(struct spdk_ftl_dev *dev, uint64_t lba, uint64_t count)
{
FTL_L2P_OP(unpin)(dev, lba, count);
}
void
ftl_l2p_pin_skip(struct spdk_ftl_dev *dev, ftl_l2p_pin_cb cb, void *cb_ctx,
struct ftl_l2p_pin_ctx *pin_ctx)
{
ftl_l2p_pin_ctx_init(pin_ctx, FTL_LBA_INVALID, 0, cb, cb_ctx);
cb(dev, 0, pin_ctx);
}
void
ftl_l2p_set(struct spdk_ftl_dev *dev, uint64_t lba, ftl_addr addr)
{
FTL_L2P_OP(set)(dev, lba, addr);
}
ftl_addr
ftl_l2p_get(struct spdk_ftl_dev *dev, uint64_t lba)
{
return FTL_L2P_OP(get)(dev, lba);
}
void
ftl_l2p_clear(struct spdk_ftl_dev *dev, ftl_l2p_cb cb, void *cb_ctx)
{
FTL_L2P_OP(clear)(dev, cb, cb_ctx);
}
void
ftl_l2p_process(struct spdk_ftl_dev *dev)
{
struct ftl_l2p_pin_ctx *pin_ctx;
pin_ctx = TAILQ_FIRST(&dev->l2p_deferred_pins);
if (pin_ctx) {
TAILQ_REMOVE(&dev->l2p_deferred_pins, pin_ctx, link);
FTL_L2P_OP(pin)(dev, pin_ctx);
}
FTL_L2P_OP(process)(dev);
}
bool
ftl_l2p_is_halted(struct spdk_ftl_dev *dev)
{
if (!TAILQ_EMPTY(&dev->l2p_deferred_pins)) {
return false;
}
return FTL_L2P_OP(is_halted)(dev);
}
void
ftl_l2p_halt(struct spdk_ftl_dev *dev)
{
return FTL_L2P_OP(halt)(dev);
}
void
ftl_l2p_update_base(struct spdk_ftl_dev *dev, uint64_t lba, ftl_addr new_addr, ftl_addr old_addr)
{
ftl_addr current_addr;
/* Updating L2P for data in base device - used by compaction and GC, may be invalidated by user write.
* Split off from updating L2P in cache due to extra edge cases for handling dirty shutdown in the cache case.
* Also some assumptions are not the same (can't assign INVALID address for base device - trim cases are done on cache)
*/
assert(ftl_check_core_thread(dev));
assert(new_addr != FTL_ADDR_INVALID);
assert(old_addr != FTL_ADDR_INVALID);
assert(!ftl_addr_in_nvc(dev, new_addr));
current_addr = ftl_l2p_get(dev, lba);
if (current_addr == old_addr) {
/* DO NOT CHANGE ORDER - START (need to set L2P (and valid bits), before invalidating old ones,
* due to dirty shutdown from shm recovery - it's ok to have too many bits set, but not ok to
* have too many cleared) */
ftl_l2p_set(dev, lba, new_addr);
/* DO NOT CHANGE ORDER - END */
} else {
/* new addr could be set by running p2l checkpoint but in the time window between
* p2l checkpoint completion and l2p set operation new data could be written on
* open chunk so this address need to be invalidated */
ftl_invalidate_addr(dev, new_addr);
}
ftl_invalidate_addr(dev, old_addr);
}
void
ftl_l2p_pin_complete(struct spdk_ftl_dev *dev, int status, struct ftl_l2p_pin_ctx *pin_ctx)
{
if (spdk_unlikely(status == -EAGAIN)) {
TAILQ_INSERT_TAIL(&dev->l2p_deferred_pins, pin_ctx, link);
} else {
pin_ctx->cb(dev, status, pin_ctx);
}
}

56
lib/ftl/ftl_l2p.h Normal file
View File

@ -0,0 +1,56 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) Intel Corporation.
* All rights reserved.
*/
#ifndef FTL_L2P_H
#define FTL_L2P_H
#include "spdk/queue.h"
#include "ftl_internal.h"
struct spdk_ftl_dev;
struct ftl_nv_cache_chunk;
struct ftl_rq;
struct ftl_io;
struct ftl_l2p_pin_ctx;
typedef void (*ftl_l2p_cb)(struct spdk_ftl_dev *dev, int status, void *ctx);
typedef void (*ftl_l2p_pin_cb)(struct spdk_ftl_dev *dev, int status,
struct ftl_l2p_pin_ctx *pin_ctx);
int ftl_l2p_init(struct spdk_ftl_dev *dev);
void ftl_l2p_deinit(struct spdk_ftl_dev *dev);
struct ftl_l2p_pin_ctx {
uint64_t lba;
uint64_t count;
ftl_l2p_pin_cb cb;
void *cb_ctx;
TAILQ_ENTRY(ftl_l2p_pin_ctx) link;
};
void ftl_l2p_pin(struct spdk_ftl_dev *dev, uint64_t lba, uint64_t count, ftl_l2p_pin_cb cb,
void *cb_ctx, struct ftl_l2p_pin_ctx *pin_ctx);
void ftl_l2p_unpin(struct spdk_ftl_dev *dev, uint64_t lba, uint64_t count);
void ftl_l2p_pin_skip(struct spdk_ftl_dev *dev, ftl_l2p_pin_cb cb, void *cb_ctx,
struct ftl_l2p_pin_ctx *pin_ctx);
void ftl_l2p_set(struct spdk_ftl_dev *dev, uint64_t lba, ftl_addr addr);
ftl_addr ftl_l2p_get(struct spdk_ftl_dev *dev, uint64_t lba);
void ftl_l2p_clear(struct spdk_ftl_dev *dev, ftl_l2p_cb cb, void *cb_ctx);
void ftl_l2p_restore(struct spdk_ftl_dev *dev, ftl_l2p_cb cb, void *cb_ctx);
void ftl_l2p_process(struct spdk_ftl_dev *dev);
bool ftl_l2p_is_halted(struct spdk_ftl_dev *dev);
void ftl_l2p_halt(struct spdk_ftl_dev *dev);
void ftl_l2p_update_cache(struct spdk_ftl_dev *dev, uint64_t lba, ftl_addr new_addr,
ftl_addr old_addr);
void ftl_l2p_update_base(struct spdk_ftl_dev *dev, uint64_t lba, ftl_addr new_addr,
ftl_addr old_addr);
void ftl_l2p_pin_complete(struct spdk_ftl_dev *dev, int status, struct ftl_l2p_pin_ctx *pin_ctx);
#endif /* FTL_L2P_H */

167
lib/ftl/ftl_l2p_flat.c Normal file
View File

@ -0,0 +1,167 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) Intel Corporation.
* All rights reserved.
*/
#include "ftl_l2p.h"
#include "ftl_core.h"
#include "ftl_utils.h"
#include "ftl_l2p_flat.h"
#include "utils/ftl_addr_utils.h"
static struct ftl_md *
get_l2p_md(struct spdk_ftl_dev *dev)
{
return dev->layout.md[FTL_LAYOUT_REGION_TYPE_L2P];
}
struct ftl_l2p_flat {
void *l2p;
bool is_halted;
};
void
ftl_l2p_flat_pin(struct spdk_ftl_dev *dev, struct ftl_l2p_pin_ctx *pin_ctx)
{
assert(dev->num_lbas >= pin_ctx->lba + pin_ctx->count);
ftl_l2p_pin_complete(dev, 0, pin_ctx);
}
void
ftl_l2p_flat_unpin(struct spdk_ftl_dev *dev, uint64_t lba, uint64_t count)
{
assert(dev->num_lbas >= lba + count);
}
void
ftl_l2p_flat_set(struct spdk_ftl_dev *dev, uint64_t lba, ftl_addr addr)
{
struct ftl_l2p_flat *l2p_flat = dev->l2p;
assert(dev->num_lbas > lba);
ftl_addr_store(dev, l2p_flat->l2p, lba, addr);
}
ftl_addr
ftl_l2p_flat_get(struct spdk_ftl_dev *dev, uint64_t lba)
{
struct ftl_l2p_flat *l2p_flat = dev->l2p;
assert(dev->num_lbas > lba);
return ftl_addr_load(dev, l2p_flat->l2p, lba);
}
static void
md_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status)
{
ftl_l2p_cb cb = md->owner.private;
void *cb_ctx = md->owner.cb_ctx;
cb(dev, status, cb_ctx);
}
void
ftl_l2p_flat_clear(struct spdk_ftl_dev *dev, ftl_l2p_cb cb, void *cb_ctx)
{
struct ftl_l2p_flat *l2p_flat = dev->l2p;
struct ftl_md *md;
memset(l2p_flat->l2p, (int)FTL_ADDR_INVALID,
ftl_md_get_buffer_size(get_l2p_md(dev)));
md = get_l2p_md(dev);
md->cb = md_cb;
md->owner.cb_ctx = cb_ctx;
md->owner.private = cb;
ftl_md_persist(md);
}
static int
ftl_l2p_flat_init_dram(struct spdk_ftl_dev *dev, struct ftl_l2p_flat *l2p_flat,
size_t l2p_size)
{
struct ftl_md *md = get_l2p_md(dev);
assert(ftl_md_get_buffer_size(md) >= l2p_size);
l2p_flat->l2p = ftl_md_get_buffer(md);
if (!l2p_flat->l2p) {
FTL_ERRLOG(dev, "Failed to allocate l2p table\n");
return -1;
}
return 0;
}
int
ftl_l2p_flat_init(struct spdk_ftl_dev *dev)
{
size_t l2p_size = dev->num_lbas * dev->layout.l2p.addr_size;
struct ftl_l2p_flat *l2p_flat;
int ret;
if (dev->num_lbas == 0) {
FTL_ERRLOG(dev, "Invalid l2p table size\n");
return -1;
}
if (dev->l2p) {
FTL_ERRLOG(dev, "L2p table already allocated\n");
return -1;
}
l2p_flat = calloc(1, sizeof(*l2p_flat));
if (!l2p_flat) {
FTL_ERRLOG(dev, "Failed to allocate l2p_flat\n");
return -1;
}
ret = ftl_l2p_flat_init_dram(dev, l2p_flat, l2p_size);
if (ret) {
free(l2p_flat);
return ret;
}
dev->l2p = l2p_flat;
return 0;
}
void
ftl_l2p_flat_deinit(struct spdk_ftl_dev *dev)
{
struct ftl_l2p_flat *l2p_flat = dev->l2p;
if (!l2p_flat) {
return;
}
free(l2p_flat);
dev->l2p = NULL;
}
void
ftl_l2p_flat_unmap(struct spdk_ftl_dev *dev, ftl_l2p_cb cb, void *cb_ctx)
{
cb(dev, 0, cb_ctx);
}
void
ftl_l2p_flat_process(struct spdk_ftl_dev *dev)
{
}
bool
ftl_l2p_flat_is_halted(struct spdk_ftl_dev *dev)
{
return true;
}
void
ftl_l2p_flat_halt(struct spdk_ftl_dev *dev)
{
}

21
lib/ftl/ftl_l2p_flat.h Normal file
View File

@ -0,0 +1,21 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) Intel Corporation.
* All rights reserved.
*/
#ifndef FTL_L2P_FLAT_H
#define FTL_L2P_FLAT_H
int ftl_l2p_flat_init(struct spdk_ftl_dev *dev);
void ftl_l2p_flat_deinit(struct spdk_ftl_dev *dev);
void ftl_l2p_flat_pin(struct spdk_ftl_dev *dev, struct ftl_l2p_pin_ctx *pin_ctx);
void ftl_l2p_flat_unpin(struct spdk_ftl_dev *dev, uint64_t lba, uint64_t count);
ftl_addr ftl_l2p_flat_get(struct spdk_ftl_dev *dev, uint64_t lba);
void ftl_l2p_flat_set(struct spdk_ftl_dev *dev, uint64_t lba, ftl_addr addr);
void ftl_l2p_flat_unmap(struct spdk_ftl_dev *dev, ftl_l2p_cb cb, void *cb_ctx);
void ftl_l2p_flat_clear(struct spdk_ftl_dev *dev, ftl_l2p_cb cb, void *cb_ctx);
void ftl_l2p_flat_process(struct spdk_ftl_dev *dev);
bool ftl_l2p_flat_is_halted(struct spdk_ftl_dev *dev);
void ftl_l2p_flat_halt(struct spdk_ftl_dev *dev);
#endif /* FTL_L2P_FLAT_H */

View File

@ -139,6 +139,20 @@ setup_layout_nvc(struct spdk_ftl_dev *dev)
region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB];
offset += region->current.blocks;
/* Initialize L2P region */
if (offset >= layout->nvc.total_blocks) {
goto error;
}
region = &layout->region[FTL_LAYOUT_REGION_TYPE_L2P];
region->type = FTL_LAYOUT_REGION_TYPE_L2P;
region->name = "l2p";
region->current.version = 0;
region->prev.version = 0;
region->current.offset = offset;
region->current.blocks = blocks_region(layout->l2p.addr_size * dev->num_lbas);
set_region_bdev_nvc(region, dev);
offset += region->current.blocks;
if (offset >= layout->nvc.total_blocks) {
goto error;
}

View File

@ -20,6 +20,9 @@ enum ftl_layout_region_type {
FTL_LAYOUT_REGION_TYPE_SB,
/* Mirrored instance of the superblock on the base device */
FTL_LAYOUT_REGION_TYPE_SB_BASE,
/* If using cached L2P, this region stores the serialized instance of it */
FTL_LAYOUT_REGION_TYPE_L2P,
/* User data region on the nv cache device */
FTL_LAYOUT_REGION_TYPE_DATA_NVC,

View File

@ -0,0 +1,46 @@
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright (c) Intel Corporation.
* All rights reserved.
*/
#include "spdk/thread.h"
#include "ftl_core.h"
#include "ftl_mngt.h"
#include "ftl_mngt_steps.h"
#include "ftl_l2p.h"
static void
l2p_cb(struct spdk_ftl_dev *dev, int status, void *ctx)
{
struct ftl_mngt_process *mngt = ctx;
if (status) {
ftl_mngt_fail_step(mngt);
} else {
ftl_mngt_next_step(mngt);
}
}
void
ftl_mngt_init_l2p(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
if (ftl_l2p_init(dev)) {
ftl_mngt_fail_step(mngt);
} else {
ftl_mngt_next_step(mngt);
}
}
void
ftl_mngt_deinit_l2p(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
ftl_l2p_deinit(dev);
ftl_mngt_next_step(mngt);
}
void
ftl_mngt_clear_l2p(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt)
{
ftl_l2p_clear(dev, l2p_cb, mngt);
}

View File

@ -31,6 +31,10 @@ static const struct ftl_mngt_process_desc desc_shutdown = {
.name = "Dump statistics",
.action = ftl_mngt_dump_stats
},
{
.name = "Deinitialize L2P",
.action = ftl_mngt_deinit_l2p
},
{
.name = "Rollback FTL device",
.action = ftl_mngt_rollback_device

View File

@ -89,6 +89,16 @@ static const struct ftl_mngt_process_desc desc_startup = {
static const struct ftl_mngt_process_desc desc_first_start = {
.name = "FTL first start",
.steps = {
{
.name = "Initialize L2P",
.action = ftl_mngt_init_l2p,
.cleanup = ftl_mngt_deinit_l2p
},
{
.name = "Clear L2P",
.action = ftl_mngt_clear_l2p,
.cleanup = ftl_mngt_clear_l2p
},
{
.name = "Scrub NV cache",
.action = ftl_mngt_scrub_nv_cache,

View File

@ -36,6 +36,12 @@ void ftl_mngt_init_io_channel(struct spdk_ftl_dev *dev, struct ftl_mngt_process
void ftl_mngt_deinit_io_channel(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
void ftl_mngt_init_l2p(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
void ftl_mngt_deinit_l2p(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
void ftl_mngt_clear_l2p(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
void ftl_mngt_scrub_nv_cache(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);
void ftl_mngt_finalize_startup(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt);