2017-12-13 00:49:05

by David Daney

[permalink] [raw]
Subject: [PATCH v7 0/4] Prerequisites for Cavium OCTEON-III network driver.

We want to add the Cavium OCTEON-III network driver. But since
interacting with the input and output queues is done via special CPU
local memory, we also need to add support to the MIPS/Octeon
architecture code. Aren't SoCs nice in this way? These are the
prerequisite patches that are needed before the network driver can be
merged.

Changes in v7:

o Splitting of the patch set only. These 4 mips patches are unchanged
from the previous posting.

Changes in v6:

o Added back cleanup patch for previous generation SoC "staging"
driver, as Greg K-H acked it.

o Moved FPA driver to drivers/net/ethernet/cavium/octeon as it is
currently only used by the octeon3-ethernet driver.

o Many code formatting fixes as noted by davem.

Changes in v5:

o Removed cleanup patch for previous generation SoC "staging" driver,
as it will be sent as a follow-on.

o Fixed kernel doc formatting in all patches.

o Removed redundant licensing text boilerplate.

o Reviewed-by: header added to 2/7.

o Rewrote locking code in 3/7 to eliminate inline asm.

Changes in v4:

o Use phy_print_status() instead of open coding the equivalent.

o Print warning on phy mode mismatch.

o Improve dt-bindings and add Acked-by.

Changes in v3:

o Fix PKI (RX path) initialization to work with little endian kernel.

Changes in v2:

o Cleanup and use of standard bindings in the device tree bindings
document.

o Added (hopefully) clarifying comments about several OCTEON
architectural peculiarities.

o Removed unused testing code from the driver.

o Removed some module parameters that already default to the proper
values.

o KConfig cleanup, including testing on x86_64, arm64 and mips.

o Fixed breakage to the driver for previous generation of OCTEON SoCs (in
the staging directory still).

o Verified bisectability of the patch set.

Carlos Munoz (2):
MIPS: Octeon: Enable LMTDMA/LMTST operations.
MIPS: Octeon: Add a global resource manager.

David Daney (2):
MIPS: Octeon: Automatically provision CVMSEG space.
staging: octeon: Remove USE_ASYNC_IOBDMA macro.

arch/mips/cavium-octeon/Kconfig | 27 +-
arch/mips/cavium-octeon/Makefile | 1 +
arch/mips/cavium-octeon/resource-mgr.c | 351 +++++++++++++++++++++
arch/mips/cavium-octeon/setup.c | 22 +-
.../asm/mach-cavium-octeon/kernel-entry-init.h | 20 +-
arch/mips/include/asm/mipsregs.h | 2 +
arch/mips/include/asm/octeon/octeon.h | 32 +-
arch/mips/include/asm/processor.h | 2 +-
arch/mips/kernel/octeon_switch.S | 2 -
arch/mips/mm/tlbex.c | 29 +-
drivers/staging/octeon/ethernet-defines.h | 6 -
drivers/staging/octeon/ethernet-rx.c | 25 +-
drivers/staging/octeon/ethernet-tx.c | 85 ++---
13 files changed, 472 insertions(+), 132 deletions(-)
create mode 100644 arch/mips/cavium-octeon/resource-mgr.c

--
2.14.3


2017-12-13 00:49:15

by David Daney

[permalink] [raw]
Subject: [PATCH v7 1/4] MIPS: Octeon: Enable LMTDMA/LMTST operations.

From: Carlos Munoz <[email protected]>

LMTDMA/LMTST operations move data between cores and I/O devices:

* LMTST operations can send an address and a variable length
(up to 128 bytes) of data to an I/O device.
* LMTDMA operations can send an address and a variable length
(up to 128) of data to the I/O device and then return a
variable length (up to 128 bytes) response from the I/O device.

For both LMTST and LMTDMA, the data sent to the device is first stored
in the CVMSEG core local memory cache line indexed by
CVMMEMCTL[LMTLINE], the data is then atomically transmitted to the
device with a store to the CVMSEG LMTDMA trigger location.

