Commit 1790ee8a authored by Artur Paszkiewicz's avatar Artur Paszkiewicz Committed by Jim Harris
Browse files

ftl: I/O statistics



Add gathering of some performance counters and RPC for printing them.

Signed-off-by: default avatarArtur Paszkiewicz <artur.paszkiewicz@intel.com>
Signed-off-by: default avatarKozlowski Mateusz <mateusz.kozlowski@intel.com>
Change-Id: I2e77d37fb66459240ff2e241f2b1f77c60f4eef4
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/13390


Community-CI: Mellanox Build Bot
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
parent d748bc41
Loading
Loading
Loading
Loading
+174 −0
Original line number Diff line number Diff line
@@ -470,6 +470,7 @@ Example response:
    "bdev_ftl_create",
    "bdev_ftl_load",
    "bdev_ftl_unmap",
    "bdev_ftl_get_stats",
    "bdev_lvol_get_lvstores",
    "bdev_lvol_delete",
    "bdev_lvol_resize",
@@ -4954,6 +4955,179 @@ Example response:
  "result": true
}
~~~

### bdev_ftl_get_stats {#rpc_bdev_ftl_get_stats}

Get IO statistics for FTL bdev

This RPC is subject to change.

#### Parameters

Name                    | Optional | Type        | Description
----------------------- | -------- | ----------- | -----------
name                    | Required | string      | Bdev name

#### Response

The response is an object containing IO statistics for an FTL instance, split into multiple subobjects:

- `user` - contains information about number of IOs, and errors for any incoming requests,
- `cmp` - information about IO for the compaction process,
- `gc` - information about IO for the garbage collection process,
- `md_base` - internal metadata requests to the base FTL device,
- `md_nv_cache` - internal metadata requests to the cache device,
- `l2p` - requests done on the L2P cache region.

Each subobject contains the following information:

- `ios` - describes the total number of IOs requested,
- `blocks` - the total number of requested blocks,
- `errors` - describes the number of detected errors for a given operation, with the following distinctions:
  - `media` - media errors,
  - `crc` - mismatch in calculated CRC versus saved checksum in the metadata,
  - `other` - any other errors.

#### Example

Example request:

~~~json
{
  "params": {
    "name": "ftl0"
  },
  "jsonrpc": "2.0",
  "method": "bdev_ftl_get_stats",
  "id": 1
}
~~~

Example response:

~~~json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
      "name": "ftl0",
      "user": {
        "read": {
          "ios": 0,
          "blocks": 0,
          "errors": {
            "media": 0,
            "crc": 0,
            "other": 0
          }
        },
        "write": {
          "ios": 318707,
          "blocks": 318707,
          "errors": {
            "media": 0,
            "other": 0
          }
        }
      },
      "cmp": {
        "read": {
          "ios": 0,
          "blocks": 0,
          "errors": {
            "media": 0,
            "crc": 0,
            "other": 0
          }
        },
        "write": {
          "ios": 0,
          "blocks": 0,
          "errors": {
            "media": 0,
            "other": 0
          }
        }
      },
      "gc": {
        "read": {
          "ios": 0,
          "blocks": 0,
          "errors": {
            "media": 0,
            "crc": 0,
            "other": 0
          }
        },
        "write": {
          "ios": 0,
          "blocks": 0,
          "errors": {
            "media": 0,
            "other": 0
          }
        }
      },
      "md_base": {
        "read": {
          "ios": 0,
          "blocks": 0,
          "errors": {
            "media": 0,
            "crc": 0,
            "other": 0
          }
        },
        "write": {
          "ios": 1,
          "blocks": 32,
          "errors": {
            "media": 0,
            "other": 0
          }
        }
      },
      "md_nv_cache": {
        "read": {
          "ios": 0,
          "blocks": 0,
          "errors": {
            "media": 0,
            "crc": 0,
            "other": 0
          }
        },
        "write": {
          "ios": 1064,
          "blocks": 1073896,
          "errors": {
            "media": 0,
            "other": 0
          }
        }
      },
      "l2p": {
        "read": {
          "ios": 240659,
          "blocks": 240659,
          "errors": {
            "media": 0,
            "crc": 0,
            "other": 0
          }
        },
        "write": {
          "ios": 235745,
          "blocks": 235745,
          "errors": {
            "media": 0,
            "other": 0
          }
        }
      }
    }
}
~~~
### bdev_pmem_create_pool {#rpc_bdev_pmem_create_pool}

