Commit d4f12daf authored by Hannes Hering's avatar Hannes Hering Committed by Jeff Garzik
Browse files

ehea: Fix memory hotplug support



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.

Signed-off-by: default avatarHannes Hering <hering2@de.ibm.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent 93fbaae1
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -40,7 +40,7 @@
#include <asm/io.h>
#include <asm/io.h>


#define DRV_NAME	"ehea"
#define DRV_NAME	"ehea"
#define DRV_VERSION	"EHEA_0093"
#define DRV_VERSION	"EHEA_0094"


/* eHEA capability flags */
/* eHEA capability flags */
#define DLPAR_PORT_ADD_REM 1
#define DLPAR_PORT_ADD_REM 1
+15 −10
Original line number Original line Diff line number Diff line
@@ -2863,7 +2863,7 @@ static void ehea_rereg_mrs(struct work_struct *work)
	struct ehea_adapter *adapter;
	struct ehea_adapter *adapter;


	mutex_lock(&dlpar_mem_lock);
	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)
	list_for_each_entry(adapter, &adapter_list, list)
		if (adapter->active_ports) {
		if (adapter->active_ports) {
@@ -2900,13 +2900,6 @@ static void ehea_rereg_mrs(struct work_struct *work)
			}
			}
		}
		}


	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);
	clear_bit(__EHEA_STOP_XFER, &ehea_driver_flags);


	list_for_each_entry(adapter, &adapter_list, list)
	list_for_each_entry(adapter, &adapter_list, list)
@@ -3519,9 +3512,21 @@ void ehea_crash_handler(void)
static int ehea_mem_notifier(struct notifier_block *nb,
static int ehea_mem_notifier(struct notifier_block *nb,
                             unsigned long action, void *data)
                             unsigned long action, void *data)
{
{
	struct memory_notify *arg = data;
	switch (action) {
	switch (action) {
	case MEM_OFFLINE:
	case MEM_CANCEL_OFFLINE:
		ehea_info("memory has been removed");
		ehea_info("memory offlining canceled");
		/* Readd canceled memory block */
	case MEM_ONLINE:
		ehea_info("memory is going online");
		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");
		if (ehea_rem_sect_bmap(arg->start_pfn, arg->nr_pages))
			return NOTIFY_BAD;
		ehea_rereg_mrs(NULL);
		ehea_rereg_mrs(NULL);
		break;
		break;
	default:
	default:
+101 −30
Original line number Original line Diff line number Diff line
@@ -587,53 +587,124 @@ static inline int ehea_init_bmap(struct ehea_bmap *ehea_bmap, int top, int dir)
	return ehea_init_top_bmap(ehea_bmap->top[top], dir);
	return ehea_init_top_bmap(ehea_bmap->top[top], dir);
}
}


static int ehea_create_busmap_callback(unsigned long pfn,
static DEFINE_MUTEX(ehea_busmap_mutex);
				       unsigned long nr_pages, void *arg)
static unsigned long ehea_mr_len;

#define EHEA_BUSMAP_ADD_SECT 1
#define EHEA_BUSMAP_REM_SECT 0

static void ehea_rebuild_busmap(void)
{
{
	unsigned long i, mr_len, start_section, end_section;
	u64 vaddr = EHEA_BUSMAP_START;
	start_section = (pfn * PAGE_SIZE) / EHEA_SECTSIZE;
	int top, dir, idx;
	end_section = start_section + ((nr_pages * PAGE_SIZE) / EHEA_SECTSIZE);
	mr_len = *(unsigned long *)arg;


	if (!ehea_bmap)
	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 pgnum, int add)
{
	unsigned long i, start_section, end_section;

	if (!ehea_bmap) {
		ehea_bmap = kzalloc(sizeof(struct ehea_bmap), GFP_KERNEL);
		ehea_bmap = kzalloc(sizeof(struct ehea_bmap), GFP_KERNEL);
		if (!ehea_bmap)
		if (!ehea_bmap)
			return -ENOMEM;
			return -ENOMEM;
	}


	start_section = (pfn * PAGE_SIZE) / EHEA_SECTSIZE;
	end_section = start_section + ((pgnum * PAGE_SIZE) / EHEA_SECTSIZE);
	/* Mark entries as valid or invalid only; address is assigned later */
	for (i = start_section; i < end_section; i++) {
	for (i = start_section; i < end_section; i++) {
		int ret;
		u64 flag;
		int top, dir, idx;
		int top = ehea_calc_index(i, EHEA_TOP_INDEX_SHIFT);
		u64 vaddr;
		int dir = ehea_calc_index(i, EHEA_DIR_INDEX_SHIFT);

		int idx = i & EHEA_INDEX_MASK;
		top = ehea_calc_index(i, EHEA_TOP_INDEX_SHIFT);
		dir = ehea_calc_index(i, EHEA_DIR_INDEX_SHIFT);
		
		
		ret = ehea_init_bmap(ehea_bmap, top, dir);
		if (add) {
			int ret = ehea_init_bmap(ehea_bmap, top, dir);
			if (ret)
			if (ret)
				return 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;
		}


		idx = i & EHEA_INDEX_MASK;
		ehea_bmap->top[top]->dir[dir]->ent[idx] = flag;
		vaddr = EHEA_BUSMAP_START + mr_len + i * EHEA_SECTSIZE;
	}

	ehea_rebuild_busmap(); /* Assign contiguous addresses for mr */
		ehea_bmap->top[top]->dir[dir]->ent[idx] = vaddr;
	return 0;
}
}


	mr_len += nr_pages * PAGE_SIZE;
int ehea_add_sect_bmap(unsigned long pfn, unsigned long nr_pages)
	*(unsigned long *)arg = mr_len;
{
	int ret;


	return 0;
	mutex_lock(&ehea_busmap_mutex);
	ret = ehea_update_busmap(pfn, nr_pages, EHEA_BUSMAP_ADD_SECT);
	mutex_unlock(&ehea_busmap_mutex);
	return ret;
}
}


static unsigned long ehea_mr_len;
int ehea_rem_sect_bmap(unsigned long pfn, unsigned long nr_pages)
{
	int ret;


static DEFINE_MUTEX(ehea_busmap_mutex);
	mutex_lock(&ehea_busmap_mutex);
	ret = ehea_update_busmap(pfn, nr_pages, EHEA_BUSMAP_REM_SECT);
	mutex_unlock(&ehea_busmap_mutex);
	return ret;
}

static int ehea_create_busmap_callback(unsigned long pfn,
				       unsigned long nr_pages, void *arg)
{
	return ehea_update_busmap(pfn, nr_pages, EHEA_BUSMAP_ADD_SECT);
}


int ehea_create_busmap(void)
int ehea_create_busmap(void)
{
{
	int ret;
	int ret;

	mutex_lock(&ehea_busmap_mutex);
	mutex_lock(&ehea_busmap_mutex);
	ehea_mr_len = 0;
	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);
				   ehea_create_busmap_callback);
	mutex_unlock(&ehea_busmap_mutex);
	mutex_unlock(&ehea_busmap_mutex);
	return ret;
	return ret;
+2 −0
Original line number Original line Diff line number Diff line
@@ -378,6 +378,8 @@ int ehea_rem_mr(struct ehea_mr *mr);


void ehea_error_data(struct ehea_adapter *adapter, u64 res_handle);
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);
int ehea_create_busmap(void);
void ehea_destroy_busmap(void);
void ehea_destroy_busmap(void);
u64 ehea_map_vaddr(void *caddr);
u64 ehea_map_vaddr(void *caddr);