/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Artem B. Bityutskiy * * This test does a lot of I/O to volumes in parallel. */ #include <stdio.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <pthread.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include "libubi.h" #define PROGRAM_NAME "io_paral" #include "common.h" #include "helpers.h" #define THREADS_NUM 4 #define ITERATIONS (1024 * 1) #define VOL_LEBS 10 static libubi_t libubi; static struct ubi_dev_info dev_info; static const char *node; static int vol_size; static struct ubi_mkvol_request reqests[THREADS_NUM + 1]; static char vol_name[THREADS_NUM + 1][100]; static char vol_nodes[THREADS_NUM + 1][sizeof(UBI_VOLUME_PATTERN) + 99]; static unsigned char *wbufs[THREADS_NUM + 1]; static unsigned char *rbufs[THREADS_NUM + 1]; static int update_volume(int vol_id, int bytes) { int i, fd, ret, written = 0, rd = 0; char *vol_node = vol_nodes[vol_id]; unsigned char *wbuf = wbufs[vol_id]; unsigned char *rbuf = rbufs[vol_id]; unsigned int seed = seed_random_generator(); fd = open(vol_node, O_RDWR); if (fd == -1) { failed("open"); errorm("cannot open \"%s\"\n", vol_node); return -1; } for (i = 0; i < bytes; i++) wbuf[i] = rand_r(&seed) % 255; memset(rbuf, '\0', bytes); ret = ubi_update_start(libubi, fd, bytes); if (ret) { failed("ubi_update_start"); errorm("volume id is %d", vol_id); goto err_close; } while (written < bytes) { int to_write = rand_r(&seed) % (bytes - written); if (to_write == 0) to_write = 1; ret = write(fd, wbuf + written, to_write); if (ret != to_write) { failed("write"); errorm("failed to write %d bytes at offset %d " "of volume %d", to_write, written, vol_id); errorm("update: %d bytes", bytes); goto err_close; } written += to_write; } close(fd); fd = open(vol_node, O_RDONLY); if (fd == -1) { failed("open"); errorm("cannot open \"%s\"\n", node); return -1; } /* read data back and check */ while (rd < bytes) { int to_read = rand_r(&seed) % (bytes - rd); if (to_read == 0) to_read = 1; ret = read(fd, rbuf + rd, to_read); if (ret != to_read) { failed("read"); errorm("failed to read %d bytes at offset %d " "of volume %d", to_read, rd, vol_id); goto err_close; } rd += to_read; } if (memcmp(wbuf, rbuf, bytes)) { errorm("written and read data are different"); goto err_close; } close(fd); return 0; err_close: close(fd); return -1; } static void *update_thread(void *ptr) { int vol_id = (long)ptr, i; unsigned int seed = seed_random_generator(); for (i = 0; i < ITERATIONS; i++) { int ret, bytes = (rand_r(&seed) % (vol_size - 1)) + 1; int remove = !(rand_r(&seed) % 16); /* From time to time remove the volume */ if (remove) { ret = ubi_rmvol(libubi, node, vol_id); if (ret) { failed("ubi_rmvol"); errorm("cannot remove volume %d", vol_id); return (void *) -1; } ret = ubi_mkvol(libubi, node, &reqests[vol_id]); if (ret) { failed("ubi_mkvol"); errorm("cannot create volume %d", vol_id); return (void *) -1; } } ret = update_volume(vol_id, bytes); if (ret != 0) return (void *) -1; } return (void *) 0; } static void *write_thread(void *ptr) { int ret, fd, vol_id = (long)ptr, i; char *vol_node = vol_nodes[vol_id]; unsigned char *wbuf = wbufs[vol_id]; unsigned char *rbuf = rbufs[vol_id]; unsigned int seed = seed_random_generator(); fd = open(vol_node, O_RDWR); if (fd == -1) { failed("open"); errorm("cannot open \"%s\"\n", vol_node); return (void *) -1; } ret = ubi_set_property(fd, UBI_VOL_PROP_DIRECT_WRITE, 1); if (ret) { failed("ubi_set_property"); errorm("cannot set property for \"%s\"\n", vol_node); } for (i = 0; i < ITERATIONS * VOL_LEBS; i++) { int j, leb = rand_r(&seed) % VOL_LEBS; off_t offs = dev_info.leb_size * leb; ret = ubi_leb_unmap(fd, leb); if (ret) { failed("ubi_leb_unmap"); errorm("cannot unmap LEB %d", leb); break; } for (j = 0; j < dev_info.leb_size; j++) wbuf[j] = rand_r(&seed) % 255; memset(rbuf, '\0', dev_info.leb_size); ret = pwrite(fd, wbuf, dev_info.leb_size, offs); if (ret != dev_info.leb_size) { failed("pwrite"); errorm("cannot write %d bytes to offs %ld, wrote %d", dev_info.leb_size, offs, ret); break; } /* read data back and check */ ret = pread(fd, rbuf, dev_info.leb_size, offs); if (ret != dev_info.leb_size) { failed("read"); errorm("failed to read %d bytes at offset %ld " "of volume %d", dev_info.leb_size, offs, vol_id); break; } if (memcmp(wbuf, rbuf, dev_info.leb_size)) { errorm("written and read data are different"); break; } } close(fd); return (void *) 0; } int main(int argc, char * const argv[]) { int i, ret, error=false; intptr_t thread_ret; pthread_t threads[THREADS_NUM]; if (initial_check(argc, argv)) return 1; node = argv[1]; libubi = libubi_open(); if (libubi == NULL) { failed("libubi_open"); return 1; } if (ubi_get_dev_info(libubi, node, &dev_info)) { failed("ubi_get_dev_info"); goto close; } /* * Create 1 volume more than threads count. The last volume * will not change to let WL move more stuff. */ vol_size = dev_info.leb_size * VOL_LEBS; for (i = 0; i <= THREADS_NUM; i++) { reqests[i].alignment = 1; reqests[i].bytes = vol_size; reqests[i].vol_id = i; sprintf(vol_name[i], PROGRAM_NAME":%d", i); reqests[i].name = vol_name[i]; reqests[i].vol_type = UBI_DYNAMIC_VOLUME; reqests[i].flags = 0; if (i == THREADS_NUM) reqests[i].vol_type = UBI_STATIC_VOLUME; sprintf(vol_nodes[i], UBI_VOLUME_PATTERN, dev_info.dev_num, i); if (ubi_mkvol(libubi, node, &reqests[i])) { failed("ubi_mkvol"); goto remove; } wbufs[i] = malloc(vol_size); rbufs[i] = malloc(vol_size); if (!wbufs[i] || !rbufs[i]) { failed("malloc"); goto remove; } ret = update_volume(i, vol_size); if (ret) goto remove; } for (i = 0; i < THREADS_NUM / 2; i++) { ret = pthread_create(&threads[i], NULL, &write_thread, (void *)(long)i); if (ret) { failed("pthread_create"); goto remove; } } for (i = THREADS_NUM / 2; i < THREADS_NUM; i++) { ret = pthread_create(&threads[i], NULL, &update_thread, (void *)(long)i); if (ret) { failed("pthread_create"); goto remove; } } for (i = 0; i < THREADS_NUM; i++) { pthread_join(threads[i], (void **) &thread_ret); if (thread_ret != 0) error = true; } if (error) goto remove; for (i = 0; i <= THREADS_NUM; i++) { if (ubi_rmvol(libubi, node, i)) { failed("ubi_rmvol"); goto remove; } if (wbufs[i]) free(wbufs[i]); if (rbufs[i]) free(rbufs[i]); wbufs[i] = NULL; rbufs[i] = NULL; } libubi_close(libubi); return 0; remove: for (i = 0; i <= THREADS_NUM; i++) { ubi_rmvol(libubi, node, i); if (wbufs[i]) free(wbufs[i]); if (rbufs[i]) free(rbufs[i]); wbufs[i] = NULL; rbufs[i] = NULL; } close: libubi_close(libubi); return 1; }