Create a @ref bdev_config_pmem blk pool file. It is equivalent of following `pmempool create` command:
+60 −1
Original line number Diff line number Diff line
@@ -27,6 +27,52 @@ enum {
	SPDK_FTL_LIMIT_MAX
};

struct ftl_stats_error {
	uint64_t media;
	uint64_t crc;
	uint64_t other;
};

struct ftl_stats_group {
	uint64_t ios;
	uint64_t blocks;
	struct ftl_stats_error errors;
};

struct ftl_stats_entry {
	struct ftl_stats_group read;
	struct ftl_stats_group write;
};

enum ftl_stats_type {
	FTL_STATS_TYPE_USER = 0,
	FTL_STATS_TYPE_CMP,
	FTL_STATS_TYPE_GC,
	FTL_STATS_TYPE_MD_BASE,
	FTL_STATS_TYPE_MD_NV_CACHE,
	FTL_STATS_TYPE_L2P,
	FTL_STATS_TYPE_MAX,
};

struct ftl_stats {
	/* Number of times write limits were triggered by FTL writers
	 * (gc and compaction) dependent on number of free bands. GC starts at
	 * SPDK_FTL_LIMIT_START level, while at SPDK_FTL_LIMIT_CRIT compaction stops
	 * and only GC is allowed to work.
	 */
	uint64_t		limits[SPDK_FTL_LIMIT_MAX];

	/* Total number of blocks with IO to the underlying devices
	 * 1. nv cache read/write
	 * 2. base bdev read/write
	 */
	uint64_t		io_activity_total;

	struct ftl_stats_entry	entries[FTL_STATS_TYPE_MAX];
};

typedef void (*spdk_ftl_stats_fn)(struct ftl_stats *stats, void *cb_arg);