Reviewed-by: James Hogan <[email protected]>
Signed-off-by: Carlos Munoz <[email protected]>
Signed-off-by: Steven J. Hill <[email protected]>
Signed-off-by: David Daney <[email protected]>
---
arch/mips/cavium-octeon/setup.c | 6 ++++++
arch/mips/include/asm/octeon/octeon.h | 12 ++++++++++--
2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/arch/mips/cavium-octeon/setup.c b/arch/mips/cavium-octeon/setup.c
index a8034d0dcade..99e6a68bc652 100644
--- a/arch/mips/cavium-octeon/setup.c
+++ b/arch/mips/cavium-octeon/setup.c
@@ -609,6 +609,12 @@ void octeon_user_io_init(void)
#else
cvmmemctl.s.cvmsegenak = 0;
#endif
+ if (OCTEON_IS_OCTEON3()) {
+ /* Enable LMTDMA */
+ cvmmemctl.s.lmtena = 1;
+ /* Scratch line to use for LMT operation */
+ cvmmemctl.s.lmtline = 2;
+ }
/* R/W If set, CVMSEG is available for loads/stores in
* supervisor mode. */
cvmmemctl.s.cvmsegenas = 0;
diff --git a/arch/mips/include/asm/octeon/octeon.h b/arch/mips/include/asm/octeon/octeon.h
index c99c4b6a79f4..92a17d67c1fa 100644
--- a/arch/mips/include/asm/octeon/octeon.h
+++ b/arch/mips/include/asm/octeon/octeon.h
@@ -179,7 +179,15 @@ union octeon_cvmemctl {
/* RO 1 = BIST fail, 0 = BIST pass */
__BITFIELD_FIELD(uint64_t wbfbist:1,
/* Reserved */
- __BITFIELD_FIELD(uint64_t reserved:17,
+ __BITFIELD_FIELD(uint64_t reserved_52_57:6,
+ /* When set, LMTDMA/LMTST operations are permitted */
+ __BITFIELD_FIELD(uint64_t lmtena:1,
+ /* Selects the CVMSEG LM cacheline used by LMTDMA
+ * LMTST and wide atomic store operations.
+ */
+ __BITFIELD_FIELD(uint64_t lmtline:6,
+ /* Reserved */
+ __BITFIELD_FIELD(uint64_t reserved_41_44:4,
/* OCTEON II - TLB replacement policy: 0 = bitmask LRU; 1 = NLU.
* This field selects between the TLB replacement policies:
* bitmask LRU or NLU. Bitmask LRU maintains a mask of
@@ -275,7 +283,7 @@ union octeon_cvmemctl {
/* R/W Size of local memory in cache blocks, 54 (6912
* bytes) is max legal value. */
__BITFIELD_FIELD(uint64_t lmemsz:6,
- ;)))))))))))))))))))))))))))))))))
+ ;))))))))))))))))))))))))))))))))))))
} s;
};

--
2.14.3

2017-12-13 00:49:25

by David Daney

[permalink] [raw]
Subject: [PATCH v7 3/4] staging: octeon: Remove USE_ASYNC_IOBDMA macro.

Previous patch sets USE_ASYNC_IOBDMA to 1 unconditionally. Remove
USE_ASYNC_IOBDMA from all if statements. Remove dead code caused by
the change.

Acked-by: Greg Kroah-Hartman <[email protected]>
Signed-off-by: David Daney <[email protected]>
---
drivers/staging/octeon/ethernet-defines.h | 6 ---
drivers/staging/octeon/ethernet-rx.c | 25 ++++-----
drivers/staging/octeon/ethernet-tx.c | 85 ++++++++++---------------------
3 files changed, 37 insertions(+), 79 deletions(-)

diff --git a/drivers/staging/octeon/ethernet-defines.h b/drivers/staging/octeon/ethernet-defines.h
index e898df25b87f..21438c804a43 100644
--- a/drivers/staging/octeon/ethernet-defines.h
+++ b/drivers/staging/octeon/ethernet-defines.h
@@ -10,10 +10,6 @@

/*
* A few defines are used to control the operation of this driver:
- * USE_ASYNC_IOBDMA
- * Use asynchronous IO access to hardware. This uses Octeon's asynchronous
- * IOBDMAs to issue IO accesses without stalling. Set this to zero
- * to disable this. Note that IOBDMAs require CVMSEG.
* REUSE_SKBUFFS_WITHOUT_FREE
* Allows the TX path to free an skbuff into the FPA hardware pool. This
* can significantly improve performance for forwarding and bridging, but
@@ -32,8 +28,6 @@
#define REUSE_SKBUFFS_WITHOUT_FREE 1
#endif

-#define USE_ASYNC_IOBDMA 1
-
/* Maximum number of SKBs to try to free per xmit packet. */
#define MAX_OUT_QUEUE_DEPTH 1000

diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c
index 1a44291318ee..dd76c99d5ae0 100644
--- a/drivers/staging/octeon/ethernet-rx.c
+++ b/drivers/staging/octeon/ethernet-rx.c
@@ -201,11 +201,9 @@ static int cvm_oct_poll(struct oct_rx_group *rx_group, int budget)
/* Prefetch cvm_oct_device since we know we need it soon */
prefetch(cvm_oct_device);

- if (USE_ASYNC_IOBDMA) {
- /* Save scratch in case userspace is using it */
- CVMX_SYNCIOBDMA;
- old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
- }
+ /* Save scratch in case userspace is using it */
+ CVMX_SYNCIOBDMA;
+ old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH);

/* Only allow work for our group (and preserve priorities) */
if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
@@ -220,10 +218,8 @@ static int cvm_oct_poll(struct oct_rx_group *rx_group, int budget)
BIT(rx_group->group));
}

- if (USE_ASYNC_IOBDMA) {
- cvmx_pow_work_request_async(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT);
- did_work_request = 1;
- }
+ cvmx_pow_work_request_async(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT);
+ did_work_request = 1;

while (rx_count < budget) {
struct sk_buff *skb = NULL;
@@ -232,7 +228,7 @@ static int cvm_oct_poll(struct oct_rx_group *rx_group, int budget)
cvmx_wqe_t *work;
int port;

- if (USE_ASYNC_IOBDMA && did_work_request)
+ if (did_work_request)
work = cvmx_pow_work_response_async(CVMX_SCR_SCRATCH);
else
work = cvmx_pow_work_request_sync(CVMX_POW_NO_WAIT);
@@ -260,7 +256,7 @@ static int cvm_oct_poll(struct oct_rx_group *rx_group, int budget)
sizeof(void *));
prefetch(pskb);

