+++ /dev/null
-Subject: [PATCH] ehea: Fix memory hotplug support
-Subject [PATCH] ehea: Add hugepage detection
-From: Hannes Hering <hannes.hering@linux.vnet.ibm.com>
-References: 436447 - LTC 48713
-References: 439599 - LTC 48958
-References: 477972 - LTC 51731
-
-This patch implements the memory notifier to update the busmap instantly
-instead of rebuilding the whole map. This is necessary because
-walk_memory_resource provides different information than required during memory
-hotplug.
-
-...
-
-All kernel memory which is used for kernel/hardware data transfer must be registered
-with firmware using "memory regions". 16GB hugepages may not be part of a memory region
-due to firmware restrictions.
-This patch modifies the walk_memory_resource callback fn to filter hugepages and add
-only standard memory to the busmap which is later on used for MR registration.
-
-...
-
-Added missing set_bit() to disable data transfer when a memchange notification is handled
-
-...
-
-Signed-off-by: Thomas Klein <tklein@de.ibm.com>
-Signed-off-by: Hannes Hering <hering2@de.ibm.com>
-Signed-off-by: Olaf Hering <olh@suse.de>
----
- drivers/net/ehea/ehea.h | 4
- drivers/net/ehea/ehea_main.c | 27 ++++--
- drivers/net/ehea/ehea_phyp.c | 2
- drivers/net/ehea/ehea_qmr.c | 175 ++++++++++++++++++++++++++++++++++++-------
- drivers/net/ehea/ehea_qmr.h | 5 +
- 5 files changed, 172 insertions(+), 41 deletions(-)
-
---- a/drivers/net/ehea/ehea.h
-+++ b/drivers/net/ehea/ehea.h
-@@ -40,13 +40,13 @@
- #include <asm/io.h>
-
- #define DRV_NAME "ehea"
--#define DRV_VERSION "EHEA_0092"
-+#define DRV_VERSION "EHEA_0094-02"
-
- /* eHEA capability flags */
- #define DLPAR_PORT_ADD_REM 1
- #define DLPAR_MEM_ADD 2
- #define DLPAR_MEM_REM 4
--#define EHEA_CAPABILITIES (DLPAR_PORT_ADD_REM | DLPAR_MEM_ADD)
-+#define EHEA_CAPABILITIES (DLPAR_PORT_ADD_REM | DLPAR_MEM_ADD | DLPAR_MEM_REM)
-
- #define EHEA_MSG_DEFAULT (NETIF_MSG_LINK | NETIF_MSG_TIMER \
- | NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR)
---- a/drivers/net/ehea/ehea_main.c
-+++ b/drivers/net/ehea/ehea_main.c
-@@ -2869,7 +2869,7 @@ static void ehea_rereg_mrs(struct work_s
- struct ehea_adapter *adapter;
-
- mutex_lock(&dlpar_mem_lock);
-- ehea_info("LPAR memory enlarged - re-initializing driver");
-+ ehea_info("LPAR memory changed - re-initializing driver");
-
- list_for_each_entry(adapter, &adapter_list, list)
- if (adapter->active_ports) {
-@@ -2906,13 +2906,6 @@ static void ehea_rereg_mrs(struct work_s
- }
- }
-
-- ehea_destroy_busmap();
-- ret = ehea_create_busmap();
-- if (ret) {
-- ehea_error("creating ehea busmap failed");
-- goto out;
-- }
--
- clear_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
-
- list_for_each_entry(adapter, &adapter_list, list)
-@@ -3525,9 +3518,23 @@ void ehea_crash_handler(void)
- static int ehea_mem_notifier(struct notifier_block *nb,
- unsigned long action, void *data)
- {
-+ struct memory_notify *arg = data;
- switch (action) {
-- case MEM_OFFLINE:
-- ehea_info("memory has been removed");
-+ case MEM_CANCEL_OFFLINE:
-+ ehea_info("memory offlining canceled");
-+ /* Readd canceled memory block */
-+ case MEM_ONLINE:
-+ ehea_info("memory is going online");
-+ set_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
-+ if (ehea_add_sect_bmap(arg->start_pfn, arg->nr_pages))
-+ return NOTIFY_BAD;
-+ ehea_rereg_mrs(NULL);
-+ break;
-+ case MEM_GOING_OFFLINE:
-+ ehea_info("memory is going offline");
-+ set_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
-+ if (ehea_rem_sect_bmap(arg->start_pfn, arg->nr_pages))
-+ return NOTIFY_BAD;
- ehea_rereg_mrs(NULL);
- break;
- default:
---- a/drivers/net/ehea/ehea_phyp.c
-+++ b/drivers/net/ehea/ehea_phyp.c
-@@ -535,7 +535,7 @@ u64 ehea_h_query_ehea(const u64 adapter_
- cb_logaddr, /* R5 */
- 0, 0, 0, 0, 0); /* R6-R10 */
- #ifdef DEBUG
-- ehea_dmp(cb_addr, sizeof(struct hcp_query_ehea), "hcp_query_ehea");
-+ ehea_dump(cb_addr, sizeof(struct hcp_query_ehea), "hcp_query_ehea");
- #endif
- return hret;
- }
---- a/drivers/net/ehea/ehea_qmr.c
-+++ b/drivers/net/ehea/ehea_qmr.c
-@@ -567,7 +567,7 @@ static inline int ehea_calc_index(unsign
- static inline int ehea_init_top_bmap(struct ehea_top_bmap *ehea_top_bmap,
- int dir)
- {
-- if(!ehea_top_bmap->dir[dir]) {
-+ if (!ehea_top_bmap->dir[dir]) {
- ehea_top_bmap->dir[dir] =
- kzalloc(sizeof(struct ehea_dir_bmap), GFP_KERNEL);
- if (!ehea_top_bmap->dir[dir])
-@@ -578,7 +578,7 @@ static inline int ehea_init_top_bmap(str
-
- static inline int ehea_init_bmap(struct ehea_bmap *ehea_bmap, int top, int dir)
- {
-- if(!ehea_bmap->top[top]) {
-+ if (!ehea_bmap->top[top]) {
- ehea_bmap->top[top] =
- kzalloc(sizeof(struct ehea_top_bmap), GFP_KERNEL);
- if (!ehea_bmap->top[top])
-@@ -587,52 +587,171 @@ static inline int ehea_init_bmap(struct
- return ehea_init_top_bmap(ehea_bmap->top[top], dir);
- }
-
--static int ehea_create_busmap_callback(unsigned long pfn,
-- unsigned long nr_pages, void *arg)
-+static DEFINE_MUTEX(ehea_busmap_mutex);
-+static unsigned long ehea_mr_len;
-+
-+#define EHEA_BUSMAP_ADD_SECT 1
-+#define EHEA_BUSMAP_REM_SECT 0
-+
-+static void ehea_rebuild_busmap(void)
-+{
-+ u64 vaddr = EHEA_BUSMAP_START;
-+ int top, dir, idx;
-+
-+ for (top = 0; top < EHEA_MAP_ENTRIES; top++) {
-+ struct ehea_top_bmap *ehea_top;
-+ int valid_dir_entries = 0;
-+
-+ if (!ehea_bmap->top[top])
-+ continue;
-+ ehea_top = ehea_bmap->top[top];
-+ for (dir = 0; dir < EHEA_MAP_ENTRIES; dir++) {
-+ struct ehea_dir_bmap *ehea_dir;
-+ int valid_entries = 0;
-+
-+ if (!ehea_top->dir[dir])
-+ continue;
-+ valid_dir_entries++;
-+ ehea_dir = ehea_top->dir[dir];
-+ for (idx = 0; idx < EHEA_MAP_ENTRIES; idx++) {
-+ if (!ehea_dir->ent[idx])
-+ continue;
-+ valid_entries++;
-+ ehea_dir->ent[idx] = vaddr;
-+ vaddr += EHEA_SECTSIZE;
-+ }
-+ if (!valid_entries) {
-+ ehea_top->dir[dir] = NULL;
-+ kfree(ehea_dir);
-+ }
-+ }
-+ if (!valid_dir_entries) {
-+ ehea_bmap->top[top] = NULL;
-+ kfree(ehea_top);
-+ }
-+ }
-+}
-+
-+static int ehea_update_busmap(unsigned long pfn, unsigned long nr_pages, int add)
- {
-- unsigned long i, mr_len, start_section, end_section;
-+ unsigned long i, start_section, end_section;
-+
-+ if (!nr_pages)
-+ return 0;
-+
-+ if (!ehea_bmap) {
-+ ehea_bmap = kzalloc(sizeof(struct ehea_bmap), GFP_KERNEL);
-+ if (!ehea_bmap)
-+ return -ENOMEM;
-+ }
-+
- start_section = (pfn * PAGE_SIZE) / EHEA_SECTSIZE;
- end_section = start_section + ((nr_pages * PAGE_SIZE) / EHEA_SECTSIZE);
-- mr_len = *(unsigned long *)arg;
-+ /* Mark entries as valid or invalid only; address is assigned later */
-+ for (i = start_section; i < end_section; i++) {
-+ u64 flag;
-+ int top = ehea_calc_index(i, EHEA_TOP_INDEX_SHIFT);
-+ int dir = ehea_calc_index(i, EHEA_DIR_INDEX_SHIFT);
-+ int idx = i & EHEA_INDEX_MASK;
-+
-+ if (add) {
-+ int ret = ehea_init_bmap(ehea_bmap, top, dir);
-+ if (ret)
-+ return ret;
-+ flag = 1; /* valid */
-+ ehea_mr_len += EHEA_SECTSIZE;
-+ } else {
-+ if (!ehea_bmap->top[top])
-+ continue;
-+ if (!ehea_bmap->top[top]->dir[dir])
-+ continue;
-+ flag = 0; /* invalid */
-+ ehea_mr_len -= EHEA_SECTSIZE;
-+ }
-
-- ehea_bmap = kzalloc(sizeof(struct ehea_bmap), GFP_KERNEL);
-- if (!ehea_bmap)
-- return -ENOMEM;
-+ ehea_bmap->top[top]->dir[dir]->ent[idx] = flag;
-+ }
-+ ehea_rebuild_busmap(); /* Assign contiguous addresses for mr */
-+ return 0;
-+}
-
-- for (i = start_section; i < end_section; i++) {
-- int ret;
-- int top, dir, idx;
-- u64 vaddr;
-+int ehea_add_sect_bmap(unsigned long pfn, unsigned long nr_pages)
-+{
-+ int ret;
-
-- top = ehea_calc_index(i, EHEA_TOP_INDEX_SHIFT);
-- dir = ehea_calc_index(i, EHEA_DIR_INDEX_SHIFT);
-+ mutex_lock(&ehea_busmap_mutex);
-+ ret = ehea_update_busmap(pfn, nr_pages, EHEA_BUSMAP_ADD_SECT);
-+ mutex_unlock(&ehea_busmap_mutex);
-+ return ret;
-+}
-
-- ret = ehea_init_bmap(ehea_bmap, top, dir);
-- if(ret)
-- return ret;
-+int ehea_rem_sect_bmap(unsigned long pfn, unsigned long nr_pages)
-+{
-+ int ret;
-
-- idx = i & EHEA_INDEX_MASK;
-- vaddr = EHEA_BUSMAP_START + mr_len + i * EHEA_SECTSIZE;
-+ mutex_lock(&ehea_busmap_mutex);
-+ ret = ehea_update_busmap(pfn, nr_pages, EHEA_BUSMAP_REM_SECT);
-+ mutex_unlock(&ehea_busmap_mutex);
-+ return ret;
-+}
-
-- ehea_bmap->top[top]->dir[dir]->ent[idx] = vaddr;
-- }
-+static int ehea_is_hugepage(unsigned long pfn)
-+{
-+ int page_order;
-
-- mr_len += nr_pages * PAGE_SIZE;
-- *(unsigned long *)arg = mr_len;
-+ if (pfn & EHEA_HUGEPAGE_PFN_MASK)
-+ return 0;
-
-- return 0;
-+ page_order = compound_order(pfn_to_page(pfn));
-+ if (page_order + PAGE_SHIFT != EHEA_HUGEPAGESHIFT)
-+ return 0;
-+
-+ return 1;
- }
-
--static unsigned long ehea_mr_len;
-+static int ehea_create_busmap_callback(unsigned long initial_pfn,
-+ unsigned long total_nr_pages, void *arg)
-+{
-+ int ret;
-+ unsigned long pfn, start_pfn, end_pfn, nr_pages;
-
--static DEFINE_MUTEX(ehea_busmap_mutex);
-+ if ((total_nr_pages * PAGE_SIZE) < EHEA_HUGEPAGE_SIZE)
-+ return ehea_update_busmap(initial_pfn, total_nr_pages,
-+ EHEA_BUSMAP_ADD_SECT);
-+
-+ /* Given chunk is >= 16GB -> check for hugepages */
-+ start_pfn = initial_pfn;
-+ end_pfn = initial_pfn + total_nr_pages;
-+ pfn = start_pfn;
-+
-+ while (pfn < end_pfn) {
-+ if (ehea_is_hugepage(pfn)) {
-+ /* Add mem found in front of the hugepage */
-+ nr_pages = pfn - start_pfn;
-+ ret = ehea_update_busmap(start_pfn, nr_pages,
-+ EHEA_BUSMAP_ADD_SECT);
-+ if (ret)
-+ return ret;
-+
-+ /* Skip the hugepage */
-+ pfn += (EHEA_HUGEPAGE_SIZE / PAGE_SIZE);
-+ start_pfn = pfn;
-+ } else
-+ pfn += (EHEA_SECTSIZE / PAGE_SIZE);
-+ }
-+
-+ /* Add mem found behind the hugepage(s) */
-+ nr_pages = pfn - start_pfn;
-+ return ehea_update_busmap(start_pfn, nr_pages, EHEA_BUSMAP_ADD_SECT);
-+}
-
- int ehea_create_busmap(void)
- {
- int ret;
-+
- mutex_lock(&ehea_busmap_mutex);
- ehea_mr_len = 0;
-- ret = walk_memory_resource(0, 1ULL << MAX_PHYSMEM_BITS, &ehea_mr_len,
-+ ret = walk_memory_resource(0, 1ULL << MAX_PHYSMEM_BITS, NULL,
- ehea_create_busmap_callback);
- mutex_unlock(&ehea_busmap_mutex);
- return ret;
---- a/drivers/net/ehea/ehea_qmr.h
-+++ b/drivers/net/ehea/ehea_qmr.h
-@@ -40,6 +40,9 @@
- #define EHEA_PAGESIZE (1UL << EHEA_PAGESHIFT)
- #define EHEA_SECTSIZE (1UL << 24)
- #define EHEA_PAGES_PER_SECTION (EHEA_SECTSIZE >> EHEA_PAGESHIFT)
-+#define EHEA_HUGEPAGESHIFT 34
-+#define EHEA_HUGEPAGE_SIZE (1UL << EHEA_HUGEPAGESHIFT)
-+#define EHEA_HUGEPAGE_PFN_MASK ((EHEA_HUGEPAGE_SIZE - 1) >> PAGE_SHIFT)
-
- #if ((1UL << SECTION_SIZE_BITS) < EHEA_SECTSIZE)
- #error eHEA module cannot work if kernel sectionsize < ehea sectionsize
-@@ -378,6 +381,8 @@ int ehea_rem_mr(struct ehea_mr *mr);
-
- void ehea_error_data(struct ehea_adapter *adapter, u64 res_handle);
-
-+int ehea_add_sect_bmap(unsigned long pfn, unsigned long nr_pages);
-+int ehea_rem_sect_bmap(unsigned long pfn, unsigned long nr_pages);
- int ehea_create_busmap(void);
- void ehea_destroy_busmap(void);
- u64 ehea_map_vaddr(void *caddr);