Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fadump: Invalidate registration and release reserved memory for general use.

This patch introduces an sysfs interface '/sys/kernel/fadump_release_mem' to
invalidate the last fadump registration, invalidate '/proc/vmcore', release
the reserved memory for general use and re-register for future kernel dump.
Once the dump is copied to the disk, unlike phyp dump, the userspace tool
can release all the memory reserved for dump with one single operation of
echo 1 to '/sys/kernel/fadump_release_mem'.

Release the reserved memory region excluding the size of the memory required
for future kernel dump registration. And therefore, unlike kdump, Fadump
doesn't need a 2nd reboot to get back the system to the production
configuration.

Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

authored by

Mahesh Salgaonkar and committed by
Benjamin Herrenschmidt
b500afff 16257393

+157 -4
+3
arch/powerpc/include/asm/fadump.h
··· 208 208 extern int setup_fadump(void); 209 209 extern int is_fadump_active(void); 210 210 extern void crash_fadump(struct pt_regs *, const char *); 211 + extern void fadump_cleanup(void); 212 + 213 + extern void vmcore_cleanup(void); 211 214 #else /* CONFIG_FA_DUMP */ 212 215 static inline int is_fadump_active(void) { return 0; } 213 216 static inline void crash_fadump(struct pt_regs *regs, const char *str) { }
+154 -4
arch/powerpc/kernel/fadump.c
··· 33 33 #include <linux/debugfs.h> 34 34 #include <linux/seq_file.h> 35 35 #include <linux/crash_dump.h> 36 + #include <linux/kobject.h> 37 + #include <linux/sysfs.h> 36 38 37 39 #include <asm/page.h> 38 40 #include <asm/prom.h> ··· 986 984 return 0; 987 985 } 988 986 987 + static int fadump_invalidate_dump(struct fadump_mem_struct *fdm) 988 + { 989 + int rc = 0; 990 + unsigned int wait_time; 991 + 992 + pr_debug("Invalidating firmware-assisted dump registration\n"); 993 + 994 + /* TODO: Add upper time limit for the delay */ 995 + do { 996 + rc = rtas_call(fw_dump.ibm_configure_kernel_dump, 3, 1, NULL, 997 + FADUMP_INVALIDATE, fdm, 998 + sizeof(struct fadump_mem_struct)); 999 + 1000 + wait_time = rtas_busy_delay_time(rc); 1001 + if (wait_time) 1002 + mdelay(wait_time); 1003 + } while (wait_time); 1004 + 1005 + if (rc) { 1006 + printk(KERN_ERR "Failed to invalidate firmware-assisted dump " 1007 + "rgistration. unexpected error(%d).\n", rc); 1008 + return rc; 1009 + } 1010 + fw_dump.dump_active = 0; 1011 + fdm_active = NULL; 1012 + return 0; 1013 + } 1014 + 1015 + void fadump_cleanup(void) 1016 + { 1017 + /* Invalidate the registration only if dump is active. */ 1018 + if (fw_dump.dump_active) { 1019 + init_fadump_mem_struct(&fdm, 1020 + fdm_active->cpu_state_data.destination_address); 1021 + fadump_invalidate_dump(&fdm); 1022 + } 1023 + } 1024 + 1025 + /* 1026 + * Release the memory that was reserved in early boot to preserve the memory 1027 + * contents. The released memory will be available for general use. 1028 + */ 1029 + static void fadump_release_memory(unsigned long begin, unsigned long end) 1030 + { 1031 + unsigned long addr; 1032 + unsigned long ra_start, ra_end; 1033 + 1034 + ra_start = fw_dump.reserve_dump_area_start; 1035 + ra_end = ra_start + fw_dump.reserve_dump_area_size; 1036 + 1037 + for (addr = begin; addr < end; addr += PAGE_SIZE) { 1038 + /* 1039 + * exclude the dump reserve area. Will reuse it for next 1040 + * fadump registration. 1041 + */ 1042 + if (addr <= ra_end && ((addr + PAGE_SIZE) > ra_start)) 1043 + continue; 1044 + 1045 + ClearPageReserved(pfn_to_page(addr >> PAGE_SHIFT)); 1046 + init_page_count(pfn_to_page(addr >> PAGE_SHIFT)); 1047 + free_page((unsigned long)__va(addr)); 1048 + totalram_pages++; 1049 + } 1050 + } 1051 + 1052 + static void fadump_invalidate_release_mem(void) 1053 + { 1054 + unsigned long reserved_area_start, reserved_area_end; 1055 + unsigned long destination_address; 1056 + 1057 + mutex_lock(&fadump_mutex); 1058 + if (!fw_dump.dump_active) { 1059 + mutex_unlock(&fadump_mutex); 1060 + return; 1061 + } 1062 + 1063 + destination_address = fdm_active->cpu_state_data.destination_address; 1064 + fadump_cleanup(); 1065 + mutex_unlock(&fadump_mutex); 1066 + 1067 + /* 1068 + * Save the current reserved memory bounds we will require them 1069 + * later for releasing the memory for general use. 1070 + */ 1071 + reserved_area_start = fw_dump.reserve_dump_area_start; 1072 + reserved_area_end = reserved_area_start + 1073 + fw_dump.reserve_dump_area_size; 1074 + /* 1075 + * Setup reserve_dump_area_start and its size so that we can 1076 + * reuse this reserved memory for Re-registration. 1077 + */ 1078 + fw_dump.reserve_dump_area_start = destination_address; 1079 + fw_dump.reserve_dump_area_size = get_fadump_area_size(); 1080 + 1081 + fadump_release_memory(reserved_area_start, reserved_area_end); 1082 + if (fw_dump.cpu_notes_buf) { 1083 + fadump_cpu_notes_buf_free( 1084 + (unsigned long)__va(fw_dump.cpu_notes_buf), 1085 + fw_dump.cpu_notes_buf_size); 1086 + fw_dump.cpu_notes_buf = 0; 1087 + fw_dump.cpu_notes_buf_size = 0; 1088 + } 1089 + /* Initialize the kernel dump memory structure for FAD registration. */ 1090 + init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start); 1091 + } 1092 + 1093 + static ssize_t fadump_release_memory_store(struct kobject *kobj, 1094 + struct kobj_attribute *attr, 1095 + const char *buf, size_t count) 1096 + { 1097 + if (!fw_dump.dump_active) 1098 + return -EPERM; 1099 + 1100 + if (buf[0] == '1') { 1101 + /* 1102 + * Take away the '/proc/vmcore'. We are releasing the dump 1103 + * memory, hence it will not be valid anymore. 1104 + */ 1105 + vmcore_cleanup(); 1106 + fadump_invalidate_release_mem(); 1107 + 1108 + } else 1109 + return -EINVAL; 1110 + return count; 1111 + } 1112 + 989 1113 static ssize_t fadump_enabled_show(struct kobject *kobj, 990 1114 struct kobj_attribute *attr, 991 1115 char *buf) ··· 1171 1043 if (!fw_dump.fadump_enabled) 1172 1044 return 0; 1173 1045 1046 + mutex_lock(&fadump_mutex); 1174 1047 if (fdm_active) 1175 1048 fdm_ptr = fdm_active; 1176 - else 1049 + else { 1050 + mutex_unlock(&fadump_mutex); 1177 1051 fdm_ptr = &fdm; 1052 + } 1178 1053 1179 1054 seq_printf(m, 1180 1055 "CPU : [%#016llx-%#016llx] %#llx bytes, " ··· 1207 1076 if (!fdm_active || 1208 1077 (fw_dump.reserve_dump_area_start == 1209 1078 fdm_ptr->cpu_state_data.destination_address)) 1210 - return 0; 1079 + goto out; 1211 1080 1212 1081 /* Dump is active. Show reserved memory region. */ 1213 1082 seq_printf(m, ··· 1219 1088 fw_dump.reserve_dump_area_start, 1220 1089 fdm_ptr->cpu_state_data.destination_address - 1221 1090 fw_dump.reserve_dump_area_start); 1091 + out: 1092 + if (fdm_active) 1093 + mutex_unlock(&fadump_mutex); 1222 1094 return 0; 1223 1095 } 1224 1096 1097 + static struct kobj_attribute fadump_release_attr = __ATTR(fadump_release_mem, 1098 + 0200, NULL, 1099 + fadump_release_memory_store); 1225 1100 static struct kobj_attribute fadump_attr = __ATTR(fadump_enabled, 1226 1101 0444, fadump_enabled_show, 1227 1102 NULL); ··· 1268 1131 if (!debugfs_file) 1269 1132 printk(KERN_ERR "fadump: unable to create debugfs file" 1270 1133 " fadump_region\n"); 1134 + 1135 + if (fw_dump.dump_active) { 1136 + rc = sysfs_create_file(kernel_kobj, &fadump_release_attr.attr); 1137 + if (rc) 1138 + printk(KERN_ERR "fadump: unable to create sysfs file" 1139 + " fadump_release_mem (%d)\n", rc); 1140 + } 1271 1141 return; 1272 1142 } 1273 1143 ··· 1297 1153 * If dump data is available then see if it is valid and prepare for 1298 1154 * saving it to the disk. 1299 1155 */ 1300 - if (fw_dump.dump_active) 1301 - process_fadump(fdm_active); 1156 + if (fw_dump.dump_active) { 1157 + /* 1158 + * if dump process fails then invalidate the registration 1159 + * and release memory before proceeding for re-registration. 1160 + */ 1161 + if (process_fadump(fdm_active) < 0) 1162 + fadump_invalidate_release_mem(); 1163 + } 1302 1164 /* Initialize the kernel dump memory structure for FAD registration. */ 1303 1165 else if (fw_dump.reserve_dump_area_size) 1304 1166 init_fadump_mem_struct(&fdm, fw_dump.reserve_dump_area_start);