- if (USE_ASYNC_IOBDMA && rx_count < (budget - 1)) {
+ if (rx_count < (budget - 1)) {
cvmx_pow_work_request_async_nocheck(CVMX_SCR_SCRATCH,
CVMX_POW_NO_WAIT);
did_work_request = 1;
@@ -403,10 +399,9 @@ static int cvm_oct_poll(struct oct_rx_group *rx_group, int budget)
cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid), old_group_mask);
}

- if (USE_ASYNC_IOBDMA) {
- /* Restore the scratch area */
- cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
- }
+ /* Restore the scratch area */
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
+
cvm_oct_rx_refill_pool(0);

return rx_count;
diff --git a/drivers/staging/octeon/ethernet-tx.c b/drivers/staging/octeon/ethernet-tx.c
index 31f35025d19e..2eede0907924 100644
--- a/drivers/staging/octeon/ethernet-tx.c
+++ b/drivers/staging/octeon/ethernet-tx.c
@@ -179,23 +179,18 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
qos = 0;
}

- if (USE_ASYNC_IOBDMA) {
- /* Save scratch in case userspace is using it */
- CVMX_SYNCIOBDMA;
- old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
- old_scratch2 = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8);
-
- /*
- * Fetch and increment the number of packets to be
- * freed.
- */
- cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH + 8,
- FAU_NUM_PACKET_BUFFERS_TO_FREE,
- 0);
- cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH,
- priv->fau + qos * 4,
- MAX_SKB_TO_FREE);
- }
+ /* Save scratch in case userspace is using it */
+ CVMX_SYNCIOBDMA;
+ old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ old_scratch2 = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8);
+
+ /* Fetch and increment the number of packets to be freed. */
+ cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH + 8,
+ FAU_NUM_PACKET_BUFFERS_TO_FREE,
+ 0);
+ cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH,
+ priv->fau + qos * 4,
+ MAX_SKB_TO_FREE);

/*
* We have space for 6 segment pointers, If there will be more
@@ -204,22 +199,11 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
if (unlikely(skb_shinfo(skb)->nr_frags > 5)) {
if (unlikely(__skb_linearize(skb))) {
queue_type = QUEUE_DROP;
- if (USE_ASYNC_IOBDMA) {
- /*
- * Get the number of skbuffs in use
- * by the hardware
- */
- CVMX_SYNCIOBDMA;
- skb_to_free =
- cvmx_scratch_read64(CVMX_SCR_SCRATCH);
- } else {
- /*
- * Get the number of skbuffs in use
- * by the hardware
- */
- skb_to_free = cvmx_fau_fetch_and_add32(
- priv->fau + qos * 4, MAX_SKB_TO_FREE);
- }
+ /* Get the number of skbuffs in use by the
+ * hardware
+ */
+ CVMX_SYNCIOBDMA;
+ skb_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free,
priv->fau +
qos * 4);
@@ -387,18 +371,10 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
pko_command.s.ipoffp1 = skb_network_offset(skb) + 1;
}

- if (USE_ASYNC_IOBDMA) {
- /* Get the number of skbuffs in use by the hardware */
- CVMX_SYNCIOBDMA;
- skb_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
- buffers_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8);
- } else {
- /* Get the number of skbuffs in use by the hardware */
- skb_to_free = cvmx_fau_fetch_and_add32(priv->fau + qos * 4,
- MAX_SKB_TO_FREE);
- buffers_to_free =
- cvmx_fau_fetch_and_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
- }
+ /* Get the number of skbuffs in use by the hardware */
+ CVMX_SYNCIOBDMA;
+ skb_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ buffers_to_free = cvmx_scratch_read64(CVMX_SCR_SCRATCH + 8);

skb_to_free = cvm_oct_adjust_skb_to_free(skb_to_free,
priv->fau + qos * 4);
@@ -416,9 +392,7 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
} else {
queue_type = QUEUE_HW;
}
- if (USE_ASYNC_IOBDMA)
- cvmx_fau_async_fetch_and_add32(
- CVMX_SCR_SCRATCH, FAU_TOTAL_TX_TO_CLEAN, 1);
+ cvmx_fau_async_fetch_and_add32(CVMX_SCR_SCRATCH, FAU_TOTAL_TX_TO_CLEAN, 1);

spin_lock_irqsave(&priv->tx_free_list[qos].lock, flags);

@@ -488,16 +462,11 @@ int cvm_oct_xmit(struct sk_buff *skb, struct net_device *dev)
dev_kfree_skb_any(t);
}

- if (USE_ASYNC_IOBDMA) {
- CVMX_SYNCIOBDMA;
- total_to_clean = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
- /* Restore the scratch area */
- cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
- cvmx_scratch_write64(CVMX_SCR_SCRATCH + 8, old_scratch2);
- } else {
- total_to_clean = cvmx_fau_fetch_and_add32(
- FAU_TOTAL_TX_TO_CLEAN, 1);
- }
+ CVMX_SYNCIOBDMA;
+ total_to_clean = cvmx_scratch_read64(CVMX_SCR_SCRATCH);
+ /* Restore the scratch area */
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
+ cvmx_scratch_write64(CVMX_SCR_SCRATCH + 8, old_scratch2);

