[Skiboot] [RFC PATCH 6/6] libflash: add blocklevel_raw_prepare()

Stewart Smith stewart at linux.ibm.com
Thu Feb 28 17:18:26 AEDT 2019


The logic of a prepare call is to move the window (or at least start the
process of moving the window) so that when a read/write/erase call is
made, we don't have to wait for a IPMI round trip.

For non-hiomap backends, one could use similar logic.

---
Not Signed-off-by yet as I'm totally not convinced
---
 core/flash.c           |  9 +++++++--
 libflash/blocklevel.c  | 22 +++++++++++++++++++++
 libflash/blocklevel.h  |  2 ++
 libflash/ipmi-hiomap.c | 44 ++++++++++++++++++++++++++++++++++++++++--
 4 files changed, 73 insertions(+), 4 deletions(-)

diff --git a/core/flash.c b/core/flash.c
index 04d99348515e..e267aaa0373d 100644
--- a/core/flash.c
+++ b/core/flash.c
@@ -1,4 +1,4 @@
-/* Copyright 2013-2018 IBM Corp.
+/* Copyright 2013-2019 IBM Corp.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -239,7 +239,7 @@ static void flash_poll(struct timer *t __unused, void *data, uint64_t now __unus
 {
 	struct flash *flash = data;
 	uint64_t offset, buf, len;
-	int rc;
+	int rc = 0;
 
 	offset = flash->async.pos;
 	buf = flash->async.buf;
@@ -254,6 +254,10 @@ static void flash_poll(struct timer *t __unused, void *data, uint64_t now __unus
 	len = MIN(flash->async.len, flash->block_size);
 	prlog(PR_TRACE, "Flash poll op %d len %llu\n", flash->async.op, len);
 
+	/* If we've done work to prepare, come back later */
+	if (blocklevel_raw_prepare(flash->bl, (flash->async.op!=FLASH_OP_READ), offset, len))
+		goto schedule;
+
 	switch (flash->async.op) {
 	case FLASH_OP_READ:
 		rc = blocklevel_raw_read(flash->bl, offset, (void *)buf, len);
@@ -274,6 +278,7 @@ static void flash_poll(struct timer *t __unused, void *data, uint64_t now __unus
 	flash->async.pos += len;
 	flash->async.buf += len;
 	flash->async.len -= len;
+schedule:
 	if (!rc && flash->async.len) {
 		/*
 		 * We want to get called pretty much straight away, just have
diff --git a/libflash/blocklevel.c b/libflash/blocklevel.c
index 5b57d3c6194f..9659c7fd68af 100644
--- a/libflash/blocklevel.c
+++ b/libflash/blocklevel.c
@@ -92,6 +92,28 @@ static int release(struct blocklevel_device *bl)
 	return rc;
 }
 
+int blocklevel_raw_prepare(struct blocklevel_device *bl, bool rw, uint64_t pos, uint64_t len)
+{
+	int rc = 0;
+
+	FL_DBG("%s: 0x%" PRIx64 "\t0x%" PRIx64 "\n", __func__, pos, len);
+	if (!bl) {
+		errno = EINVAL;
+		return FLASH_ERR_PARM_ERROR;
+	}
+
+	rc = reacquire(bl);
+	if (rc)
+		return rc;
+
+	if (bl->prepare)
+		rc = bl->prepare(bl, rw, pos, len);
+
+	release(bl);
+
+	return rc;
+}
+
 int blocklevel_raw_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len)
 {
 	int rc;
diff --git a/libflash/blocklevel.h b/libflash/blocklevel.h
index ba42c83d003d..d4ce68cb5752 100644
--- a/libflash/blocklevel.h
+++ b/libflash/blocklevel.h
@@ -42,6 +42,7 @@ struct blocklevel_device {
 	void *priv;
 	int (*reacquire)(struct blocklevel_device *bl);
 	int (*release)(struct blocklevel_device *bl);
+	int (*prepare)(struct blocklevel_device *bl, bool rw, uint64_t pos, uint64_t len);
 	int (*read)(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len);
 	int (*write)(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len);
 	int (*erase)(struct blocklevel_device *bl, uint64_t pos, uint64_t len);
@@ -57,6 +58,7 @@ struct blocklevel_device {
 
 	struct blocklevel_range ecc_prot;
 };
+int blocklevel_raw_prepare(struct blocklevel_device *bl, bool rw, uint64_t pos, uint64_t len);
 int blocklevel_raw_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len);
 int blocklevel_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len);
 int blocklevel_raw_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len);
diff --git a/libflash/ipmi-hiomap.c b/libflash/ipmi-hiomap.c
index 56492fa87067..8bd9d1762ce9 100644
--- a/libflash/ipmi-hiomap.c
+++ b/libflash/ipmi-hiomap.c
@@ -94,8 +94,6 @@ static int hiomap_window_valid(struct ipmi_hiomap *ctx, uint64_t pos,
 		return FLASH_ERR_AGAIN;
 	if (ctx->bmc_state & HIOMAP_E_WINDOW_RESET)
 		return FLASH_ERR_AGAIN;
-	if (ctx->window_state == closed_window)
-		return FLASH_ERR_PARM_ERROR;
 	if (pos < ctx->current.cur_pos)
 		return FLASH_ERR_PARM_ERROR;
 	if ((pos + len) > (ctx->current.cur_pos + ctx->current.size))
@@ -707,6 +705,47 @@ restore:
 	return rc;
 }
 
+static int ipmi_hiomap_prepare(struct blocklevel_device *bl, bool rw,
+			       uint64_t pos, uint64_t len)
+{
+	struct ipmi_hiomap *ctx;
+	enum lpc_window_state want_state;
+	uint64_t size;
+	uint8_t command = HIOMAP_C_CREATE_READ_WINDOW;
+	bool valid_state;
+	bool is_read;
+	int rc = 0;
+
+	/* LPC is only 32bit */
+	if (pos > UINT_MAX || len > UINT_MAX)
+		return FLASH_ERR_PARM_ERROR;
+
+	if (rw)
+		command = HIOMAP_C_CREATE_WRITE_WINDOW;
+
+	ctx = container_of(bl, struct ipmi_hiomap, bl);
+
+	is_read = (command == HIOMAP_C_CREATE_READ_WINDOW);
+	want_state = is_read ? read_window : write_window;
+
+	lock(&ctx->lock);
+	valid_state = want_state == ctx->window_state;
+	rc = hiomap_window_valid(ctx, pos, len);
+	if (valid_state && !rc) {
+		unlock(&ctx->lock);
+		return 0;
+	}
+	unlock(&ctx->lock);
+
+	rc = ipmi_hiomap_handle_events(ctx);
+	if (rc)
+		return rc;
+
+	rc = hiomap_window_move(ctx, command, pos, len, &size);
+
+	return (rc==0)? 1 : -(rc);
+}
+
 static int ipmi_hiomap_read(struct blocklevel_device *bl, uint64_t pos,
 			    void *buf, uint64_t len)
 {
@@ -900,6 +939,7 @@ int ipmi_hiomap_init(struct blocklevel_device **bl)
 
 	init_lock(&ctx->lock);
 
+	ctx->bl.prepare = &ipmi_hiomap_prepare;
 	ctx->bl.read = &ipmi_hiomap_read;
 	ctx->bl.write = &ipmi_hiomap_write;
 	ctx->bl.erase = &ipmi_hiomap_erase;
-- 
2.20.1



More information about the Skiboot mailing list