336 lines
7.3 KiB
C
336 lines
7.3 KiB
C
|
/*
|
||
|
* 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 <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];
|
||
|
|
||
|
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() % 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() % (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() % (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;
|
||
|
|
||
|
for (i = 0; i < ITERATIONS; i++) {
|
||
|
int ret, bytes = (rand() % (vol_size - 1)) + 1;
|
||
|
int remove = !(rand() % 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 NULL;
|
||
|
}
|
||
|
ret = ubi_mkvol(libubi, node, &reqests[vol_id]);
|
||
|
if (ret) {
|
||
|
failed("ubi_mkvol");
|
||
|
errorm("cannot create volume %d", vol_id);
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ret = update_volume(vol_id, bytes);
|
||
|
if (ret)
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
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];
|
||
|
|
||
|
fd = open(vol_node, O_RDWR);
|
||
|
if (fd == -1) {
|
||
|
failed("open");
|
||
|
errorm("cannot open \"%s\"\n", vol_node);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
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() % 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() % 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 %lld, 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 %d "
|
||
|
"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 NULL;
|
||
|
}
|
||
|
|
||
|
int main(int argc, char * const argv[])
|
||
|
{
|
||
|
int i, ret;
|
||
|
pthread_t threads[THREADS_NUM];
|
||
|
|
||
|
seed_random_generator();
|
||
|
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;
|
||
|
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], NULL);
|
||
|
|
||
|
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;
|
||
|
}
|