Loading arch/arm64/configs/vendor/kona_defconfig +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading drivers/soc/qcom/mem-offline.c +141 −31 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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); Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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)) { Loading @@ -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; Loading Loading
arch/arm64/configs/vendor/kona_defconfig +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
drivers/soc/qcom/mem-offline.c +141 −31 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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); Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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)) { Loading @@ -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; Loading