Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6d6aa1c9 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "soc: qcom: mem-offline: add support for multiple size pasr segments"

parents 7697c3ba e51c9bb5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -566,6 +566,7 @@ CONFIG_RPMSG_QCOM_GLINK_SMEM=y
CONFIG_RPMSG_QCOM_GLINK_SPSS=y
CONFIG_QCOM_COMMAND_DB=y
CONFIG_QCOM_MEM_OFFLINE=y
CONFIG_BUG_ON_HW_MEM_ONLINE_FAIL=y
CONFIG_OVERRIDE_MEMORY_LIMIT=y
CONFIG_QCOM_CPUSS_DUMP=y
CONFIG_QCOM_RUN_QUEUE_STATS=y
+141 −31
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ enum memory_states {
	MAX_STATE,
};

static enum memory_states *mem_hw_state;
static enum memory_states *mem_sec_state;

static struct mem_offline_mailbox {
	struct mbox_client cl;
@@ -136,6 +136,113 @@ static int aop_send_msg(unsigned long addr, bool online)
	return (mbox_send_message(mailbox.mbox, &pkt) < 0);
}

/*
 * When offline_granule >= memory block size, this returns the number of
 * sections in a offlineable segment.
 * When offline_granule < memory block size, returns the sections_per_block.
 */
static unsigned long get_rounded_sections_per_segment(void)
{

	return max(((offline_granule * SZ_1M) / memory_block_size_bytes()) *
		     sections_per_block,
		     (unsigned long)sections_per_block);
}

static int send_msg(struct memory_notify *mn, bool online, int count)
{
	unsigned long segment_size = offline_granule * SZ_1M;
	unsigned long start, base_sec_nr, sec_nr, sections_per_segment;
	int ret, idx, i;

	sections_per_segment = get_rounded_sections_per_segment();
	sec_nr = pfn_to_section_nr(SECTION_ALIGN_DOWN(mn->start_pfn));
	idx = (sec_nr - start_section_nr) / sections_per_segment;
	base_sec_nr = start_section_nr + (idx * sections_per_segment);
	start = section_nr_to_pfn(base_sec_nr);

	for (i = 0; i < count; ++i) {
		ret = aop_send_msg(__pfn_to_phys(start), online);
		if (ret) {
			pr_err("PASR: AOP %s request addr:0x%llx failed\n",
			       online ? "online" : "offline",
			       __pfn_to_phys(start));
			goto undo;
		}

		start = __phys_to_pfn(__pfn_to_phys(start) + segment_size);
	}

	return 0;
undo:
	start = section_nr_to_pfn(base_sec_nr);
	while (i-- > 0) {
		int ret;

		ret = aop_send_msg(__pfn_to_phys(start), !online);
		if (ret)
			panic("Failed to completely online/offline a hotpluggable segment. A quasi state of memblock can cause randomn system failures.");
		start = __phys_to_pfn(__pfn_to_phys(start) + segment_size);
	}

	return ret;
}

static bool need_to_send_remote_request(struct memory_notify *mn,
				    enum memory_states request)
{
	int i, idx, cur_idx;
	int base_sec_nr, sec_nr;
	unsigned long sections_per_segment;

	sections_per_segment = get_rounded_sections_per_segment();
	sec_nr = pfn_to_section_nr(SECTION_ALIGN_DOWN(mn->start_pfn));
	idx = (sec_nr - start_section_nr) / sections_per_segment;
	cur_idx = (sec_nr - start_section_nr) / sections_per_block;
	base_sec_nr = start_section_nr + (idx * sections_per_segment);

	/*
	 * For MEM_OFFLINE, don't send the request if there are other online
	 * blocks in the segment.
	 * For MEM_ONLINE, don't send the request if there is already one
	 * online block in the segment.
	 */
	if (request == MEMORY_OFFLINE || request == MEMORY_ONLINE) {
		for (i = base_sec_nr;
		     i < (base_sec_nr + sections_per_segment);
		     i += sections_per_block) {
			idx = (i - start_section_nr) / sections_per_block;
			/* current operating block */
			if (idx == cur_idx)
				continue;
			if (mem_sec_state[idx] == MEMORY_ONLINE)
				goto out;
		}
		return true;
	}
out:
	return false;
}

/*
 * This returns the number of hotpluggable segments in a memory block.
 */
static int get_num_memblock_hotplug_segments(void)
{
	unsigned long segment_size = offline_granule * SZ_1M;
	unsigned long block_size = memory_block_size_bytes();

	if (segment_size < block_size) {
		if (block_size % segment_size) {
			pr_warn("PASR is unusable. Offline granule size should be in multiples for memory_block_size_bytes.\n");
			return 0;
		}
		return block_size / segment_size;
	}

	return 1;
}