if (total_to_clean & 0x3ff) {
/*
--
2.14.3

2017-12-13 00:49:48

by David Daney

[permalink] [raw]
Subject: [PATCH v7 4/4] MIPS: Octeon: Add a global resource manager.

From: Carlos Munoz <[email protected]>

Add a global resource manager to manage tagged pointers within
bootmem allocated memory. This is used by various functional
blocks in the Octeon core like the FPA, Ethernet nexus, etc.

Signed-off-by: Carlos Munoz <[email protected]>
Signed-off-by: Steven J. Hill <[email protected]>
Signed-off-by: David Daney <[email protected]>
---
arch/mips/cavium-octeon/Makefile | 1 +
arch/mips/cavium-octeon/resource-mgr.c | 351 +++++++++++++++++++++++++++++++++
arch/mips/include/asm/octeon/octeon.h | 18 ++
3 files changed, 370 insertions(+)
create mode 100644 arch/mips/cavium-octeon/resource-mgr.c

diff --git a/arch/mips/cavium-octeon/Makefile b/arch/mips/cavium-octeon/Makefile
index 7c02e542959a..28c0bb75d1a4 100644
--- a/arch/mips/cavium-octeon/Makefile
+++ b/arch/mips/cavium-octeon/Makefile
@@ -10,6 +10,7 @@
#

obj-y := cpu.o setup.o octeon-platform.o octeon-irq.o csrc-octeon.o
+obj-y += resource-mgr.o
obj-y += dma-octeon.o
obj-y += octeon-memcpy.o
obj-y += executive/
diff --git a/arch/mips/cavium-octeon/resource-mgr.c b/arch/mips/cavium-octeon/resource-mgr.c
new file mode 100644
index 000000000000..74efda5420ff
--- /dev/null
+++ b/arch/mips/cavium-octeon/resource-mgr.c
@@ -0,0 +1,351 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Resource manager for Octeon.
+ *
+ * Copyright (C) 2017 Cavium, Inc.
+ */
+#include <linux/module.h>
+
+#include <asm/octeon/octeon.h>
+#include <asm/octeon/cvmx-bootmem.h>
+
+#define RESOURCE_MGR_BLOCK_NAME "cvmx-global-resources"
+#define MAX_RESOURCES 128
+#define INST_AVAILABLE -88
+#define OWNER 0xbadc0de
+
+struct global_resource_entry {
+ struct global_resource_tag tag;
+ u64 phys_addr;
+ u64 size;
+};
+
+struct global_resources {
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ u32 rlock;
+ u32 pad;
+#else
+ u32 pad;
+ u32 rlock;
+#endif
+ u64 entry_cnt;
+ struct global_resource_entry resource_entry[];
+};
+
+static struct global_resources *res_mgr_info;
+
+
+/*
+ * The resource manager interacts with software running outside of the
+ * Linux kernel, which necessitates locking to maintain data structure
+ * consistency. These custom locking functions implement the locking
+ * protocol, and cannot be replaced by kernel locking functions that
+ * may use different in-memory structures.
+ */
+
+static void res_mgr_lock(void)
+{
+ while (cmpxchg(&res_mgr_info->rlock, 0, 1))
+ ; /* Loop while not zero */
+ rmb();
+}
+
+static void res_mgr_unlock(void)
+{
+ /* Wait until all resource operations finish before unlocking. */
+ wmb();
+ WRITE_ONCE(res_mgr_info->rlock, 0);
+ /* Force a write buffer flush. */
+ wmb();
+}
+
+static int res_mgr_find_resource(struct global_resource_tag tag)
+{
+ struct global_resource_entry *res_entry;
+ int i;
+
+ for (i = 0; i < res_mgr_info->entry_cnt; i++) {
+ res_entry = &res_mgr_info->resource_entry[i];
+ if (res_entry->tag.lo == tag.lo && res_entry->tag.hi == tag.hi)
+ return i;
+ }
+ return -1;
+}
+
+/**
+ * res_mgr_create_resource() - Create a resource.
+ * @tag: Identifies the resource.
+ * @inst_cnt: Number of resource instances to create.
+ *
+ * Returns 0 if the source was created successfully.
+ * Returns < 0 for error codes.
+ */
+int res_mgr_create_resource(struct global_resource_tag tag, int inst_cnt)
+{
+ struct global_resource_entry *res_entry;
+ u64 size;
+ u64 *res_addr;
+ int res_index, i, rc = 0;
+
+ res_mgr_lock();
+
+ /* Make sure resource doesn't already exist. */
+ res_index = res_mgr_find_resource(tag);
+ if (res_index >= 0) {
+ rc = -EEXIST;
+ goto err;
+ }
+
+ if (res_mgr_info->entry_cnt >= MAX_RESOURCES) {
+ pr_err("Resource max limit reached, not created\n");
+ rc = -ENOSPC;
+ goto err;
+ }
+
+ /*
+ * Each instance is kept in an array of u64s. The first array element
+ * holds the number of allocated instances.
+ */
+ size = sizeof(u64) * (inst_cnt + 1);
+ res_addr = cvmx_bootmem_alloc_range(size, CVMX_CACHE_LINE_SIZE, 0, 0);
+ if (!res_addr) {
+ pr_err("Failed to allocate resource. not created\n");
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ /* Initialize the newly created resource. */
+ *res_addr = inst_cnt;
+ for (i = 1; i <= inst_cnt; i++)
+ res_addr[i] = INST_AVAILABLE;
+
+ res_index = res_mgr_info->entry_cnt;
+ res_entry = &res_mgr_info->resource_entry[res_index];
+ res_entry->tag = tag;
+ res_entry->phys_addr = virt_to_phys(res_addr);
+ res_entry->size = size;
+ res_mgr_info->entry_cnt++;
+
+err:
+ res_mgr_unlock();
+
+ return rc;
+}
+EXPORT_SYMBOL(res_mgr_create_resource);
+
+/**
+ * res_mgr_alloc_range() - Allocate a range of resource instances.
+ * @tag: Identifies the resource.
+ * @req_inst: Requested start of instance range to allocate.
+ * Range instances are guaranteed to be sequential
+ * (-1 for don't care).
+ * @req_cnt: Number of instances to allocate.
+ * @use_last_avail: Set to request the last available instance.
+ * @inst: Updated with the allocated instances.
+ *
+ * Returns 0 if the source was created successfully.
+ * Returns < 0 for error codes.
+ */
+int res_mgr_alloc_range(struct global_resource_tag tag, int req_inst,
+ int req_cnt, bool use_last_avail, int *inst)
+{
+ struct global_resource_entry *res_entry;
+ int res_index;
+ u64 *res_addr;
+ u64 inst_cnt;
+ int alloc_cnt, i, rc = -ENOENT;
+
+ /* Start with no instances allocated. */
+ for (i = 0; i < req_cnt; i++)
+ inst[i] = INST_AVAILABLE;
+
+ res_mgr_lock();
+
+ /* Find the resource. */
+ res_index = res_mgr_find_resource(tag);
+ if (res_index < 0) {
+ pr_err("Resource not found, can't allocate instance\n");
+ goto err;
+ }
+
+ /* Get resource data. */
+ res_entry = &res_mgr_info->resource_entry[res_index];
+ res_addr = phys_to_virt(res_entry->phys_addr);
+ inst_cnt = *res_addr;
+
+ /* Allocate the requested instances. */
+ if (req_inst >= 0) {
+ /* Specific instance range requested. */
+ if (req_inst + req_cnt >= inst_cnt) {
+ pr_err("Requested instance out of range\n");
+ goto err;
+ }
+
+ for (i = 0; i < req_cnt; i++) {
+ if (*(res_addr + req_inst + 1 + i) == INST_AVAILABLE) {
+ inst[i] = req_inst + i;
+ } else {
+ inst[0] = INST_AVAILABLE;
+ break;
+ }
+ }
+ } else if (use_last_avail) {
+ /* Last available instance requested. */
+ alloc_cnt = 0;
+ for (i = inst_cnt; i > 0; i--) {
+ if (*(res_addr + i) == INST_AVAILABLE) {
+ /*
+ * Instance off by 1 (first element holds the
+ * count).
+ */
+ inst[alloc_cnt] = i - 1;
+
+ alloc_cnt++;
+ if (alloc_cnt == req_cnt)
+ break;
+ }
+ }
+
+ if (i == 0)
+ inst[0] = INST_AVAILABLE;
+ } else {
+ /* Next available instance requested. */
+ alloc_cnt = 0;
+ for (i = 1; i <= inst_cnt; i++) {
+ if (*(res_addr + i) == INST_AVAILABLE) {
+ /*
+ * Instance off by 1 (first element holds the
+ * count).
+ */
+ inst[alloc_cnt] = i - 1;
+
+ alloc_cnt++;
+ if (alloc_cnt == req_cnt)
+ break;
+ }
+ }
+
+ if (i > inst_cnt)
+ inst[0] = INST_AVAILABLE;
+ }
+
+ if (inst[0] != INST_AVAILABLE) {
+ for (i = 0; i < req_cnt; i++)
+ *(res_addr + inst[i] + 1) = OWNER;
+ rc = 0;
+ }
+
+err:
+ res_mgr_unlock();
+
+ return rc;
+}
+EXPORT_SYMBOL(res_mgr_alloc_range);
+
+/**
+ * res_mgr_alloc() - Allocate a resource instance.
+ * @tag: Identifies the resource.
+ * @req_inst: Requested instance to allocate (-1 for don't care).
+ * @use_last_avail: Set to request the last available instance.
+ *
+ * Returns: Allocated resource instance if successful.
+ * Returns <0 for error codes.
+ */
+int res_mgr_alloc(struct global_resource_tag tag, int req_inst,
+ bool use_last_avail)
+{
+ int inst, rc;
+
+ rc = res_mgr_alloc_range(tag, req_inst, 1, use_last_avail, &inst);
+ if (!rc)
+ return inst;
+ return rc;
+}
+EXPORT_SYMBOL(res_mgr_alloc);
+
+/**
+ * res_mgr_free_range() - Free a resource instance range.
+ * @tag: Identifies the resource.
+ * @inst: Requested instance to free.
+ * @req_cnt: Number of instances to free.
+ */
+void res_mgr_free_range(struct global_resource_tag tag, const int *inst,
+ int req_cnt)
+{
+ struct global_resource_entry *res_entry;
+ int res_index, i;
+ u64 *res_addr;
+
+ res_mgr_lock();
+
+ /* Find the resource. */
+ res_index = res_mgr_find_resource(tag);
+ if (res_index < 0) {
+ pr_err("Resource not found, can't free instance\n");
+ goto err;
+ }
+
+ /* Get the resource data. */
+ res_entry = &res_mgr_info->resource_entry[res_index];
+ res_addr = phys_to_virt(res_entry->phys_addr);
+
+ /* Free the resource instances. */
+ for (i = 0; i < req_cnt; i++) {
+ /* Instance off by 1 (first element holds the count). */
+ *(res_addr + inst[i] + 1) = INST_AVAILABLE;
+ }
+
+err:
+ res_mgr_unlock();
+}
+EXPORT_SYMBOL(res_mgr_free_range);
+
+/**
+ * res_mgr_free() - Free a resource instance.
+ * @tag: Identifies the resource.
+ * @req_inst: Requested instance to free.
+ */
+void res_mgr_free(struct global_resource_tag tag, int inst)
+{
+ res_mgr_free_range(tag, &inst, 1);
+}
+EXPORT_SYMBOL(res_mgr_free);
+
+static int __init res_mgr_init(void)
+{
+ struct cvmx_bootmem_named_block_desc *block;
+ int block_size;
+ u64 addr;
+
+ cvmx_bootmem_lock();
+
+ /* Search for the resource manager data in boot memory. */
+ block = cvmx_bootmem_phy_named_block_find(RESOURCE_MGR_BLOCK_NAME,
+ CVMX_BOOTMEM_FLAG_NO_LOCKING);
+ if (block) {
+ /* Found. */
+ res_mgr_info = phys_to_virt(block->base_addr);
+ } else {
+ /* Create it. */
+ block_size = sizeof(struct global_resources) +
+ sizeof(struct global_resource_entry) * MAX_RESOURCES;
+ addr = cvmx_bootmem_phy_named_block_alloc(block_size, 0, 0,
+ CVMX_CACHE_LINE_SIZE, RESOURCE_MGR_BLOCK_NAME,
+ CVMX_BOOTMEM_FLAG_NO_LOCKING);
+ if (!addr) {
+ pr_err("Failed to allocate name block %s\n",
+ RESOURCE_MGR_BLOCK_NAME);
+ } else {
+ res_mgr_info = phys_to_virt(addr);
+ memset(res_mgr_info, 0, block_size);
+ }
+ }
+
+ cvmx_bootmem_unlock();
+
+ return 0;
+}
+device_initcall(res_mgr_init);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cavium, Inc. Octeon resource manager");
diff --git a/arch/mips/include/asm/octeon/octeon.h b/arch/mips/include/asm/octeon/octeon.h
index f01af2469874..4dafeaf262b5 100644
--- a/arch/mips/include/asm/octeon/octeon.h
+++ b/arch/mips/include/asm/octeon/octeon.h
@@ -346,6 +346,24 @@ void octeon_mult_restore3_end(void);
void octeon_mult_restore2(void);
void octeon_mult_restore2_end(void);

+/*
+ * This definition must be kept in sync with the one in
+ * cvmx-global-resources.c
+ */
+struct global_resource_tag {
+ uint64_t lo;
+ uint64_t hi;
+};
+
+void res_mgr_free(struct global_resource_tag tag, int inst);
+void res_mgr_free_range(struct global_resource_tag tag, const int *inst,
+ int req_cnt);
+int res_mgr_alloc(struct global_resource_tag tag, int req_inst,
+ bool use_last_avail);
+int res_mgr_alloc_range(struct global_resource_tag tag, int req_inst,
+ int req_cnt, bool use_last_avail, int *inst);
+int res_mgr_create_resource(struct global_resource_tag tag, int inst_cnt);
+
/**
* Read a 32bit value from the Octeon NPI register space
*
--
2.14.3

2017-12-13 00:50:22

by David Daney

[permalink] [raw]
Subject: [PATCH v7 2/4] MIPS: Octeon: Automatically provision CVMSEG space.

Remove CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE and automatically calculate
the amount of CVMSEG space needed.

1st 128-bytes: Use by IOBDMA
2nd 128-bytes: Reserved by kernel for scratch/TLS emulation.
3rd 128-bytes: OCTEON-III LMTLINE

New config variable CONFIG_CAVIUM_OCTEON_EXTRA_CVMSEG provisions
additional lines, defaults to zero.

Signed-off-by: David Daney <[email protected]>
Signed-off-by: Carlos Munoz <[email protected]>
---
arch/mips/cavium-octeon/Kconfig | 27 ++++++++++++--------
arch/mips/cavium-octeon/setup.c | 16 ++++++------
.../asm/mach-cavium-octeon/kernel-entry-init.h | 20 +++++++++------
arch/mips/include/asm/mipsregs.h | 2 ++
arch/mips/include/asm/octeon/octeon.h | 2 ++
arch/mips/include/asm/processor.h | 2 +-
arch/mips/kernel/octeon_switch.S | 2 --
arch/mips/mm/tlbex.c | 29 ++++++----------------
drivers/staging/octeon/ethernet-defines.h | 2 +-
9 files changed, 50 insertions(+), 52 deletions(-)

diff --git a/arch/mips/cavium-octeon/Kconfig b/arch/mips/cavium-octeon/Kconfig
index 204a1670fd9b..a50d1aa5863b 100644
--- a/arch/mips/cavium-octeon/Kconfig
+++ b/arch/mips/cavium-octeon/Kconfig
@@ -11,21 +11,26 @@ config CAVIUM_CN63XXP1
non-CN63XXP1 hardware, so it is recommended to select "n"
unless it is known the workarounds are needed.

-config CAVIUM_OCTEON_CVMSEG_SIZE
- int "Number of L1 cache lines reserved for CVMSEG memory"
- range 0 54
- default 1
- help
- CVMSEG LM is a segment that accesses portions of the dcache as a
- local memory; the larger CVMSEG is, the smaller the cache is.
- This selects the size of CVMSEG LM, which is in cache blocks. The
- legally range is from zero to 54 cache blocks (i.e. CVMSEG LM is
- between zero and 6192 bytes).
-
endif # CPU_CAVIUM_OCTEON

if CAVIUM_OCTEON_SOC

+config CAVIUM_OCTEON_EXTRA_CVMSEG
+ int "Number of extra L1 cache lines reserved for CVMSEG memory"
+ range 0 50
+ default 0
+ help
+ CVMSEG LM is a segment that accesses portions of the dcache
+ as a local memory; the larger CVMSEG is, the smaller the
+ cache is. The kernel uses two or three blocks (one for TLB
+ exception handlers, one for driver IOBDMA operations, and on
+ models that need it, one for LMTDMA operations). This
+ selects an optional extra number of CVMSEG lines for use by
+ other software.
+
+ Normally no extra lines are required, and this parameter
+ should be set to zero.
+
config CAVIUM_OCTEON_LOCK_L2
bool "Lock often used kernel code in the L2"
default "y"
diff --git a/arch/mips/cavium-octeon/setup.c b/arch/mips/cavium-octeon/setup.c
index 99e6a68bc652..51c4d3c3cada 100644
--- a/arch/mips/cavium-octeon/setup.c
+++ b/arch/mips/cavium-octeon/setup.c
@@ -68,6 +68,12 @@ extern void pci_console_init(const char *arg);
static unsigned long long max_memory = ULLONG_MAX;
static unsigned long long reserve_low_mem;

+/*
+ * modified in hernel-entry-init.h, must have an initial value to keep
+ * it from being clobbered when bss is zeroed.
+ */
+u32 octeon_cvmseg_lines = 2;
+
DEFINE_SEMAPHORE(octeon_bootbus_sem);
EXPORT_SYMBOL(octeon_bootbus_sem);

@@ -604,11 +610,7 @@ void octeon_user_io_init(void)

/* R/W If set, CVMSEG is available for loads/stores in
* kernel/debug mode. */
-#if CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0
cvmmemctl.s.cvmsegenak = 1;
-#else
- cvmmemctl.s.cvmsegenak = 0;
-#endif
if (OCTEON_IS_OCTEON3()) {
/* Enable LMTDMA */
cvmmemctl.s.lmtena = 1;
@@ -626,9 +628,9 @@ void octeon_user_io_init(void)

/* Setup of CVMSEG is done in kernel-entry-init.h */
if (smp_processor_id() == 0)
- pr_notice("CVMSEG size: %d cache lines (%d bytes)\n",
- CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE,
- CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE * 128);
+ pr_notice("CVMSEG size: %u cache lines (%u bytes)\n",
+ octeon_cvmseg_lines,
+ octeon_cvmseg_lines * 128);

if (octeon_has_feature(OCTEON_FEATURE_FAU)) {
union cvmx_iob_fau_timeout fau_timeout;
diff --git a/arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h b/arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h
index c38b38ce5a3d..cdcca60978a2 100644
--- a/arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h
+++ b/arch/mips/include/asm/mach-cavium-octeon/kernel-entry-init.h
@@ -26,11 +26,18 @@
# a3 = address of boot descriptor block
.set push
.set arch=octeon
+ mfc0 v1, CP0_PRID_REG
+ andi v1, 0xff00
+ li v0, 0x9500 # cn78XX or later
+ subu v1, v1, v0
+ li t2, 2 + CONFIG_CAVIUM_OCTEON_EXTRA_CVMSEG
+ bltz v1, 1f
+ addiu t2, 1 # t2 has cvmseg_size
+1:
# Read the cavium mem control register
dmfc0 v0, CP0_CVMMEMCTL_REG
# Clear the lower 6 bits, the CVMSEG size
- dins v0, $0, 0, 6
- ori v0, CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE
+ dins v0, t2, 0, 6
dmtc0 v0, CP0_CVMMEMCTL_REG # Write the cavium mem control register
dmfc0 v0, CP0_CVMCTL_REG # Read the cavium control register
# Disable unaligned load/store support but leave HW fixup enabled
@@ -70,7 +77,7 @@
# Flush dcache after config change
cache 9, 0($0)
# Zero all of CVMSEG to make sure parity is correct
- dli v0, CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE
+ move v0, t2
dsll v0, 7
beqz v0, 2f
1: dsubu v0, 8
@@ -126,12 +133,7 @@
LONG_L sp, (t0)
# Set the SP global variable to zero so the master knows we've started
LONG_S zero, (t0)
-#ifdef __OCTEON__
- syncw
- syncw
-#else
sync
-#endif
# Jump to the normal Linux SMP entry point
j smp_bootstrap
nop
@@ -148,6 +150,8 @@

#endif /* CONFIG_SMP */
octeon_main_processor:
+ dla v0, octeon_cvmseg_lines
+ sw t2, 0(v0)
.set pop
.endm

diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h
index 6b1f1ad0542c..0b588640b65a 100644
--- a/arch/mips/include/asm/mipsregs.h
+++ b/arch/mips/include/asm/mipsregs.h
@@ -1126,6 +1126,8 @@
#define FPU_CSR_RD 0x3 /* towards -Infinity */


+#define CAVIUM_OCTEON_SCRATCH_OFFSET (2 * 128 - 16 - 32768)
+
#ifndef __ASSEMBLY__

/*
diff --git a/arch/mips/include/asm/octeon/octeon.h b/arch/mips/include/asm/octeon/octeon.h
index 92a17d67c1fa..f01af2469874 100644
--- a/arch/mips/include/asm/octeon/octeon.h
+++ b/arch/mips/include/asm/octeon/octeon.h
@@ -359,6 +359,8 @@ static inline uint32_t octeon_npi_read32(uint64_t address)

extern struct cvmx_bootinfo *octeon_bootinfo;

+extern u32 octeon_cvmseg_lines;
+
extern uint64_t octeon_bootloader_entry_addr;

extern void (*octeon_irq_setup_secondary)(void);
diff --git a/arch/mips/include/asm/processor.h b/arch/mips/include/asm/processor.h
index af34afbc32d9..1a20f9c5509f 100644
--- a/arch/mips/include/asm/processor.h
+++ b/arch/mips/include/asm/processor.h
@@ -216,7 +216,7 @@ struct octeon_cop2_state {
.cp2 = {0,},

struct octeon_cvmseg_state {
- unsigned long cvmseg[CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE]
+ unsigned long cvmseg[CONFIG_CAVIUM_OCTEON_EXTRA_CVMSEG + 3]
[cpu_dcache_line_size() / sizeof(unsigned long)];
};

diff --git a/arch/mips/kernel/octeon_switch.S b/arch/mips/kernel/octeon_switch.S
index e42113fe2762..4f56902d5ee7 100644
--- a/arch/mips/kernel/octeon_switch.S
+++ b/arch/mips/kernel/octeon_switch.S
@@ -29,7 +29,6 @@
cpu_save_nonscratch a0
LONG_S ra, THREAD_REG31(a0)

-#if CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0
/* Check if we need to store CVMSEG state */
dmfc0 t0, $11,7 /* CvmMemCtl */
bbit0 t0, 6, 3f /* Is user access enabled? */
@@ -58,7 +57,6 @@
dmfc0 t0, $11,7 /* CvmMemCtl */
xori t0, t0, 0x40 /* Bit 6 is CVMSEG user enable */
dmtc0 t0, $11,7 /* CvmMemCtl */
-#endif
3:

#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 79b9f2ad3ff5..3d3dfba465ae 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -115,33 +115,17 @@ static int use_lwx_insns(void)
return 0;
}
}
-#if defined(CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE) && \
- CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0
-static bool scratchpad_available(void)
-{
- return true;
-}
-static int scratchpad_offset(int i)
-{
- /*
- * CVMSEG starts at address -32768 and extends for
- * CAVIUM_OCTEON_CVMSEG_SIZE 128 byte cache lines.
- */
- i += 1; /* Kernel use starts at the top and works down. */
- return CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE * 128 - (8 * i) - 32768;
-}
-#else
-static bool scratchpad_available(void)
-{
- return false;
-}
+
static int scratchpad_offset(int i)
{
+ if (IS_ENABLED(CONFIG_CPU_CAVIUM_OCTEON))
+ return (CAVIUM_OCTEON_SCRATCH_OFFSET - (8 * i));
+
BUG();
/* Really unreachable, but evidently some GCC want this. */
return 0;
}
-#endif
+
/*
* Found by experiment: At least some revisions of the 4kc throw under
* some circumstances a machine check exception, triggered by invalid
@@ -1302,7 +1286,8 @@ static void build_r4000_tlb_refill_handler(void)
memset(relocs, 0, sizeof(relocs));
memset(final_handler, 0, sizeof(final_handler));

- if (IS_ENABLED(CONFIG_64BIT) && (scratch_reg >= 0 || scratchpad_available()) && use_bbit_insns()) {
+ if (IS_ENABLED(CONFIG_64BIT) && use_bbit_insns() &&
+ (scratch_reg >= 0 || IS_ENABLED(CONFIG_CPU_CAVIUM_OCTEON))) {
htlb_info = build_fast_tlb_refill_handler(&p, &l, &r, K0, K1,
scratch_reg);
vmalloc_mode = refill_scratch;
diff --git a/drivers/staging/octeon/ethernet-defines.h b/drivers/staging/octeon/ethernet-defines.h
index 07bd2b87f6a0..e898df25b87f 100644
--- a/drivers/staging/octeon/ethernet-defines.h
+++ b/drivers/staging/octeon/ethernet-defines.h
@@ -32,7 +32,7 @@
#define REUSE_SKBUFFS_WITHOUT_FREE 1
#endif

-#define USE_ASYNC_IOBDMA (CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0)
+#define USE_ASYNC_IOBDMA 1

/* Maximum number of SKBs to try to free per xmit packet. */
#define MAX_OUT_QUEUE_DEPTH 1000
--
2.14.3