dump: add API to write dump pages
functions are used to write page to vmcore. vmcore is written page by page. page desc is used to store the information of a page, including a page's size, offset, compression format, etc. Signed-off-by: Qiao Nuohan <qiaonuohan@cn.fujitsu.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
This commit is contained in:
		
							parent
							
								
									64cfba6a47
								
							
						
					
					
						commit
						d12f57ec66
					
				
							
								
								
									
										231
									
								
								dump.c
									
									
									
									
									
								
							
							
						
						
									
										231
									
								
								dump.c
									
									
									
									
									
								
							| @ -25,6 +25,14 @@ | ||||
| #include "qapi/error.h" | ||||
| #include "qmp-commands.h" | ||||
| 
 | ||||
| #include <zlib.h> | ||||
| #ifdef CONFIG_LZO | ||||
| #include <lzo/lzo1x.h> | ||||
| #endif | ||||
| #ifdef CONFIG_SNAPPY | ||||
| #include <snappy-c.h> | ||||
| #endif | ||||
| 
 | ||||
| static uint16_t cpu_convert_to_target16(uint16_t val, int endian) | ||||
| { | ||||
|     if (endian == ELFDATA2LSB) { | ||||
| @ -1212,6 +1220,229 @@ static void free_data_cache(DataCache *data_cache) | ||||
|     g_free(data_cache->buf); | ||||
| } | ||||
| 
 | ||||
| static size_t get_len_buf_out(size_t page_size, uint32_t flag_compress) | ||||
| { | ||||
|     size_t len_buf_out_zlib, len_buf_out_lzo, len_buf_out_snappy; | ||||
|     size_t len_buf_out; | ||||
| 
 | ||||
|     /* init buf_out */ | ||||
|     len_buf_out_zlib = len_buf_out_lzo = len_buf_out_snappy = 0; | ||||
| 
 | ||||
|     /* buf size for zlib */ | ||||
|     len_buf_out_zlib = compressBound(page_size); | ||||
| 
 | ||||
|     /* buf size for lzo */ | ||||
| #ifdef CONFIG_LZO | ||||
|     if (flag_compress & DUMP_DH_COMPRESSED_LZO) { | ||||
|         if (lzo_init() != LZO_E_OK) { | ||||
|             /* return 0 to indicate lzo is unavailable */ | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /*
 | ||||
|      * LZO will expand incompressible data by a little amount. please check the | ||||
|      * following URL to see the expansion calculation: | ||||
|      * http://www.oberhumer.com/opensource/lzo/lzofaq.php
 | ||||
|      */ | ||||
|     len_buf_out_lzo = page_size + page_size / 16 + 64 + 3; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_SNAPPY | ||||
|     /* buf size for snappy */ | ||||
|     len_buf_out_snappy = snappy_max_compressed_length(page_size); | ||||
| #endif | ||||
| 
 | ||||
|     /* get the biggest that can store all kinds of compressed page */ | ||||
|     len_buf_out = MAX(len_buf_out_zlib, | ||||
|                       MAX(len_buf_out_lzo, len_buf_out_snappy)); | ||||
| 
 | ||||
|     return len_buf_out; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * check if the page is all 0 | ||||
|  */ | ||||
| static inline bool is_zero_page(const uint8_t *buf, size_t page_size) | ||||
| { | ||||
|     return buffer_is_zero(buf, page_size); | ||||
| } | ||||
| 
 | ||||
| static int write_dump_pages(DumpState *s) | ||||
| { | ||||
|     int ret = 0; | ||||
|     DataCache page_desc, page_data; | ||||
|     size_t len_buf_out, size_out; | ||||
| #ifdef CONFIG_LZO | ||||
|     lzo_bytep wrkmem = NULL; | ||||
| #endif | ||||
|     uint8_t *buf_out = NULL; | ||||
|     off_t offset_desc, offset_data; | ||||
|     PageDescriptor pd, pd_zero; | ||||
|     uint8_t *buf; | ||||
|     int endian = s->dump_info.d_endian; | ||||
|     GuestPhysBlock *block_iter = NULL; | ||||
|     uint64_t pfn_iter; | ||||
| 
 | ||||
|     /* get offset of page_desc and page_data in dump file */ | ||||
|     offset_desc = s->offset_page; | ||||
|     offset_data = offset_desc + sizeof(PageDescriptor) * s->num_dumpable; | ||||
| 
 | ||||
|     prepare_data_cache(&page_desc, s, offset_desc); | ||||
|     prepare_data_cache(&page_data, s, offset_data); | ||||
| 
 | ||||
|     /* prepare buffer to store compressed data */ | ||||
|     len_buf_out = get_len_buf_out(s->page_size, s->flag_compress); | ||||
|     if (len_buf_out == 0) { | ||||
|         dump_error(s, "dump: failed to get length of output buffer.\n"); | ||||
|         goto out; | ||||
|     } | ||||
| 
 | ||||
| #ifdef CONFIG_LZO | ||||
|     wrkmem = g_malloc(LZO1X_1_MEM_COMPRESS); | ||||
| #endif | ||||
| 
 | ||||
|     buf_out = g_malloc(len_buf_out); | ||||
| 
 | ||||
|     /*
 | ||||
|      * init zero page's page_desc and page_data, because every zero page | ||||
|      * uses the same page_data | ||||
|      */ | ||||
|     pd_zero.size = cpu_convert_to_target32(s->page_size, endian); | ||||
|     pd_zero.flags = cpu_convert_to_target32(0, endian); | ||||
|     pd_zero.offset = cpu_convert_to_target64(offset_data, endian); | ||||
|     pd_zero.page_flags = cpu_convert_to_target64(0, endian); | ||||
|     buf = g_malloc0(s->page_size); | ||||
|     ret = write_cache(&page_data, buf, s->page_size, false); | ||||
|     g_free(buf); | ||||
|     if (ret < 0) { | ||||
|         dump_error(s, "dump: failed to write page data(zero page).\n"); | ||||
|         goto out; | ||||
|     } | ||||
| 
 | ||||
|     offset_data += s->page_size; | ||||
| 
 | ||||
|     /*
 | ||||
|      * dump memory to vmcore page by page. zero page will all be resided in the | ||||
|      * first page of page section | ||||
|      */ | ||||
|     while (get_next_page(&block_iter, &pfn_iter, &buf, s)) { | ||||
|         /* check zero page */ | ||||
|         if (is_zero_page(buf, s->page_size)) { | ||||
|             ret = write_cache(&page_desc, &pd_zero, sizeof(PageDescriptor), | ||||
|                               false); | ||||
|             if (ret < 0) { | ||||
|                 dump_error(s, "dump: failed to write page desc.\n"); | ||||
|                 goto out; | ||||
|             } | ||||
|         } else { | ||||
|             /*
 | ||||
|              * not zero page, then: | ||||
|              * 1. compress the page | ||||
|              * 2. write the compressed page into the cache of page_data | ||||
|              * 3. get page desc of the compressed page and write it into the | ||||
|              *    cache of page_desc | ||||
|              * | ||||
|              * only one compression format will be used here, for | ||||
|              * s->flag_compress is set. But when compression fails to work, | ||||
|              * we fall back to save in plaintext. | ||||
|              */ | ||||
|              size_out = len_buf_out; | ||||
|              if ((s->flag_compress & DUMP_DH_COMPRESSED_ZLIB) && | ||||
|                     (compress2(buf_out, (uLongf *)&size_out, buf, s->page_size, | ||||
|                     Z_BEST_SPEED) == Z_OK) && (size_out < s->page_size)) { | ||||
|                 pd.flags = cpu_convert_to_target32(DUMP_DH_COMPRESSED_ZLIB, | ||||
|                                                    endian); | ||||
|                 pd.size  = cpu_convert_to_target32(size_out, endian); | ||||
| 
 | ||||
|                 ret = write_cache(&page_data, buf_out, size_out, false); | ||||
|                 if (ret < 0) { | ||||
|                     dump_error(s, "dump: failed to write page data.\n"); | ||||
|                     goto out; | ||||
|                 } | ||||
| #ifdef CONFIG_LZO | ||||
|             } else if ((s->flag_compress & DUMP_DH_COMPRESSED_LZO) && | ||||
|                     (lzo1x_1_compress(buf, s->page_size, buf_out, | ||||
|                     (lzo_uint *)&size_out, wrkmem) == LZO_E_OK) && | ||||
|                     (size_out < s->page_size)) { | ||||
|                 pd.flags = cpu_convert_to_target32(DUMP_DH_COMPRESSED_LZO, | ||||
|                                                    endian); | ||||
|                 pd.size  = cpu_convert_to_target32(size_out, endian); | ||||
| 
 | ||||
|                 ret = write_cache(&page_data, buf_out, size_out, false); | ||||
|                 if (ret < 0) { | ||||
|                     dump_error(s, "dump: failed to write page data.\n"); | ||||
|                     goto out; | ||||
|                 } | ||||
| #endif | ||||
| #ifdef CONFIG_SNAPPY | ||||
|             } else if ((s->flag_compress & DUMP_DH_COMPRESSED_SNAPPY) && | ||||
|                     (snappy_compress((char *)buf, s->page_size, | ||||
|                     (char *)buf_out, &size_out) == SNAPPY_OK) && | ||||
|                     (size_out < s->page_size)) { | ||||
|                 pd.flags = cpu_convert_to_target32( | ||||
|                                         DUMP_DH_COMPRESSED_SNAPPY, endian); | ||||
|                 pd.size  = cpu_convert_to_target32(size_out, endian); | ||||
| 
 | ||||
|                 ret = write_cache(&page_data, buf_out, size_out, false); | ||||
|                 if (ret < 0) { | ||||
|                     dump_error(s, "dump: failed to write page data.\n"); | ||||
|                     goto out; | ||||
|                 } | ||||
| #endif | ||||
|             } else { | ||||
|                 /*
 | ||||
|                  * fall back to save in plaintext, size_out should be | ||||
|                  * assigned to s->page_size | ||||
|                  */ | ||||
|                 pd.flags = cpu_convert_to_target32(0, endian); | ||||
|                 size_out = s->page_size; | ||||
|                 pd.size = cpu_convert_to_target32(size_out, endian); | ||||
| 
 | ||||
|                 ret = write_cache(&page_data, buf, s->page_size, false); | ||||
|                 if (ret < 0) { | ||||
|                     dump_error(s, "dump: failed to write page data.\n"); | ||||
|                     goto out; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             /* get and write page desc here */ | ||||
|             pd.page_flags = cpu_convert_to_target64(0, endian); | ||||
|             pd.offset = cpu_convert_to_target64(offset_data, endian); | ||||
|             offset_data += size_out; | ||||
| 
 | ||||
|             ret = write_cache(&page_desc, &pd, sizeof(PageDescriptor), false); | ||||
|             if (ret < 0) { | ||||
|                 dump_error(s, "dump: failed to write page desc.\n"); | ||||
|                 goto out; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ret = write_cache(&page_desc, NULL, 0, true); | ||||
|     if (ret < 0) { | ||||
|         dump_error(s, "dump: failed to sync cache for page_desc.\n"); | ||||
|         goto out; | ||||
|     } | ||||
|     ret = write_cache(&page_data, NULL, 0, true); | ||||
|     if (ret < 0) { | ||||
|         dump_error(s, "dump: failed to sync cache for page_data.\n"); | ||||
|         goto out; | ||||
|     } | ||||
| 
 | ||||
| out: | ||||
|     free_data_cache(&page_desc); | ||||
|     free_data_cache(&page_data); | ||||
| 
 | ||||
| #ifdef CONFIG_LZO | ||||
|     g_free(wrkmem); | ||||
| #endif | ||||
| 
 | ||||
|     g_free(buf_out); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static ram_addr_t get_start_block(DumpState *s) | ||||
| { | ||||
|     GuestPhysBlock *block; | ||||
|  | ||||
| @ -151,6 +151,13 @@ typedef struct DataCache { | ||||
|     off_t offset;       /* offset of the file */ | ||||
| } DataCache; | ||||
| 
 | ||||
| typedef struct QEMU_PACKED PageDescriptor { | ||||
|     uint64_t offset;                /* the offset of the page data*/ | ||||
|     uint32_t size;                  /* the size of this dump page */ | ||||
|     uint32_t flags;                 /* flags */ | ||||
|     uint64_t page_flags;            /* page flags */ | ||||
| } PageDescriptor; | ||||
| 
 | ||||
| struct GuestPhysBlockList; /* memory_mapping.h */ | ||||
| int cpu_get_dump_info(ArchDumpInfo *info, | ||||
|                       const struct GuestPhysBlockList *guest_phys_blocks); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 qiaonuohan
						qiaonuohan