static int mem_change_refresh_state(struct memory_notify *mn,
				    enum memory_states state)
{
@@ -143,22 +250,31 @@ static int mem_change_refresh_state(struct memory_notify *mn,
	unsigned long sec_nr = pfn_to_section_nr(start);
	bool online = (state == MEMORY_ONLINE) ? true : false;
	unsigned long idx = (sec_nr - start_section_nr) / sections_per_block;
	int ret;
	int ret, count;

	if (mem_hw_state[idx] == state) {
	if (mem_sec_state[idx] == state) {
		/* we shouldn't be getting this request */
		pr_warn("mem-offline: hardware state of mem%d block already in %s state. Ignoring refresh state change request\n",
		pr_warn("mem-offline: state of mem%d block already in %s state. Ignoring refresh state change request\n",
				sec_nr, online ? "online" : "offline");
		return 0;
	}
	ret = aop_send_msg(__pfn_to_phys(start), online);

	count = get_num_memblock_hotplug_segments();
	if (!count)
		return -EINVAL;

	if (!need_to_send_remote_request(mn, state))
		goto out;

	ret = send_msg(mn, online, count);
	if (ret) {
		if (state == MEMORY_ONLINE)
		/* online failures are critical failures */
		if (online)
			BUG_ON(IS_ENABLED(CONFIG_BUG_ON_HW_MEM_ONLINE_FAIL));
		return -EINVAL;
	}
	mem_hw_state[idx] = state;

out:
	mem_sec_state[idx] = state;
	return 0;
}

@@ -201,11 +317,9 @@ static int mem_event_callback(struct notifier_block *self,
			   idx) / sections_per_block].fail_count;
		cur = ktime_get();

		if (mem_change_refresh_state(mn, MEMORY_ONLINE)) {
			pr_err("PASR: AOP online request addr:0x%llx failed\n",
			       __pfn_to_phys(start));
		if (mem_change_refresh_state(mn, MEMORY_ONLINE))
			return NOTIFY_BAD;
		}

		if (!debug_pagealloc_enabled()) {
			/* Create kernel page-tables */
			create_pgtable_mapping(start_addr, end_addr);
@@ -231,14 +345,11 @@ static int mem_event_callback(struct notifier_block *self,
			/* Clear kernel page-tables */
			clear_pgtable_mapping(start_addr, end_addr);
		}
		if (mem_change_refresh_state(mn, MEMORY_OFFLINE)) {
			pr_err("PASR: AOP offline request addr:0x%llx failed\n",
			       __pfn_to_phys(start));
		mem_change_refresh_state(mn, MEMORY_OFFLINE);
		/*
		 * Notifying that something went bad at this stage won't
		 * help since this is the last stage of memory hotplug.
		 */
		}

		delay = ktime_ms_delta(ktime_get(), cur);
		record_stat(sec_nr, delay, MEMORY_OFFLINE);
@@ -249,9 +360,7 @@ static int mem_event_callback(struct notifier_block *self,
	case MEM_CANCEL_ONLINE:
		pr_info("mem-offline: MEM_CANCEL_ONLINE: start = 0x%llx end = 0x%llx\n",
				start_addr, end_addr);
		if (mem_change_refresh_state(mn, MEMORY_OFFLINE))
			pr_err("PASR: AOP offline request addr:0x%llx failed\n",
			       __pfn_to_phys(start));
		mem_change_refresh_state(mn, MEMORY_OFFLINE);
		break;
	default:
		break;
@@ -411,8 +520,9 @@ static int mem_parse_dt(struct platform_device *pdev)
		return -EINVAL;
	}
	offline_granule = be32_to_cpup(val);
	if (!offline_granule && !(offline_granule & (offline_granule - 1)) &&
			offline_granule * SZ_1M < MIN_MEMORY_BLOCK_SIZE) {
	if (!offline_granule || (offline_granule & (offline_granule - 1)) ||
	    ((offline_granule * SZ_1M < MIN_MEMORY_BLOCK_SIZE) &&
	     (MIN_MEMORY_BLOCK_SIZE % (offline_granule * SZ_1M)))) {
		pr_err("mem-offine: invalid granule property\n");
		return -EINVAL;
	}
@@ -461,19 +571,19 @@ static int mem_offline_driver_probe(struct platform_device *pdev)
	if (!mem_info)
		return -ENOMEM;

	mem_hw_state = kcalloc(total_blks, sizeof(*mem_hw_state), GFP_KERNEL);
	if (!mem_hw_state) {
	mem_sec_state = kcalloc(total_blks, sizeof(*mem_sec_state), GFP_KERNEL);
	if (!mem_sec_state) {
		ret = -ENOMEM;
		goto err_free_mem_info;
	}

	/* we assume that hardware state of mem blocks are online after boot */
	for (i = 0; i < total_blks; i++)
		mem_hw_state[i] = MEMORY_ONLINE;
		mem_sec_state[i] = MEMORY_ONLINE;

	if (mem_sysfs_init()) {
		ret = -ENODEV;
		goto err_free_mem_hw_state;
		goto err_free_mem_sec_state;
	}

	if (register_hotmemory_notifier(&hotplug_memory_callback_nb)) {
@@ -489,8 +599,8 @@ static int mem_offline_driver_probe(struct platform_device *pdev)
err_sysfs_remove_group:
	sysfs_remove_group(kobj, &mem_attr_group);
	kobject_put(kobj);
err_free_mem_hw_state:
	kfree(mem_hw_state);
err_free_mem_sec_state:
	kfree(mem_sec_state);
err_free_mem_info:
	kfree(mem_info);
	return ret;