struct spdk_ftl_conf {
	/* Device's name */
	char					*name;
@@ -234,6 +280,19 @@ size_t spdk_ftl_io_size(void);
 */
void spdk_ftl_dev_set_fast_shutdown(struct spdk_ftl_dev *dev, bool fast_shutdown);

/*
 * Returns current FTL I/O statistics.
 *
 * \param dev Device
 * \param stats Allocated ftl_stats
 * \param cb_fn Callback function to invoke when the call is completed
 * \param cb_arg Argument to pass to the callback function
 *
 * \return 0 if successfully submitted, negative errno otherwise.
 */
int spdk_ftl_get_stats(struct spdk_ftl_dev *dev, struct ftl_stats *stats, spdk_ftl_stats_fn cb_fn,
		       void *cb_arg);

#ifdef __cplusplus
}
#endif
+16 −4
Original line number Diff line number Diff line
@@ -15,6 +15,10 @@ static void
write_rq_end(struct spdk_bdev_io *bdev_io, bool success, void *arg)
{
	struct ftl_rq *rq = arg;
	struct spdk_ftl_dev *dev = rq->dev;

	ftl_stats_bdev_io_completed(dev, rq->owner.compaction ? FTL_STATS_TYPE_CMP : FTL_STATS_TYPE_GC,
				    bdev_io);

	rq->success = success;

@@ -61,7 +65,7 @@ ftl_band_rq_write(struct ftl_band *band, struct ftl_rq *rq)
	ftl_band_rq_bdev_write(rq);

	band->queue_depth++;
	dev->io_activity_total += rq->num_blocks;
	dev->stats.io_activity_total += rq->num_blocks;

	ftl_band_iter_advance(band, rq->num_blocks);
	if (ftl_band_filled(band, band->md->iter.offset)) {
@@ -79,6 +83,8 @@ read_rq_end(struct spdk_bdev_io *bdev_io, bool success, void *arg)
	struct ftl_band *band = entry->io.band;
	struct ftl_rq *rq = ftl_rq_from_entry(entry);

	ftl_stats_bdev_io_completed(band->dev, FTL_STATS_TYPE_GC, bdev_io);

	rq->success = success;
	if (spdk_unlikely(!success)) {
		ftl_band_rq_bdev_read(entry);
@@ -134,7 +140,7 @@ ftl_band_rq_read(struct ftl_band *band, struct ftl_rq *rq)

	ftl_band_rq_bdev_read(entry);

	dev->io_activity_total += rq->num_blocks;
	dev->stats.io_activity_total += rq->num_blocks;
	band->queue_depth++;
}

@@ -144,6 +150,8 @@ write_brq_end(struct spdk_bdev_io *bdev_io, bool success, void *arg)
	struct ftl_basic_rq *brq = arg;
	struct ftl_band *band = brq->io.band;

	ftl_stats_bdev_io_completed(band->dev, FTL_STATS_TYPE_MD_BASE, bdev_io);

	brq->success = success;

	assert(band->queue_depth > 0);
@@ -188,7 +196,7 @@ ftl_band_basic_rq_write(struct ftl_band *band, struct ftl_basic_rq *brq)

	ftl_band_brq_bdev_write(brq);

	dev->io_activity_total += brq->num_blocks;
	dev->stats.io_activity_total += brq->num_blocks;
	band->queue_depth++;
	ftl_band_iter_advance(band, brq->num_blocks);
	if (ftl_band_filled(band, band->md->iter.offset)) {
@@ -203,6 +211,8 @@ read_brq_end(struct spdk_bdev_io *bdev_io, bool success, void *arg)
	struct ftl_basic_rq *brq = arg;
	struct ftl_band *band = brq->io.band;

	ftl_stats_bdev_io_completed(band->dev, FTL_STATS_TYPE_MD_BASE, bdev_io);

	brq->success = success;

	assert(band->queue_depth > 0);
@@ -245,7 +255,7 @@ ftl_band_basic_rq_read(struct ftl_band *band, struct ftl_basic_rq *brq)
	ftl_band_brq_bdev_read(brq);

	brq->io.band->queue_depth++;
	dev->io_activity_total += brq->num_blocks;
	dev->stats.io_activity_total += brq->num_blocks;
}

static void
@@ -404,6 +414,8 @@ read_md_cb(struct ftl_basic_rq *brq)
	if (band->md->p2l_map_checksum && band->md->p2l_map_checksum != band_map_crc) {
		FTL_ERRLOG(dev, "GC error, inconsistent P2L map CRC\n");
		success = false;

		ftl_stats_crc_error(band->dev, FTL_STATS_TYPE_GC);
	}
	band->owner.ops_fn = NULL;
	band->owner.priv = NULL;
+98 −2
Original line number Diff line number Diff line
@@ -30,6 +30,9 @@ static void
ftl_io_cmpl_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
{
	struct ftl_io *io = cb_arg;
	struct spdk_ftl_dev *dev = io->dev;

	ftl_stats_bdev_io_completed(dev, FTL_STATS_TYPE_USER, bdev_io);

	if (spdk_unlikely(!success)) {
		io->status = -EIO;
@@ -111,6 +114,7 @@ void
ftl_apply_limits(struct spdk_ftl_dev *dev)
{
	size_t limit;
	struct ftl_stats *stats = &dev->stats;
	int i;

	/*  Clear existing limit */
@@ -120,6 +124,7 @@ ftl_apply_limits(struct spdk_ftl_dev *dev)
		limit = ftl_get_limit(dev, i);

		if (dev->num_free <= limit) {
			stats->limits[i]++;
			dev->limit = i;
			break;
		}
@@ -663,7 +668,7 @@ int
ftl_core_poller(void *ctx)
{
	struct spdk_ftl_dev *dev = ctx;
	uint64_t io_activity_total_old = dev->io_activity_total;
	uint64_t io_activity_total_old = dev->stats.io_activity_total;

	if (dev->halt && ftl_shutdown_complete(dev)) {
		spdk_poller_unregister(&dev->core_poller);
@@ -677,7 +682,7 @@ ftl_core_poller(void *ctx)
	ftl_nv_cache_process(dev);
	ftl_l2p_process(dev);

	if (io_activity_total_old != dev->io_activity_total) {
	if (io_activity_total_old != dev->stats.io_activity_total) {
		return SPDK_POLLER_BUSY;
	}

@@ -734,10 +739,101 @@ spdk_ftl_dev_set_fast_shutdown(struct spdk_ftl_dev *dev, bool fast_shutdown)
	dev->conf.fast_shutdown = fast_shutdown;
}

void
ftl_stats_bdev_io_completed(struct spdk_ftl_dev *dev, enum ftl_stats_type type,
			    struct spdk_bdev_io *bdev_io)
{
	struct ftl_stats_entry *stats_entry = &dev->stats.entries[type];
	struct ftl_stats_group *stats_group;
	uint32_t cdw0;
	int sct;
	int sc;

	switch (bdev_io->type) {
	case SPDK_BDEV_IO_TYPE_READ:
		stats_group = &stats_entry->read;
		break;
	case SPDK_BDEV_IO_TYPE_WRITE:
	case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
		stats_group = &stats_entry->write;
		break;
	default:
		return;
	}

	spdk_bdev_io_get_nvme_status(bdev_io, &cdw0, &sct, &sc);

	if (sct == SPDK_NVME_SCT_GENERIC && sc == SPDK_NVME_SC_SUCCESS) {
		stats_group->ios++;
		stats_group->blocks += bdev_io->u.bdev.num_blocks;
	} else if (sct == SPDK_NVME_SCT_MEDIA_ERROR) {
		stats_group->errors.media++;
	} else {
		stats_group->errors.other++;
	}
}

struct spdk_io_channel *
spdk_ftl_get_io_channel(struct spdk_ftl_dev *dev)
{
	return spdk_get_io_channel(dev);
}

void
ftl_stats_crc_error(struct spdk_ftl_dev *dev, enum ftl_stats_type type)
{

	struct ftl_stats_entry *stats_entry = &dev->stats.entries[type];
	struct ftl_stats_group *stats_group = &stats_entry->read;

	stats_group->errors.crc++;
}

struct ftl_get_stats_ctx {
	struct spdk_ftl_dev *dev;
	struct ftl_stats *stats;
	spdk_ftl_stats_fn cb_fn;
	void *cb_arg;
};

static void
_ftl_get_stats(void *_ctx)
{
	struct ftl_get_stats_ctx *stats_ctx = _ctx;

	*stats_ctx->stats = stats_ctx->dev->stats;

	stats_ctx->cb_fn(stats_ctx->stats, stats_ctx->cb_arg);
	free(stats_ctx);
}

int
spdk_ftl_get_stats(struct spdk_ftl_dev *dev, struct ftl_stats *stats, spdk_ftl_stats_fn cb_fn,
		   void *cb_arg)
{
	struct ftl_get_stats_ctx *stats_ctx;
	int rc;

	stats_ctx = calloc(1, sizeof(struct ftl_get_stats_ctx));
	if (!stats_ctx) {
		return -ENOMEM;
	}

	stats_ctx->dev = dev;
	stats_ctx->stats = stats;
	stats_ctx->cb_fn = cb_fn;
	stats_ctx->cb_arg = cb_arg;

	rc = spdk_thread_send_msg(dev->core_thread, _ftl_get_stats, stats_ctx);
	if (rc) {
		goto stats_allocated;
	}

	return 0;

stats_allocated:
	free(stats_ctx);
	return rc;
}

SPDK_LOG_REGISTER_COMPONENT(ftl_core)
+7 −5
Original line number Diff line number Diff line
@@ -86,11 +86,8 @@ struct spdk_ftl_dev {
	/* Band md memory pool */
	struct ftl_mempool		*band_md_pool;

	/* counters for poller busy, include
	   1. nv cache read/write
	   2. metadata read/write
	   3. base bdev read/write */
	uint64_t			io_activity_total;
	/* Statistics */
	struct ftl_stats		stats;

	/* Array of bands */
	struct ftl_band			*bands;
@@ -205,6 +202,11 @@ void ftl_set_unmap_map(struct spdk_ftl_dev *dev, uint64_t lba, uint64_t num_bloc

void ftl_recover_max_seq(struct spdk_ftl_dev *dev);

void ftl_stats_bdev_io_completed(struct spdk_ftl_dev *dev, enum ftl_stats_type type,
				 struct spdk_bdev_io *bdev_io);

void ftl_stats_crc_error(struct spdk_ftl_dev *dev, enum ftl_stats_type type);

int ftl_unmap(struct spdk_ftl_dev *dev, struct ftl_io *io, struct spdk_io_channel *ch,
	      uint64_t lba, size_t lba_cnt, spdk_ftl_fn cb_fn, void *cb_arg);

Loading