#include #include static void pblk_write(int fd, u32 pblk_size, u64 pblk, u32 count, const u8* data) { off_t pos; size_t remain; ssize_t ret; pos = lseek(fd, pblk * pblk_size, SEEK_SET); if (pos == (off_t)-1) { error("Failed to seek\n"); } remain = count * pblk_size; while (remain) { ret = write(fd, data, remain); if (ret <= 0) { error("Failed to write\n"); } remain -= ret; data += ret; } } #define FORMAT_FLAGS_MASK \ (CBD_FLAG_DETECT_ZEROS) int cbd_format(const char* dev, uint16_t flags, enum cbd_alg alg, uint level, uint8_t pshift, uint8_t lshift, uint8_t pbatshift, uint64_t psize, uint64_t lsize, bool full_init) { int devfd; uint pblk_size; uint lblk_size; uint32_t est_zone_len; struct cbd_header header; uint8_t header_buf[PAGE_SIZE]; devfd = open(dev, O_RDWR); if (devfd < 0) { error("Cannot open device\n"); } if (flags != (flags & FORMAT_FLAGS_MASK)) { error("Bad flags 0x%04x\n", (unsigned int)flags); } if (alg <= CBD_ALG_NONE || alg >= CBD_ALG_MAX) { error("Compression algorithm %d unknown\n", (int)alg); } if (level < 1 || level > 9) { error("Compression level %u out of bounds\n", level); } if (!pshift) { pshift = CBD_DEFAULT_PHYSICAL_BLOCK_SHIFT; } if (pshift < PBLK_SHIFT_MIN || pshift > PBLK_SHIFT_MAX) { error("Physical block shift %u is not in [%u,%u]\n", (uint)pshift, (uint)PBLK_SHIFT_MIN, (uint)PBLK_SHIFT_MAX); } pblk_size = SECTOR_SIZE * (1 << pshift); if (!lshift) { lshift = CBD_DEFAULT_LOGICAL_BLOCK_SHIFT; } if (lshift < LBLK_SHIFT_MIN || lshift > LBLK_SHIFT_MAX) { error("Logical block shift %u is not in [%u,%u]\n", (uint)lshift, (uint)LBLK_SHIFT_MIN, (uint)LBLK_SHIFT_MAX); } lblk_size = pblk_size * (1 << lshift); if (pbatshift < PBAT_SHIFT_MIN || pbatshift > PBAT_SHIFT_MAX) { error("Physical block allocation table shift %u is not in [%u,%u]\n", (uint)pbatshift, (uint)PBAT_SHIFT_MIN, (uint)PBAT_SHIFT_MAX); } if (!psize) { off_t pos; pos = lseek(devfd, 0, SEEK_END); if (pos == (off_t)-1) { error("Cannot seek device\n"); } psize = pos / pblk_size * pblk_size; } if (psize % pblk_size) { error("Physical size %lu is not a multiple of %u bytes\n", (ulong)psize, pblk_size); } if (!lsize) { /* XXX: Why is the cast needed here? */ lsize = (uint64_t)(psize * CBD_DEFAULT_COMPRESSION_FACTOR) / lblk_size * lblk_size; } if (lsize % lblk_size) { error("Logical size %lu is not a multiple of %u bytes\n", (ulong)lsize, lblk_size); } printf("%s: parameters...\n", __func__); printf(" pshift=%u\n", (unsigned int)pshift); printf(" lshift=%u\n", (unsigned int)lshift); printf(" pbatshift=%u\n", (unsigned int)pbatshift); printf(" alg=%d\n", (int)alg); printf(" level=%u\n", level); printf(" psize=%lu\n", (unsigned long)psize); printf(" lsize=%lu\n", (unsigned long)lsize); memset(&header, 0, sizeof(header)); memcpy(header.magic, CBD_MAGIC, sizeof(header.magic)); header.version_major = CBD_VERSION_MAJOR; header.version_minor = CBD_VERSION_MINOR; header.params.flags = flags; header.params.compression = 0; cbd_compression_alg_put(&header.params, alg); cbd_compression_level_put(&header.params, level); header.params.pblk_shift = pshift; header.params.lblk_shift = lshift; header.params.lba_elem_pblk_bytes = ((psize / pblk_size) <= 0xffff ? 2 : ((psize / pblk_size) <= 0xffffffff ? 4 : 6)); header.params.pbat_shift = pbatshift; if (pbat_len(&header.params) * pblk_size > PAGE_SIZE) { error("Physical block allocation table too large\n"); } if (lba_len(&header.params) > pblk_size) { error("lba element size too large\n"); } /* XXX: Initial estimate */ header.params.lblk_per_zone = zone_data_len(&header.params) * (lsize / lblk_size) / (psize / pblk_size); printf(" initial estimate for lblk_per_zone: %u\n", (unsigned int)header.params.lblk_per_zone); header.params.nr_zones = ((psize / pblk_size) - CBD_HEADER_BLOCKS) / zone_len(&header.params); est_zone_len = zone_len(&header.params); header.params.lblk_per_zone = DIV_ROUND_UP(lsize / lblk_size, header.params.nr_zones); while (zone_len(&header.params) > est_zone_len) { --header.params.lblk_per_zone; } header.params.init_zones = 0; printf("%s: header...\n", __func__); printf(" compression=0x%02x\n", (unsigned int)header.params.compression); printf(" pblk_shift=%hu\n", (unsigned short)header.params.pblk_shift); printf(" lblk_shift=%hu\n", (unsigned short)header.params.lblk_shift); printf(" lba_elem_pblk_bytes=%hu\n", (unsigned short)header.params.lba_elem_pblk_bytes); printf(" pbat_shift=%hu\n", (unsigned short)header.params.pbat_shift); printf(" nr_zones=%u\n", (unsigned int)header.params.nr_zones); printf(" lblk_per_zone=%u\n", (unsigned int)header.params.lblk_per_zone); memset(header_buf, 0, sizeof(header_buf)); cbd_header_put(header_buf, &header); pblk_write(devfd, pblk_size, 0, 1, header_buf); if (full_init) { uint32_t nr_pblk = zone_metadata_len(&header.params); uint8_t* data_buf; uint64_t pblk; time_t now; time_t next_write; printf("Writing %lu zones ...\n", (unsigned long)header.params.nr_zones); data_buf = calloc(nr_pblk, pblk_size); next_write = time(NULL) + 5; while (header.params.init_zones < header.params.nr_zones) { pblk = zone_off(&header.params, header.params.init_zones); pblk_write(devfd, pblk_size, pblk, nr_pblk, data_buf); ++header.params.init_zones; now = time(NULL); if (now >= next_write) { printf("Initialized %u/%u zones\n", header.params.init_zones, header.params.nr_zones); cbd_header_put(header_buf, &header); pblk_write(devfd, pblk_size, 0, 1, header_buf); next_write = now + 5; } } free(data_buf); cbd_header_put(header_buf, &header); pblk_write(devfd, pblk_size, 0, 1, header_buf); } return 0; }