123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781 |
- #define _LARGEFILE64_SOURCE
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <stdint.h>
- #include <string.h>
- #include <errno.h>
- #include <sys/ioctl.h>
- #include <sys/stat.h>
- #include "libubi.h"
- #define PROGRAM_NAME "integ"
- #include "common.h"
- #include "helpers.h"
- struct erase_block_info;
- struct volume_info;
- struct ubi_device_info;
- struct write_info
- {
- struct write_info *next;
- struct erase_block_info *erase_block;
- int offset_within_block; /* Offset within erase block */
- off_t offset; /* Offset within volume */
- int size;
- int random_seed;
- };
- struct erase_block_info
- {
- struct volume_info *volume;
- int block_number;
- off_t offset; /* Offset within volume */
- off_t top_of_data;
- int touched; /* Have we done anything at all with this erase block */
- int erased; /* This erased block is currently erased */
- struct write_info *writes;
- };
- struct volume_fd
- {
- struct volume_fd *next;
- struct volume_info *volume;
- int fd;
- };
- struct volume_info
- {
- struct volume_info *next;
- struct ubi_device_info *ubi_device;
- struct volume_fd *fds;
- struct erase_block_info *erase_blocks;
- const char *device_file_name;
- struct ubi_vol_info info;
- };
- struct ubi_device_info
- {
- struct volume_info *volumes;
- const char *device_file_name;
- struct ubi_dev_info info;
- };
- struct open_volume_fd
- {
- struct open_volume_fd *next;
- struct volume_fd *vol_fd;
- };
- #define MAX_UBI_DEVICES 64
- static libubi_t libubi;
- static struct ubi_info info;
- static struct ubi_device_info ubi_array[MAX_UBI_DEVICES];
- static uint64_t total_written = 0;
- static uint64_t total_space = 0;
- static struct open_volume_fd *open_volumes;
- static int open_volume_count = 0;
- static const char *ubi_module_load_string;
- static unsigned char *write_buffer = NULL;
- static unsigned char *read_buffer = NULL;
- static long long max_ebs_per_vol = 0; /* max number of ebs per vol (zero => no max) */
- static unsigned long next_seed = 1;
- static unsigned get_next_seed()
- {
- next_seed = next_seed * 1103515245 + 12345;
- return ((unsigned) (next_seed / 65536) % 32768);
- }
- static void error_exit(const char *msg)
- {
- int eno = errno;
- fprintf(stderr,"UBI Integrity Test Error: %s\n",msg);
- if (eno) {
- fprintf(stderr, "errno = %d\n", eno);
- fprintf(stderr, "strerror = %s\n", strerror(eno));
- }
- exit(1);
- }
- static void *allocate(size_t n)
- {
- void *p = malloc(n);
- if (!p)
- error_exit("Memory allocation failure");
- memset(p, 0, n);
- return p;
- }
- static unsigned get_random_number(unsigned n)
- {
- uint64_t r, b;
- if (n < 1)
- return 0;
- r = rand();
- r *= n;
- b = RAND_MAX;
- b += 1;
- r /= b;
- return r;
- }
- static struct volume_fd *open_volume(struct volume_info *vol)
- {
- struct volume_fd *s;
- struct open_volume_fd *ofd;
- int fd;
- if (vol->fds) {
- /* If already open dup it */
- fd = dup(vol->fds->fd);
- if (fd == -1)
- error_exit("Failed to dup volume device file des");
- } else {
- fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE);
- if (fd == -1)
- error_exit("Failed to open volume device file");
- }
- s = allocate(sizeof(*s));
- s->fd = fd;
- s->volume = vol;
- s->next = vol->fds;
- vol->fds = s;
- /* Add to open volumes list */
- ofd = allocate(sizeof(*ofd));
- ofd->vol_fd = s;
- ofd->next = open_volumes;
- open_volumes = ofd;
- open_volume_count += 1;
- return 0;
- }
- static void close_volume(struct volume_fd *vol_fd)
- {
- struct volume_fd *vfd, *vfd_last;
- struct open_volume_fd *ofd, *ofd_last;
- int fd = vol_fd->fd;
- /* Remove from open volumes list */
- ofd_last = NULL;
- ofd = open_volumes;
- while (ofd) {
- if (ofd->vol_fd == vol_fd) {
- if (ofd_last)
- ofd_last->next = ofd->next;
- else
- open_volumes = ofd->next;
- free(ofd);
- open_volume_count -= 1;
- break;
- }
- ofd_last = ofd;
- ofd = ofd->next;
- }
- /* Remove from volume fd list */
- vfd_last = NULL;
- vfd = vol_fd->volume->fds;
- while (vfd) {
- if (vfd == vol_fd) {
- if (vfd_last)
- vfd_last->next = vfd->next;
- else
- vol_fd->volume->fds = vfd->next;
- free(vfd);
- break;
- }
- vfd_last = vfd;
- vfd = vfd->next;
- }
- /* Close volume device file */
- if (close(fd) == -1)
- error_exit("Failed to close volume file descriptor");
- }
- static void set_random_data(unsigned seed, unsigned char *buf, int size)
- {
- int i;
- unsigned r;
- r = rand();
- srand(seed);
- for (i = 0; i < size; ++i)
- buf[i] = rand();
- srand(r);
- }
- static void check_erase_block(struct erase_block_info *erase_block, int fd)
- {
- struct write_info *w;
- off_t gap_end;
- int leb_size = erase_block->volume->info.leb_size;
- ssize_t bytes_read;
- w = erase_block->writes;
- gap_end = erase_block->offset + leb_size;
- while (w) {
- if (w->offset + w->size < gap_end) {
- /* There is a gap. Check all 0xff */
- off_t gap_start = w->offset + w->size;
- ssize_t size = gap_end - gap_start;
- if (lseek(fd, gap_start, SEEK_SET) != gap_start)
- error_exit("lseek failed");
- memset(read_buffer, 0 , size);
- errno = 0;
- bytes_read = read(fd, read_buffer, size);
- if (bytes_read != size)
- error_exit("read failed in gap");
- while (size)
- if (read_buffer[--size] != 0xff) {
- fprintf(stderr, "block no. = %d\n" , erase_block->block_number);
- fprintf(stderr, "offset = %lld\n" , (long long)gap_start);
- fprintf(stderr, "size = %ld\n" , (long) bytes_read);
- error_exit("verify 0xff failed");
- }
- }
- if (lseek(fd, w->offset, SEEK_SET) != w->offset)
- error_exit("lseek failed");
- memset(read_buffer, 0 , w->size);
- errno = 0;
- bytes_read = read(fd, read_buffer, w->size);
- if (bytes_read != w->size) {
- fprintf(stderr, "offset = %lld\n" , (long long)w->offset);
- fprintf(stderr, "size = %ld\n" , (long) w->size);
- fprintf(stderr, "bytes_read = %ld\n" , (long) bytes_read);
- error_exit("read failed");
- }
- set_random_data(w->random_seed, write_buffer, w->size);
- if (memcmp(read_buffer, write_buffer, w->size))
- error_exit("verify failed");
- gap_end = w->offset;
- w = w->next;
- }
- if (gap_end > erase_block->offset) {
- /* Check all 0xff */
- off_t gap_start = erase_block->offset;
- ssize_t size = gap_end - gap_start;
- if (lseek(fd, gap_start, SEEK_SET) != gap_start)
- error_exit("lseek failed");
- memset(read_buffer, 0 , size);
- errno = 0;
- bytes_read = read(fd, read_buffer, size);
- if (bytes_read != size)
- error_exit("read failed in gap");
- while (size)
- if (read_buffer[--size] != 0xff) {
- fprintf(stderr, "block no. = %d\n" , erase_block->block_number);
- fprintf(stderr, "offset = %lld\n" , (long long)gap_start);
- fprintf(stderr, "size = %ld\n" , (long) bytes_read);
- error_exit("verify 0xff failed!");
- }
- }
- }
- static int write_to_erase_block(struct erase_block_info *erase_block, int fd)
- {
- int page_size = erase_block->volume->ubi_device->info.min_io_size;
- int leb_size = erase_block->volume->info.leb_size;
- int next_offset = 0;
- int space, size;
- off_t offset;
- unsigned seed;
- struct write_info *w;
- if (erase_block->writes)
- next_offset = erase_block->writes->offset_within_block + erase_block->writes->size;
- space = leb_size - next_offset;
- if (space <= 0)
- return 0; /* No space */
- if (!get_random_number(10)) {
- /* 1 time in 10 leave a gap */
- next_offset += get_random_number(space);
- next_offset = (next_offset / page_size) * page_size;
- space = leb_size - next_offset;
- }
- if (get_random_number(2))
- size = 1 * page_size;
- else if (get_random_number(2))
- size = 2 * page_size;
- else if (get_random_number(2))
- size = 3 * page_size;
- else if (get_random_number(2))
- size = 4 * page_size;
- else {
- if (get_random_number(4))
- size = get_random_number(space);
- else
- size = space;
- size = (size / page_size) * page_size;
- }
- if (size == 0 || size > space)
- size = page_size;
- if (next_offset + size > leb_size)
- error_exit("internal error");
- offset = erase_block->offset + next_offset;
- if (offset < erase_block->top_of_data)
- error_exit("internal error!");
- if (lseek(fd, offset, SEEK_SET) != offset)
- error_exit("lseek failed");
- /* Do write */
- seed = get_next_seed();
- if (!seed)
- seed = 1;
- set_random_data(seed, write_buffer, size);
- if (write(fd, write_buffer, size) != size)
- error_exit("write failed");
- erase_block->top_of_data = offset + size;
- /* Make write info and add to eb */
- w = allocate(sizeof(*w));
- w->offset_within_block = next_offset;
- w->offset = offset;
- w->size = size;
- w->random_seed = seed;
- w->next = erase_block->writes;
- erase_block->writes = w;
- erase_block->touched = 1;
- erase_block->erased = 0;
- total_written += size;
- return 1;
- }
- static void erase_erase_block(struct erase_block_info *erase_block, int fd)
- {
- struct write_info *w;
- uint32_t eb_no;
- int res;
- eb_no = erase_block->block_number;
- res = ioctl(fd, UBI_IOCEBER, &eb_no);
- if (res)
- error_exit("Failed to erase an erase block");
- /* Remove writes from this eb */
- while (erase_block->writes) {
- w = erase_block->writes;
- erase_block->writes = erase_block->writes->next;
- free(w);
- }
- erase_block->erased = 1;
- erase_block->touched = 1;
- erase_block->top_of_data = erase_block->offset;
- }
- static void operate_on_erase_block(struct erase_block_info *erase_block, int fd)
- {
- /*
- Possible operations:
- read from it and verify
- write to it
- erase it
- */
- int work_done = 1;
- static int no_work_done_count = 0;
- if (!get_random_number(10) && no_work_done_count <= 5) {
- check_erase_block(erase_block, fd);
- work_done = 0;
- } else if (get_random_number(100)) {
- if (!write_to_erase_block(erase_block, fd)) {
- /* The erase block was full */
- if (get_random_number(2) || no_work_done_count > 5)
- erase_erase_block(erase_block, fd);
- else
- work_done = 0;
- }
- } else
- erase_erase_block(erase_block, fd);
- if (work_done)
- no_work_done_count = 0;
- else
- no_work_done_count += 1;
- }
- static void operate_on_open_volume(struct volume_fd *vol_fd)
- {
- /*
- Possible operations:
- operate on an erase block
- close volume
- */
- if (get_random_number(100) == 0)
- close_volume(vol_fd);
- else {
- /* Pick an erase block at random */
- int eb_no = get_random_number(vol_fd->volume->info.rsvd_lebs);
- operate_on_erase_block(&vol_fd->volume->erase_blocks[eb_no], vol_fd->fd);
- }
- }
- static void operate_on_volume(struct volume_info *vol)
- {
- /*
- Possible operations:
- open it
- resize it (must close fd's first) <- TODO
- delete it (must close fd's first) <- TODO
- */
- open_volume(vol);
- }
- static int ubi_major(const char *device_file_name)
- {
- struct stat buf;
- static int maj = 0;
- if (maj)
- return maj;
- if (stat(device_file_name, &buf) == -1)
- error_exit("Failed to stat ubi device file");
- maj = major(buf.st_rdev);
- return maj;
- }
- static void operate_on_ubi_device(struct ubi_device_info *ubi_device)
- {
- /*
- TODO:
- Possible operations:
- create a new volume
- operate on existing volume
- */
- /*
- Simplified operation (i.e. only have 1 volume):
- If there are no volumes create 1 volumne
- Then operate on the volume
- */
- if (ubi_device->info.vol_count == 0) {
- /* Create the one-and-only volume we will use */
- char dev_name[1024];
- int i, n, maj, fd;
- struct volume_info *s;
- struct ubi_mkvol_request req;
- req.vol_id = UBI_VOL_NUM_AUTO;
- req.alignment = 1; /* TODO: What is this? */
- req.bytes = ubi_device->info.leb_size * max_ebs_per_vol;
- req.flags = 0;
- if (req.bytes == 0 || req.bytes > ubi_device->info.avail_bytes)
- req.bytes = ubi_device->info.avail_bytes;
- req.vol_type = UBI_DYNAMIC_VOLUME;
- req.name = "integ-test-vol";
- if (ubi_mkvol(libubi, ubi_device->device_file_name, &req))
- error_exit("ubi_mkvol failed");
- s = allocate(sizeof(*s));
- s->ubi_device = ubi_device;
- if (ubi_get_vol_info1(libubi, ubi_device->info.dev_num, req.vol_id, &s->info))
- error_exit("ubi_get_vol_info failed");
- n = s->info.rsvd_lebs;
- s->erase_blocks = allocate(sizeof(struct erase_block_info) * n);
- for (i = 0; i < n; ++i) {
- s->erase_blocks[i].volume = s;
- s->erase_blocks[i].block_number = i;
- s->erase_blocks[i].offset = i * (off_t) s->info.leb_size;
- s->erase_blocks[i].top_of_data = s->erase_blocks[i].offset;
- }
- /* FIXME: Correctly get device file name */
- sprintf(dev_name, "%s_%d", ubi_device->device_file_name, req.vol_id);
- s->device_file_name = strdup(dev_name);
- ubi_device->volumes = s;
- ubi_device->info.vol_count += 1;
- sleep(1);
- fd = open(s->device_file_name, O_RDONLY);
- if (fd == -1) {
- /* FIXME: Correctly make node */
- maj = ubi_major(ubi_device->device_file_name);
- sprintf(dev_name, "mknod %s c %d %d", s->device_file_name, maj, req.vol_id + 1);
- if (system(dev_name))
- error_exit("Failed to create device file");
- } else if (close(fd) == -1)
- error_exit("Failed to close volume device file");
- }
- operate_on_volume(ubi_device->volumes);
- }
- static void do_an_operation(void)
- {
- int too_few = (open_volume_count < info.dev_count * 3);
- int too_many = (open_volume_count > info.dev_count * 5);
- if (too_many || (!too_few && get_random_number(1000) > 0)) {
- /* Operate on an open volume */
- size_t pos;
- struct open_volume_fd *ofd;
- pos = get_random_number(open_volume_count);
- for (ofd = open_volumes; pos && ofd && ofd->next; --pos)
- ofd = ofd->next;
- operate_on_open_volume(ofd->vol_fd);
- } else if (info.dev_count > 0) {
- /* Operate on a ubi device */
- size_t ubi_pos = 0;
- if (info.dev_count > 1)
- ubi_pos = get_random_number(info.dev_count - 1);
- operate_on_ubi_device(&ubi_array[ubi_pos]);
- } else
- error_exit("Internal error");
- }
- static void get_ubi_devices_info(void)
- {
- int i, ubi_pos = 0;
- char dev_name[1024];
- ssize_t buf_size = 1024 * 128;
- if (ubi_get_info(libubi, &info))
- error_exit("ubi_get_info failed");
- if (info.dev_count > MAX_UBI_DEVICES)
- error_exit("Too many ubi devices");
- for (i = info.lowest_dev_num; i <= info.highest_dev_num; ++i) {
- struct ubi_device_info *s;
- s = &ubi_array[ubi_pos++];
- if (ubi_get_dev_info1(libubi, i, &s->info))
- error_exit("ubi_get_dev_info1 failed");
- if (s->info.vol_count)
- error_exit("There are existing volumes");
- /* FIXME: Correctly get device file name */
- sprintf(dev_name, "/dev/ubi%d", i);
- s->device_file_name = strdup(dev_name);
- if (buf_size < s->info.leb_size)
- buf_size = s->info.leb_size;
- if (max_ebs_per_vol && s->info.leb_size * max_ebs_per_vol < s->info.avail_bytes)
- total_space += s->info.leb_size * max_ebs_per_vol;
- else
- total_space += s->info.avail_bytes;
- }
- write_buffer = allocate(buf_size);
- read_buffer = allocate(buf_size);
- }
- static void load_ubi(void)
- {
- if (system("modprobe -r ubi"))
- error_exit("Failed to unload UBI module");
- if (system(ubi_module_load_string) != 0)
- error_exit("Failed to load UBI module");
- sleep(1);
- }
- static void do_some_operations(void)
- {
- unsigned i = 0;
- total_written = 0;
- printf("Total space: %llu\n", (unsigned long long) total_space);
- while (total_written < total_space * 3) {
- do_an_operation();
- if (i++ % 10000 == 0)
- printf("Total written: %llu\n", (unsigned long long) total_written);
- }
- printf("Total written: %llu\n", (unsigned long long) total_written);
- }
- static void reload_ubi(void)
- {
- /* Remove module */
- if (system("rmmod ubi") != 0)
- error_exit("Failed to remove UBI module");
- /* Install module */
- if (system(ubi_module_load_string) != 0)
- error_exit("Failed to load UBI module");
- sleep(1);
- }
- static void integ_check_volume(struct volume_info *vol)
- {
- struct erase_block_info *eb = vol->erase_blocks;
- int pos;
- int fd;
- fd = open(vol->device_file_name, O_RDWR | O_LARGEFILE);
- if (fd == -1)
- error_exit("Failed to open volume device file");
- for (pos = 0; pos < vol->info.rsvd_lebs; ++pos)
- check_erase_block(eb++, fd);
- if (close(fd) == -1)
- error_exit("Failed to close volume device file");
- }
- static void check_ubi_device(struct ubi_device_info *ubi_device)
- {
- struct volume_info *vol;
- vol = ubi_device->volumes;
- while (vol) {
- integ_check_volume(vol);
- vol = vol->next;
- }
- }
- static void check_ubi(void)
- {
- int i;
- for (i = 0; i < info.dev_count; ++i)
- check_ubi_device(&ubi_array[i]);
- }
- static int is_all_digits(const char *s)
- {
- const char *digits = "0123456789";
- if (!s || !*s)
- return 0;
- for (;*s;++s)
- if (!strchr(digits,*s))
- return 0;
- return 1;
- }
- static int get_short_arg(int *pos,const char *name,long long *result,int argc,char *argv[])
- {
- const char *p = NULL;
- int i = *pos;
- size_t n = strlen(name);
- if (strlen(argv[i]) > n)
- p = argv[i] + n;
- else if (++i < argc)
- p = argv[i];
- if (!is_all_digits(p))
- return 1;
- *result = atoll(p);
- *pos = i;
- return 0;
- }
- static int get_long_arg(int *pos,const char *name,long long *result,int argc,char *argv[])
- {
- const char *p = NULL;
- int i = *pos;
- size_t n = strlen(name);
- if (strlen(argv[i]) > n)
- p = argv[i] + n;
- else if (++i < argc)
- p = argv[i];
- if (p && *p == '=') {
- p += 1;
- if (!*p && ++i < argc)
- p = argv[i];
- }
- if (!is_all_digits(p))
- return 1;
- *result = atoll(p);
- *pos = i;
- return 0;
- }
- static int remove_all_volumes(void)
- {
- int i;
- for (i = 0; i < info.dev_count; ++i) {
- struct ubi_device_info *ubi_device = &ubi_array[i];
- struct volume_info *vol;
- vol = ubi_device->volumes;
- while (vol) {
- int res = ubi_rmvol(libubi,
- ubi_device->device_file_name,
- vol->info.vol_id);
- if (res)
- return res;
- vol = vol->next;
- }
- }
- return 0;
- }
- int main(int argc,char *argv[])
- {
- int i;
- long long r, repeat = 1;
- int initial_seed = 1, args_ok = 1;
- printf("UBI Integrity Test\n");
- /* Get arguments */
- ubi_module_load_string = 0;
- for (i = 1; i < argc; ++i) {
- if (strncmp(argv[i], "-h", 2) == 0)
- args_ok = 0;
- else if (strncmp(argv[i], "--help", 6) == 0)
- args_ok = 0;
- else if (strncmp(argv[i], "-n", 2) == 0) {
- if (get_short_arg(&i, "-n", &repeat, argc, argv))
- args_ok = 0;
- } else if (strncmp(argv[i], "--repeat", 8) == 0) {
- if (get_long_arg(&i, "--repeat", &repeat, argc, argv))
- args_ok = 0;
- } else if (strncmp(argv[i], "-m", 2) == 0) {
- if (get_short_arg(&i,"-m", &max_ebs_per_vol, argc, argv))
- args_ok = 0;
- } else if (strncmp(argv[i], "--maxebs", 8) == 0) {
- if (get_long_arg(&i, "--maxebs", &max_ebs_per_vol, argc, argv))
- args_ok = 0;
- } else if (!ubi_module_load_string)
- ubi_module_load_string = argv[i];
- else
- args_ok = 0;
- }
- if (!args_ok || !ubi_module_load_string) {
- fprintf(stderr, "Usage is: ubi_integ [<options>] <UBI Module load command>\n");
- fprintf(stderr, " Options: \n");
- fprintf(stderr, " -h, --help Help\n");
- fprintf(stderr, " -n arg, --repeat=arg Repeat test arg times\n");
- fprintf(stderr, " -m arg, --maxebs=arg Max no. of erase blocks\n");
- return 1;
- }
- next_seed = initial_seed = seed_random_generator();
- printf("Initial seed = %u\n", (unsigned) initial_seed);
- load_ubi();
- libubi = libubi_open();
- if (!libubi)
- error_exit("Failed to open libubi");
- get_ubi_devices_info();
- r = 0;
- while (repeat == 0 || r++ < repeat) {
- printf("Cycle %lld\n", r);
- do_some_operations();
- /* Close all volumes */
- while (open_volumes)
- close_volume(open_volumes->vol_fd);
- check_ubi();
- libubi_close(libubi);
- reload_ubi();
- libubi = libubi_open();
- if (!libubi)
- error_exit("Failed to open libubi");
- check_ubi();
- }
- if (remove_all_volumes())
- error_exit("Failed to remove all volumes");
- libubi_close(libubi);
- printf("UBI Integrity Test completed ok\n");
- return 0;
- }
|