Skip to content

Commit

Permalink
Initial MDRAID support #191
Browse files Browse the repository at this point in the history
Signed-off-by: Tomas Mudrunka <[email protected]>
  • Loading branch information
Harvie committed Dec 20, 2024
1 parent e9c769e commit 3d315c9
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 3 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ jobs:
matrix:
include:
- os: ubuntu-22.04
pkgs: device-tree-compiler rauc simg2img u-boot-tools f2fs-tools arm-trusted-firmware-tools
pkgs: device-tree-compiler rauc simg2img u-boot-tools f2fs-tools arm-trusted-firmware-tools mdadm
- os: ubuntu-22.04
pkgs: device-tree-compiler rauc simg2img u-boot-tools f2fs-tools arm-trusted-firmware-tools
pkgs: device-tree-compiler rauc simg2img u-boot-tools f2fs-tools arm-trusted-firmware-tools mdadm
fake: sudo rm /usr/include/linux/fiemap.h /usr/include/linux/fs.h
env: ac_cv_func_fallocate=no
- os: ubuntu-20.04
pkgs: device-tree-compiler rauc simg2img u-boot-tools f2fs-tools
pkgs: device-tree-compiler rauc simg2img u-boot-tools f2fs-tools mdadm

steps:
- name: Inspect environment
Expand Down
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ genimage_SOURCES = \
image-cramfs.c \
image-ext2.c \
image-f2fs.c \
image-mdraid.c \
image-btrfs.c \
image-file.c \
image-fip.c \
Expand Down
1 change: 1 addition & 0 deletions genimage.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ static struct image_handler *handlers[] = {
&ext3_handler,
&ext4_handler,
&f2fs_handler,
&mdraid_handler,
&btrfs_handler,
&file_handler,
&fit_handler,
Expand Down
1 change: 1 addition & 0 deletions genimage.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ extern struct image_handler ext2_handler;
extern struct image_handler ext3_handler;
extern struct image_handler ext4_handler;
extern struct image_handler f2fs_handler;
extern struct image_handler mdraid_handler;
extern struct image_handler btrfs_handler;
extern struct image_handler file_handler;
extern struct image_handler flash_handler;
Expand Down
226 changes: 226 additions & 0 deletions image-mdraid.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
/*
* Copyright (c) 2025 Tomas Mudrunka <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/*
MDRAID Superblock generator
This should create valid mdraid superblock for raid1 with 1 device (more devices can be added once mounted).
It is still very basic, but following seems to be working:
mdadm --examine test.img
losetup /dev/loop1 test.img
mdadm --assemble md /dev/loop1
Some docs:
https://raid.wiki.kernel.org/index.php/RAID_superblock_formats#Sub-versions_of_the_version-1_superblock
https://docs.huihoo.com/doxygen/linux/kernel/3.7/md__p_8h_source.html
*/

#include <confuse.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <errno.h>
#include <linux/raid/md_p.h>

#include "genimage.h"

#define DATA_OFFSET_SECTORS (2048)
#define DATA_OFFSET_BYTES (DATA_OFFSET_SECTORS*512)

static void random_uuid(__u8 *buf)
{
__u32 r[4];
for (int i = 0; i < 4; i++)
r[i] = random();
memcpy(buf, r, 16);
}

static unsigned int calc_sb_1_csum(struct mdp_superblock_1 * sb)
{
unsigned int disk_csum, csum;
unsigned long long newcsum;
int size = sizeof(*sb) + __le32_to_cpu(sb->max_dev)*2;
unsigned int *isuper = (unsigned int*)sb;

/* make sure I can count... (needs include cstddef) */
/*
if (offsetof(struct mdp_superblock_1,data_offset) != 128 ||
offsetof(struct mdp_superblock_1, utime) != 192 ||
sizeof(struct mdp_superblock_1) != 256) {
fprintf(stderr, "WARNING - superblock isn't sized correctly\n");
}
*/

disk_csum = sb->sb_csum;
sb->sb_csum = 0;
newcsum = 0;
for (; size>=4; size -= 4 ) {
newcsum += __le32_to_cpu(*isuper);
isuper++;
}

if (size == 2)
newcsum += __le16_to_cpu(*(unsigned short*) isuper);

csum = (newcsum & 0xffffffff) + (newcsum >> 32);
sb->sb_csum = disk_csum;
return __cpu_to_le32(csum);
}

static int mdraid_generate(struct image *image) {
struct image *img_in = image->handler_priv;

int max_devices = 1;

char *name = cfg_getstr(image->imagesec, "label");

struct mdp_superblock_1 *sb = xzalloc(sizeof(struct mdp_superblock_1)+max_devices*2);

/* constant array information - 128 bytes */
sb->magic = 0xa92b4efc; /* MD_SB_MAGIC: 0xa92b4efc - little endian */
sb->major_version = 1; /* 1 */
sb->feature_map = 0; //MD_FEATURE_BITMAP_OFFSET; /* bit 0 set if 'bitmap_offset' is meaningful */ //TODO: internal bitmap bit is ignored, unless there is correct bitmap with BITMAP_MAGIC in place
sb->pad0 = 0; /* always set to 0 when writing */

random_uuid(sb->set_uuid); /* user-space generated. U8[16]*/ //TODO: should we allow user to set this?
strncpy(sb->set_name, name, 32); sb->set_name[31]=0; /* set and interpreted by user-space. CHAR[32] */
sb->ctime=time(NULL); /* lo 40 bits are seconds, top 24 are microseconds or 0*/

sb->level=1; /* -4 (multipath), -1 (linear), 0,1,4,5 */
//sb->layout=2; /* only for raid5 and raid10 currently */
sb->size=(image->size - DATA_OFFSET_BYTES)/512; /* used size of component devices, in 512byte sectors */

sb->chunksize=0; /* in 512byte sectors - not used in raid 1 */
sb->raid_disks=1;
sb->bitmap_offset=8; /* sectors after start of superblock that bitmap starts
* NOTE: signed, so bitmap can be before superblock
* only meaningful of feature_map[0] is set.
*/

/* constant this-device information - 64 bytes */
sb->data_offset=DATA_OFFSET_SECTORS; /* sector start of data, often 0 */
sb->data_size=(image->size - DATA_OFFSET_BYTES)/512; /* sectors in this device that can be used for data */
sb->super_offset=8; /* sector start of this superblock */

sb->dev_number=0; /* permanent identifier of this device - not role in raid */
sb->cnt_corrected_read=0; /* number of read errors that were corrected by re-writing */
random_uuid(sb->device_uuid); /* user-space setable, ignored by kernel U8[16] */ //TODO: should we allow user to set this?
sb->devflags=0; /* per-device flags. Only two defined...*/
//#define WriteMostly1 1 /* mask for writemostly flag in above */
//#define FailFast1 2 /* Should avoid retries and fixups and just fail */

/* Bad block log. If there are any bad blocks the feature flag is set.
* If offset and size are non-zero, that space is reserved and available
*/
sb->bblog_shift=9; /* shift from sectors to block size */ //TODO: not sure why this is 9
sb->bblog_size=8; /* number of sectors reserved for list */
sb->bblog_offset=16; /* sector offset from superblock to bblog,
* signed - not unsigned */

/* array state information - 64 bytes */
sb->utime=0; /* 40 bits second, 24 bits microseconds */
sb->events=0; /* incremented when superblock updated */
sb->resync_offset=0; /* data before this offset (from data_offset) known to be in sync */
sb->max_dev=max_devices; /* size of devs[] array to consider */
//__u8 pad3[64-32]; /* set to 0 when writing */

/* device state information. Indexed by dev_number.
* 2 bytes per device
* Note there are no per-device state flags. State information is rolled
* into the 'roles' value. If a device is spare or faulty, then it doesn't
* have a meaningful role.
*/
//__le16 dev_roles[]; /* role in array, or 0xffff for a spare, or 0xfffe for faulty */


//Calculate checksum
sb->sb_csum=calc_sb_1_csum(sb);


//construct image file
int ret;
ret = prepare_image(image, image->size);
if(ret) return ret;
ret = insert_data(image, sb, imageoutfile(image), sizeof(struct mdp_superblock_1)+max_devices*2, 8*512);
if(ret) return ret;
if(img_in) {
ret = insert_image(image, img_in, image->size-DATA_OFFSET_BYTES, DATA_OFFSET_BYTES, 0);
if(ret) return ret;
}

free(sb);

return 0;
}

static int mdraid_setup(struct image *image, cfg_t *cfg) {
srand(time(NULL)); //TODO: Should we seed UUID in more unique way?

int raid_level = cfg_getint(image->imagesec, "level");

if(raid_level != 1) {
image_error(image, "MDRAID Currently only supporting raid level 1 (mirror)!\n");
return 1;
}


struct image *img_in = NULL;
struct partition *part;
list_for_each_entry(part, &image->partitions, list) {
if(strcmp(part->name, "data")) {
image_info(image, "MDRAID partition has to be called 'data' instead of '%s'\n", part->name);
} else {
if(img_in) {
image_error(image, "MDRAID cannot contain more than one data partition!\n");
return 2;
}
if(part->image) {
image_info(image, "MDRAID using data from [%s]: %s\n", part->name, part->image);
img_in = image_get(part->image);
if(image->size < (img_in->size + DATA_OFFSET_BYTES)) image->size = (img_in->size + DATA_OFFSET_BYTES);
}
}
}
if(!img_in) {
image_info(image, "MDRAID is created without data.\n");
}
image->handler_priv = img_in;


//Make sure size is aligned (should be divisible by 8 sectors to keep 4kB alignment)
int align = 8*512;
if(image->size % align) {
image->size += align-(image->size % align);
}

return 0;
}

static cfg_opt_t mdraid_opts[] = {
CFG_STR("label", "localhost:42", CFGF_NONE),
CFG_INT("level", 1, CFGF_NONE),
CFG_END()
};

struct image_handler mdraid_handler = {
.type = "mdraid",
.generate = mdraid_generate,
.setup = mdraid_setup,
.opts = mdraid_opts,
};
6 changes: 6 additions & 0 deletions test/mdraid.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
image test.mdraid {
mdraid {
level = 1
}
size = 5M
}
8 changes: 8 additions & 0 deletions test/misc.test
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ test_expect_success fiptool "fip" "
fiptool info images/test.fip
"

exec_test_set_prereq mdadm
test_expect_success mdadm "mdraid" "
run_genimage_root mdraid.config test.mdraid &&
mdadm --examine images/test.mdraid &&
mdadm --examine images/test.mdraid | grep 'Checksum.*correct$' &&
mdadm --examine images/test.mdraid | grep 'State.*active$'
"

test_done

# vim: syntax=sh

0 comments on commit 3d315c9

Please sign in to comment.