Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial MDRAID support #277

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 2 additions & 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 Expand Up @@ -75,6 +76,7 @@ EXTRA_DIST += \
test/ext4test.1.dump \
test/ext4test.2.dump \
test/f2fs.config \
test/mdraid.config \
test/btrfs.config \
test/fip.config \
test/fit.its \
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");
}
Harvie marked this conversation as resolved.
Show resolved Hide resolved
*/

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;
Harvie marked this conversation as resolved.
Show resolved Hide resolved
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);
Harvie marked this conversation as resolved.
Show resolved Hide resolved

/* constant array information - 128 bytes */
sb->magic = 0xa92b4efc; /* MD_SB_MAGIC: 0xa92b4efc - little endian */
Harvie marked this conversation as resolved.
Show resolved Hide resolved
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?
Harvie marked this conversation as resolved.
Show resolved Hide resolved
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*/
Harvie marked this conversation as resolved.
Show resolved Hide resolved

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 */
Harvie marked this conversation as resolved.
Show resolved Hide resolved
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?
Harvie marked this conversation as resolved.
Show resolved Hide resolved
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);
Harvie marked this conversation as resolved.
Show resolved Hide resolved
if(ret) return ret;
if(img_in) {
ret = insert_image(image, img_in, image->size-DATA_OFFSET_BYTES, DATA_OFFSET_BYTES, 0);
Harvie marked this conversation as resolved.
Show resolved Hide resolved
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) {
Harvie marked this conversation as resolved.
Show resolved Hide resolved
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");
Harvie marked this conversation as resolved.
Show resolved Hide resolved
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);
Harvie marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
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);
}
Harvie marked this conversation as resolved.
Show resolved Hide resolved

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 &&
Harvie marked this conversation as resolved.
Show resolved Hide resolved
mdadm --examine images/test.mdraid | grep 'Checksum.*correct$' &&
mdadm --examine images/test.mdraid | grep 'State.*active$'
"

test_done

# vim: syntax=sh