diff options
Diffstat (limited to 'tests/ubi-tests')
| -rw-r--r-- | tests/ubi-tests/Makefile | 41 | ||||
| -rw-r--r-- | tests/ubi-tests/README.udev | 25 | ||||
| -rw-r--r-- | tests/ubi-tests/common.c | 336 | ||||
| -rw-r--r-- | tests/ubi-tests/common.h | 103 | ||||
| -rw-r--r-- | tests/ubi-tests/integ.c | 783 | ||||
| -rw-r--r-- | tests/ubi-tests/io_basic.c | 182 | ||||
| -rw-r--r-- | tests/ubi-tests/io_paral.c | 251 | ||||
| -rw-r--r-- | tests/ubi-tests/io_read.c | 398 | ||||
| -rw-r--r-- | tests/ubi-tests/io_update.c | 370 | ||||
| -rw-r--r-- | tests/ubi-tests/mkvol_bad.c | 304 | ||||
| -rw-r--r-- | tests/ubi-tests/mkvol_basic.c | 253 | ||||
| -rw-r--r-- | tests/ubi-tests/mkvol_paral.c | 112 | ||||
| -rw-r--r-- | tests/ubi-tests/rmvol.c | 308 | ||||
| -rw-r--r-- | tests/ubi-tests/rsvol.c | 310 | ||||
| -rwxr-xr-x | tests/ubi-tests/runtests.sh | 39 | 
15 files changed, 3815 insertions, 0 deletions
diff --git a/tests/ubi-tests/Makefile b/tests/ubi-tests/Makefile new file mode 100644 index 0000000..a2a49d0 --- /dev/null +++ b/tests/ubi-tests/Makefile @@ -0,0 +1,41 @@ +UBIUTILS=../../ubi-utils +INCLUDE1=$(UBIUTILS)/inc +INCLUDE2=../../include +LIB=. + +CC := $(CROSS)gcc + +ALL_FILES=libubi io_update rmvol integ +ALL_FILES+=io_paral io_read io_basic mkvol_basic mkvol_bad mkvol_paral rsvol + +CFLAGS += -Wall -I$(INCLUDE1) -I$(INCLUDE2) -L$(LIB) -ggdb + +all: $(ALL_FILES) + +libubi: $(UBIUTILS)/src/libubi.c  $(UBIUTILS)/inc/libubi.h  $(UBIUTILS)/src/libubi_int.h +	$(CC) $(CFLAGS) -DUDEV_SETTLE_HACK -c $(UBIUTILS)/src/libubi.c -o libubi.o +	ar cr libubi.a libubi.o + +io_paral: io_paral.c common.c +	$(CC) $(CFLAGS) $^ -lubi -lpthread -o $@ +io_update: io_update.c common.c +	$(CC) $(CFLAGS) $^ -lubi -o $@ +io_read: io_read.c common.c +	$(CC) $(CFLAGS) $^ -lubi -o $@ +io_basic: io_basic.c common.c +	$(CC) $(CFLAGS) $^ -lubi -o $@ +mkvol_basic: mkvol_basic.c common.c +	$(CC) $(CFLAGS) $^ -lubi -o $@ +mkvol_bad: mkvol_bad.c common.c +	$(CC) $(CFLAGS) $^ -lubi -o $@ +mkvol_paral: mkvol_paral.c common.c +	$(CC) $(CFLAGS) $^ -lubi -lpthread -o $@ +rsvol: rsvol.c common.c +	$(CC) $(CFLAGS) $^ -lubi -o $@ +rmvol: rmvol.c common.c +	$(CC) $(CFLAGS) $^ -lubi -o $@ +integ: integ.c +	$(CC) $(CFLAGS) $^ -lubi -o $@ + +clean: +	rm -rf $(ALL_FILES) $(addsuffix .o, $(ALL_FILES)) diff --git a/tests/ubi-tests/README.udev b/tests/ubi-tests/README.udev new file mode 100644 index 0000000..06e71d3 --- /dev/null +++ b/tests/ubi-tests/README.udev @@ -0,0 +1,25 @@ +There is a problem with udev: when a volume is created, there is a delay +before corresponding /dev/ubiX_Y device node is created by udev, so some +tests fail because of this. The symptom is error messages like +"cannot open /dev/ubi0_0". + +One possible solution of this problem is to pre-create UBI device and volume +nodes. There is even a script which may be used for this in ubi-utils/scripts/. +But this is not enough because udev will still remove and re-create the nodes +and tests will still fail. So you need to stop removing device nodes using +the following udev rule: + +	KERNEL=="ubi*_*", ACTION=="remove", OPTIONS+="ignore_device" + +In our Ubuntu distribution we put that to new file: +/etc/udev/rules.d/50-local.rules + +Another possibility is to call udevsettle utility in libubi after the volume +has been created See src/libubi.c - the call is compiled in only if +UDEV_SETTLE_HACK is defined. This is anyway an ugly hack, but works, although +makes the tests slower. Suggestions are welcome. + +So, if you have udevsettel unility in your system, you do not have to do +anyting, and the tests should work, because we compile libubi with +UDEV_SETTLE_HACK. Otherwise, you should remove -D UDEV_SETTLE_HACK +from the makefile and pre-create UBI device nodes. diff --git a/tests/ubi-tests/common.c b/tests/ubi-tests/common.c new file mode 100644 index 0000000..cb63e77 --- /dev/null +++ b/tests/ubi-tests/common.c @@ -0,0 +1,336 @@ +/* + * 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 + * + * The stuff which is common for many tests. + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "libubi.h" +#include "common.h" + +/** + * __initial_check - check that common prerequisites which are required to run + * tests. + * + * @test  test name + * @argc  count of command-line arguments + * @argv  command-line arguments + * + * This function returns %0 if all is fine and test may be run and %-1 if not. + */ +int __initial_check(const char *test, int argc, char * const argv[]) +{ +	libubi_t libubi; +	struct ubi_dev_info dev_info; + +	/* +	 * All tests require UBI character device name as the first parameter, +	 * check this. +	 */ +	if (argc < 2) { +		__err_msg(test, __FUNCTION__, __LINE__, +			  "UBI character device node is not specified"); +		return -1; +	} + +	libubi = libubi_open(); +	if (libubi == NULL) { +		__failed(test, __FUNCTION__, __LINE__, "libubi_open"); +		return -1; +	} + +	if (ubi_get_dev_info(libubi, argv[1], &dev_info)) { +		__failed(test, __FUNCTION__, __LINE__, "ubi_get_dev_info"); +		goto close; +	} + +	if (dev_info.avail_ebs < MIN_AVAIL_EBS) { +		__err_msg(test, __FUNCTION__, __LINE__, +			  "insufficient available eraseblocks %d on UBI " +			  "device, required %d", +			  dev_info.avail_ebs, MIN_AVAIL_EBS); +		goto close; +	} + +	if (dev_info.vol_count != 0) { +		__err_msg(test, __FUNCTION__, __LINE__, +			  "device %s is not empty", argv[1]); +		goto close; +	} + +	libubi_close(libubi); +	return 0; + +close: +	libubi_close(libubi); +	return -1; +} + +/** + * __err_msg - print a message to stderr. + * + * @test  test name + * @func  function name + * @line  line number + * @fmt   format string + */ +void __err_msg(const char *test, const char *func, int line, +	       const char *fmt, ...) +{ +	va_list args; + +	fprintf(stderr, "[%s] %s():%d: ", test, func, line); +	va_start(args, fmt); +	vfprintf(stderr, fmt, args); +	fprintf(stderr, "\n"); +	va_end(args); +} + +/** + * __failed - print function fail message. + * + * @test    test name + * @func    calling function name + * @line    line number + * @failed  failed function name + */ +void __failed(const char *test, const char *func, int line, +	      const char *failed) +{ +	fprintf(stderr, "[%s] %s():%d: function %s() failed with error %d (%s)\n", +		test, func, line, failed, errno, strerror(errno)); +} + +/** + * __check_volume - check volume information. + * + * @libubi    libubi descriptor + * @dev_info  UBI device description + * @test      test name + * @func      function name + * @line      line number + * @vol_id    ID of existing volume to check + * @req       volume creation request to compare with + * + * This function checks if a volume created using @req request has exactly the + * requested characteristics. Returns 0 in case of success and %-1 in case of + * error. + */ +int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info, +		   const char *test, const char *func, int line, int vol_id, +		   const struct ubi_mkvol_request *req) +{ +	int ret; +	struct ubi_vol_info vol_info; +	int eb_size; +	long long rsvd_bytes; + +	ret = ubi_get_vol_info1(libubi, dev_info->dev_num, vol_id, &vol_info); +	if (ret) { +		__failed(test, func, line, "ubi_get_vol_info"); +		return -1; +	} + +	if (req->alignment != vol_info.alignment) { +		__err_msg(test, func, line, +			  "bad alignment: requested %d, got %d", +			  req->alignment, vol_info.alignment); +		return -1; +	} +	if (req->vol_type != vol_info.type) { +		__err_msg(test, func, line, "bad type: requested %d, got %d", +			  req->vol_type, vol_info.type); +		return -1; +	} +	if (strlen(req->name) != strlen(&vol_info.name[0]) || +	    strcmp(req->name, &vol_info.name[0]) != 0) { +		__err_msg(test, func, line, +			  "bad name: requested \"%s\", got \"%s\"", +			  req->name, &vol_info.name[0]); +		return -1; +	} +	if (vol_info.corrupted) { +		__err_msg(test, func, line, "corrupted new volume"); +		return -1; +	} + +	eb_size = dev_info->eb_size - (dev_info->eb_size % req->alignment); +	if (eb_size != vol_info.eb_size) { +		__err_msg(test, func, line, +			  "bad usable LEB size %d, should be %d", +			  vol_info.eb_size, eb_size); +		return -1; +	} + +	rsvd_bytes = req->bytes; +	if (rsvd_bytes % eb_size) +		rsvd_bytes += eb_size - (rsvd_bytes % eb_size); + +	if (rsvd_bytes != vol_info.rsvd_bytes) { +		__err_msg(test, func, line, +			  "bad reserved bytes %lld, should be %lld", +			  vol_info.rsvd_bytes, rsvd_bytes); +		return -1; +	} + +	return 0; +} + +/** + * __check_vol_patt - check that volume contains certain data + * + * @libubi    libubi descriptor + * @dev_info  UBI device description + * @test    test name + * @func    function name + * @line    line number + * @node    volume character device node + * @byte    data pattern to check + * + * This function returns %0 if the volume contains only @byte bytes, and %-1 if + * not. + */ +int __check_vol_patt(libubi_t libubi, struct ubi_dev_info *dev_info, +		     const char *test, const char *func, int line, +		     const char *node, uint8_t byte) +{ +	int ret, fd; +	long long bytes = 0; +	struct ubi_vol_info vol_info; +	unsigned char buf[512]; + +	fd = open(node, O_RDONLY); +	if (fd == -1) { +		__failed(test, func, line, "open"); +		__err_msg(test, func, line, "cannot open \"%s\"\n", node); +		return -1; +	} + +	ret = ubi_get_vol_info(libubi, node, &vol_info); +	if (ret) { +		__failed(test, func, line, "ubi_get_vol_info"); +		goto close; +	} + +	while (bytes < vol_info.data_bytes) { +		int i; + +		memset(&buf[0], ~byte, 512); +		ret = read(fd, &buf[0], 512); +		if (ret == -1) { +			__failed(test, func, line, "read"); +			__err_msg(test, func, line, "bytes = %lld, ret = %d", +				  bytes, ret); +			goto close; +		} + +		if (ret == 0 && bytes + ret < vol_info.data_bytes) { +			__err_msg(test, func, line, +				  "EOF, but read only %lld bytes of %lld", +				  bytes + ret, vol_info.data_bytes); +			goto close; +		} + +		for (i = 0; i < ret; i++) +			if (buf[i] != byte) { +				__err_msg(test, func, line, +					  "byte at %lld is not %#x but %#x", +					  bytes + i, byte, (int)buf[i]); +				goto close; +			} + +		bytes += ret; +	} + +	close(fd); +	return 0; + +close: +	close(fd); +	return -1; +} + +/** + * __update_vol_patt - update volume using a certain byte pattern + * + * @libubi    libubi descriptor + * @dev_info  UBI device description + * @test      test name + * @func      function name + * @line      line number + * @node      volume character device node + * @byte      data pattern to check + * + * This function returns %0 in case of success, and %-1 if in case of failure. + */ +int __update_vol_patt(libubi_t libubi, const char *test, const char *func, +		      int line, const char *node, long long bytes, uint8_t byte) +{ +	int ret, fd; +	long long written = 0; +	unsigned char buf[512]; + +	fd = open(node, O_RDWR); +	if (fd == -1) { +		__failed(test, func, line, "open"); +		__err_msg(test, func, line, "cannot open \"%s\"\n", node); +		return -1; +	} + +	if (ubi_update_start(libubi, fd, bytes)) { +		__failed(test, func, line, "ubi_update_start"); +		__err_msg(test, func, line, "bytes = %lld", bytes); +		goto close; +	} + +	memset(&buf[0], byte, 512); + +	while (written != bytes) { +		ret = write(fd, &buf[0], 512); +		if (ret == -1) { +			__failed(test, func, line, "write"); +			__err_msg(test, func, line, "written = %lld, ret = %d", +				  written, ret); +			goto close; +		} +		written += ret; + +		if (written > bytes) { +			__err_msg(test, func, line, "update length %lld bytes, " +				  "but %lld bytes are already written", +				  bytes, written); +			goto close; +		} +	} + +	close(fd); +	return 0; + +close: +	close(fd); +	return -1; +} diff --git a/tests/ubi-tests/common.h b/tests/ubi-tests/common.h new file mode 100644 index 0000000..3e8ada8 --- /dev/null +++ b/tests/ubi-tests/common.h @@ -0,0 +1,103 @@ +/* + * 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 + * + * The stuff which is common for many tests. + */ + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define UBI_VOLUME_PATTERN "/dev/ubi%d_%d" +#define MIN_AVAIL_EBS 5 +#define PAGE_SIZE 4096 + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +#define err_msg(fmt, ...)                                                      \ +	__err_msg(TESTNAME, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) + +#define failed(name)                                                           \ +	__failed(TESTNAME, __FUNCTION__, __LINE__, name) + +#define initial_check(argc, argv)                                              \ +	__initial_check(TESTNAME, argc, argv) + +#define check_volume(vol_id, req)                                              \ +	__check_volume(libubi, &dev_info, TESTNAME, __FUNCTION__,              \ +		       __LINE__, vol_id, req) + +#define check_vol_patt(node, byte)                                             \ +	__check_vol_patt(libubi, &dev_info, TESTNAME, __FUNCTION__, __LINE__,  \ +			 node, byte) + +#define update_vol_patt(node, bytes, byte)                                     \ +	__update_vol_patt(libubi, TESTNAME, __FUNCTION__, __LINE__,            \ +			  node, bytes, byte) + +#define check_failed(ret, error, func, fmt, ...) ({                            \ +	int __ret;                                                             \ +		                                                               \ +	if (!ret) {                                                            \ +		err_msg("%s() returned success but should have failed", func); \ +		err_msg(fmt, ##__VA_ARGS__);                                   \ +		__ret = -1;                                                    \ +	}                                                                      \ +	if (errno != (error)) {                                                \ +		err_msg("%s failed with error %d (%s), expected %d (%s)",      \ +			func, errno, strerror(errno), error, strerror(error)); \ +		err_msg(fmt, ##__VA_ARGS__);                                   \ +		__ret = -1;                                                    \ +	}                                                                      \ +	__ret = 0;                                                             \ +}) + +/* Alignments to test, @s is eraseblock size */ +#define ALIGNMENTS(s)                                                          \ +	{3, 5, 27, 666, 512, 1024, 2048, (s)/2-3, (s)/2-2, (s)/2-1, (s)/2+1,   \ +	 (s)/2+2, (s)/2+3, (s)/3-3, (s)/3-2, (s)/3-1, (s)/3+1, (s)/3+2,        \ +	 (s)/3+3, (s)/4-3, (s)/4-2, (s)/4-1, (s)/4+1, (s)/4+2, (s)/4+3,        \ +	 (s)/5-3, (s)/5-2, (s)/5-1, (s)/5+1, (s)/5+2, (s)/5+3, (s)-17, (s)-9,  \ +	 (s)-8, (s)-6, (s)-4, (s)-1, (s)}; + +extern void __err_msg(const char *test, const char *func, int line, +		      const char *fmt, ...); +void __failed(const char *test, const char *func, int line, +	      const char *failed); +int __initial_check(const char *test, int argc, char * const argv[]); +int __check_volume(libubi_t libubi, struct ubi_dev_info *dev_info, +		   const char *test, const char *func, int line, int vol_id, +		   const struct ubi_mkvol_request *req); +int __check_vol_patt(libubi_t libubi, struct ubi_dev_info *dev_info, +		     const char *test, const char *func, int line, +		     const char *node, uint8_t byte); +int __update_vol_patt(libubi_t libubi, const char *test, const char *func, +		      int line, const char *node, long long bytes, +		      uint8_t byte); + +#ifdef __cplusplus +} +#endif + +#endif /* !__COMMON_H__ */ diff --git a/tests/ubi-tests/integ.c b/tests/ubi-tests/integ.c new file mode 100644 index 0000000..4da7121 --- /dev/null +++ b/tests/ubi-tests/integ.c @@ -0,0 +1,783 @@ +#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" + +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 */ +	off64_t offset; /* Offset within volume */ +	int size; +	int random_seed; +}; + +struct erase_block_info +{ +	struct volume_info *volume; +	int block_number; +	off64_t offset; /* Offset within volume */ +	off64_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 size_t 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); +} + +#if 0 +static void print_write_info(struct write_info *w) +{ +	printf("Offset: %lld  Size:%d  Seed:%u\n", w->offset, w->size, w->random_seed); +	fflush(stdout); +} +#endif + +static void check_erase_block(struct erase_block_info *erase_block, int fd) +{ +	struct write_info *w; +	off64_t gap_end; +	int eb_size = erase_block->volume->info.eb_size; +	ssize_t bytes_read; + +	w = erase_block->writes; +	gap_end = erase_block->offset + eb_size; +	while (w) { +		if (w->offset + w->size < gap_end) { +			/* There is a gap. Check all 0xff */ +			off64_t gap_start = w->offset + w->size; +			size_t size = gap_end - gap_start; +			if (lseek64(fd, gap_start, SEEK_SET) != gap_start) +				error_exit("lseek64 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 (lseek64(fd, w->offset, SEEK_SET) != w->offset) +			error_exit("lseek64 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 */ +		off64_t gap_start = erase_block->offset; +		size_t size = gap_end - gap_start; +		if (lseek64(fd, gap_start, SEEK_SET) != gap_start) +			error_exit("lseek64 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 eb_size = erase_block->volume->info.eb_size; +	int next_offset = 0; +	int space, size; +	off64_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 = eb_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 = eb_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 > eb_size) +		error_exit("internal error"); +	offset = erase_block->offset + next_offset; +	if (offset < erase_block->top_of_data) +		error_exit("internal error!"); +	if (lseek64(fd, offset, SEEK_SET) != offset) +		error_exit("lseek64 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_ebs); +		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.eb_size * max_ebs_per_vol; +		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_ebs; +		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 * (off64_t) s->info.eb_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); +			system(dev_name); +		} 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]; +	size_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.eb_size) +			buf_size = s->info.eb_size; +		if (max_ebs_per_vol && s->info.eb_size * max_ebs_per_vol < s->info.avail_bytes) +			total_space += s->info.eb_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) +{ +	system("rmmod ubi"); +	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 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_ebs; ++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) { +		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; +	} + +	initial_seed = getpid(); +	printf("Initial seed = %u\n", (unsigned) initial_seed); +	next_seed = initial_seed; +	srand(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; +} diff --git a/tests/ubi-tests/io_basic.c b/tests/ubi-tests/io_basic.c new file mode 100644 index 0000000..2e8809a --- /dev/null +++ b/tests/ubi-tests/io_basic.c @@ -0,0 +1,182 @@ +/* + * 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 + * + * Test basic UBI volume I/O capabilities. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "libubi.h" +#define TESTNAME "io_basic" +#include "common.h" + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; + +static int test_basic(int type); +static int test_aligned(int type); + +int main(int argc, char * const argv[]) +{ +	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; +	} + +	if (test_basic(UBI_DYNAMIC_VOLUME)) +		goto close; +	if (test_basic(UBI_STATIC_VOLUME)) +		goto close; +	if (test_aligned(UBI_DYNAMIC_VOLUME)) +		goto close; +	if (test_aligned(UBI_STATIC_VOLUME)) +		goto close; + +	libubi_close(libubi); +	return 0; + +close: +	libubi_close(libubi); +	return 1; +} + +/** + * test_basic - check basic volume read and update capabilities. + * + * @type  volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int test_basic(int type) +{ +	struct ubi_mkvol_request req; +	const char *name = TESTNAME ":test_basic()"; +	char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; + +	req.vol_id = UBI_VOL_NUM_AUTO; +	req.alignment = 1; +	req.bytes = dev_info.avail_bytes; +	req.vol_type = type; +	req.name = name; + +	if (ubi_mkvol(libubi, node, &req)) { +		failed("ubi_mkvol"); +		return -1; +	} + +	sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); + +	/* Make sure newly created volume contains only 0xFF bytes */ +	if (check_vol_patt(&vol_node[0], 0xFF)) +		goto remove; + +	/* Write 0xA5 bytes to the volume */ +	if (update_vol_patt(&vol_node[0], dev_info.avail_bytes, 0xA5)) +		goto remove; +	if (check_vol_patt(&vol_node[0], 0xA5)) +		goto remove; + +	if (ubi_rmvol(libubi, node, req.vol_id)) { +		failed("ubi_rmvol"); +		return -1; +	} + +	return 0; + +remove: +	ubi_rmvol(libubi, node, req.vol_id); +	return -1; +} + +/** + * test_aligned - test volume alignment feature. + * + * @type  volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int test_aligned(int type) +{ +	int i, ebsz; +	struct ubi_mkvol_request req; +	const char *name = TESTNAME ":test_aligned()"; +	char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; +	int alignments[] = ALIGNMENTS(dev_info.eb_size); + +	req.vol_type = type; +	req.name = name; + +	for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { +		req.vol_id = UBI_VOL_NUM_AUTO; + +		req.alignment = alignments[i]; +		req.alignment -= req.alignment % dev_info.min_io_size; +		if (req.alignment == 0) +			req.alignment = dev_info.min_io_size; + +		ebsz = dev_info.eb_size - dev_info.eb_size % req.alignment; +		req.bytes = MIN_AVAIL_EBS * ebsz; + +		if (ubi_mkvol(libubi, node, &req)) { +			failed("ubi_mkvol"); +			return -1; +		} + +		sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); + +		/* Make sure newly created volume contains only 0xFF bytes */ +		if (check_vol_patt(&vol_node[0], 0xFF)) +			goto remove; + +		/* Write 0xA5 bytes to the volume */ +		if (update_vol_patt(&vol_node[0], req.bytes, 0xA5)) +			goto remove; +		if (check_vol_patt(&vol_node[0], 0xA5)) +			goto remove; + +		if (ubi_rmvol(libubi, node, req.vol_id)) { +			failed("ubi_rmvol"); +			return -1; +		} +	} + +	return 0; + +remove: +	ubi_rmvol(libubi, node, req.vol_id); +	return -1; +} diff --git a/tests/ubi-tests/io_paral.c b/tests/ubi-tests/io_paral.c new file mode 100644 index 0000000..488a0b0 --- /dev/null +++ b/tests/ubi-tests/io_paral.c @@ -0,0 +1,251 @@ +/* + * 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 TESTNAME "io_paral" +#include "common.h" + +#define THREADS_NUM 3 +#define ITERATIONS  10 + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; +static int iterations = ITERATIONS; +int total_bytes; + +static void * the_thread(void *ptr); + +static long long memory_limit(void) +{ +	long long result = 0; +	FILE *f; + +	f = fopen("/proc/meminfo", "r"); +	if (!f) +		return 0; +	fscanf(f, "%*s %lld", &result); +	fclose(f); +	return result * 1024 / 4; +} + +int main(int argc, char * const argv[]) +{ +	int i, ret; +	pthread_t threads[THREADS_NUM]; +	struct ubi_mkvol_request req; +	long long mem_limit; + +	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; +	} + +	req.alignment = 1; +	mem_limit = memory_limit(); +	if (mem_limit && mem_limit < dev_info.avail_bytes) +		total_bytes = req.bytes = +				(mem_limit / dev_info.eb_size / THREADS_NUM) +				* dev_info.eb_size; +	else +		total_bytes = req.bytes = +				((dev_info.avail_ebs - 3) / THREADS_NUM) +				* dev_info.eb_size; +	for (i = 0; i < THREADS_NUM; i++) { +		char name[100]; + +		req.vol_id = i; +		sprintf(&name[0], TESTNAME":%d", i); +		req.name = &name[0]; +		req.vol_type = (i & 1) ? UBI_STATIC_VOLUME : UBI_DYNAMIC_VOLUME; + +		if (ubi_mkvol(libubi, node, &req)) { +			failed("ubi_mkvol"); +			goto remove; +		} +	} + +	/* Create one volume with static data to make WL work more */ +	req.vol_id = THREADS_NUM; +	req.name = TESTNAME ":static"; +	req.vol_type = UBI_DYNAMIC_VOLUME; +	req.bytes = 3*dev_info.eb_size; +	if (ubi_mkvol(libubi, node, &req)) { +		failed("ubi_mkvol"); +		goto remove; +	} + +	for (i = 0; i < THREADS_NUM; i++) { +		ret = pthread_create(&threads[i], NULL, &the_thread, (void*)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; +		} +	} + +	libubi_close(libubi); +	return 0; + +remove: +	for (i = 0; i <= THREADS_NUM; i++) +		ubi_rmvol(libubi, node, i); + +close: +	libubi_close(libubi); +	return 1; +} + +/** + * the_thread - the testing thread. + * + * @ptr  thread number + */ +static void * the_thread(void *ptr) +{ +	int fd, iter = iterations, vol_id = (int)ptr; +	unsigned char *wbuf, *rbuf; +	char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; + +	wbuf = malloc(total_bytes); +	rbuf = malloc(total_bytes); +	if (!wbuf || !rbuf) { +		failed("malloc"); +		goto free; +	} + +	sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, vol_id); + +	while (iter--) { +		int i, ret, written = 0, rd = 0; +		int bytes = (random() % (total_bytes - 1)) + 1; + +		fd = open(vol_node, O_RDWR); +		if (fd == -1) { +			failed("open"); +			err_msg("cannot open \"%s\"\n", node); +			goto free; +		} + +		for (i = 0; i < bytes; i++) +			wbuf[i] = random() % 255; +		memset(rbuf, '\0', bytes); + +		do { +			ret = ubi_update_start(libubi, fd, bytes); +			if (ret && errno != EBUSY) { +				failed("ubi_update_start"); +				err_msg("vol_id %d", vol_id); +				goto close; +			} +		} while (ret); + +		while (written < bytes) { +			int to_write = random() % (bytes - written); + +			if (to_write == 0) +				to_write = 1; + +			ret = write(fd, wbuf, to_write); +			if (ret != to_write) { +				failed("write"); +				err_msg("failed to write %d bytes at offset %d " +					"of volume %d", to_write, written, +					vol_id); +				err_msg("update: %d bytes", bytes); +				goto close; +			} + +			written += to_write; +		} + +		close(fd); + +		fd = open(vol_node, O_RDONLY); +		if (fd == -1) { +			failed("open"); +			err_msg("cannot open \"%s\"\n", node); +			goto free; +		} + +		/* read data back and check */ +		while (rd < bytes) { +			int to_read = random() % (bytes - rd); + +			if (to_read == 0) +				to_read = 1; + +			ret = read(fd, rbuf, to_read); +			if (ret != to_read) { +				failed("read"); +				err_msg("failed to read %d bytes at offset %d " +					"of volume %d", to_read, rd, vol_id); +				goto close; +			} + +			rd += to_read; +		} + +		close(fd); + +	} + +	free(wbuf); +	free(rbuf); +	return NULL; + +close: +	close(fd); +free: +	free(wbuf); +	free(rbuf); +	return NULL; +} diff --git a/tests/ubi-tests/io_read.c b/tests/ubi-tests/io_read.c new file mode 100644 index 0000000..c5d1da7 --- /dev/null +++ b/tests/ubi-tests/io_read.c @@ -0,0 +1,398 @@ +/* + * 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 + * + * Test UBI volume read. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "libubi.h" +#define TESTNAME "io_basic" +#include "common.h" + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; + +static int test_static(void); +static int test_read(int type); + +int main(int argc, char * const argv[]) +{ +	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; +	} + +	if (test_static()) +		goto close; +	if (test_read(UBI_DYNAMIC_VOLUME)) +		goto close; +	if (test_read(UBI_STATIC_VOLUME)) +		goto close; + +	libubi_close(libubi); +	return 0; + +close: +	libubi_close(libubi); +	return 1; +} + +/** + * test_static - test static volume-specific features. + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int test_static(void) +{ +	struct ubi_mkvol_request req; +	const char *name = TESTNAME ":io_basic()"; +	char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; +	struct ubi_vol_info vol_info; +	int fd, ret; +	char buf[20]; + +	req.vol_id = UBI_VOL_NUM_AUTO; +	req.alignment = 1; +	req.bytes = dev_info.avail_bytes; +	req.vol_type = UBI_STATIC_VOLUME; +	req.name = name; + +	if (ubi_mkvol(libubi, node, &req)) { +		failed("ubi_mkvol"); +		return -1; +	} + +	sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, req.vol_id); + +	fd = open(vol_node, O_RDWR); +	if (fd == -1) { +		failed("open"); +		err_msg("cannot open \"%s\"\n", node); +		goto remove; +	} + +	if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { +		failed("ubi_get_vol_info"); +		goto close; +	} + +	/* Make sure new static volume contains no data */ +	if (vol_info.data_bytes != 0) { +		err_msg("data_bytes = %lld, not zero", vol_info.data_bytes); +		goto close; +	} + +	/* Ensure read returns EOF */ +	ret = read(fd, &buf[0], 1); +	if (ret < 0) { +		failed("read"); +		goto close; +	} +	if (ret != 0) { +		err_msg("read data from free static volume"); +		goto close; +	} + +	if (ubi_update_start(libubi, fd, 10)) { +		failed("ubi_update_start"); +		goto close; +	} + +	ret = write(fd, &buf[0], 10); +	if (ret < 0) { +		failed("write"); +		goto close; +	} +	if (ret != 10) { +		err_msg("written %d bytes", ret); +		goto close; +	} + +	if (lseek(fd, 0, SEEK_SET) != 0) { +		failed("seek"); +		goto close; +	} +	ret = read(fd, &buf[0], 20); +	if (ret < 0) { +		failed("read"); +		goto close; +	} +	if (ret != 10) { +		err_msg("read %d bytes", ret); +		goto close; +	} + +	close(fd); +	if (ubi_rmvol(libubi, node, req.vol_id)) { +		failed("ubi_rmvol"); +		return -1; +	} + +	return 0; + +close: +	close(fd); +remove: +	ubi_rmvol(libubi, node, req.vol_id); +	return -1; +} + +static int test_read1(struct ubi_vol_info *vol_info); + +/** + * test_read - test UBI volume reading from different offsets. + * + * @type  volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int test_read(int type) +{ +	const char *name = TESTNAME ":test_read()"; +	int alignments[] = ALIGNMENTS(dev_info.eb_size); +	char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; +	struct ubi_mkvol_request req; +	int i; + +	for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { +		int eb_size; +		struct ubi_vol_info vol_info; + +		req.vol_id = UBI_VOL_NUM_AUTO; +		req.vol_type = type; +		req.name = name; + +		req.alignment = alignments[i]; +		req.alignment -= req.alignment % dev_info.min_io_size; +		if (req.alignment == 0) +			req.alignment = dev_info.min_io_size; + +		eb_size = dev_info.eb_size - dev_info.eb_size % req.alignment; +		req.bytes =  MIN_AVAIL_EBS * eb_size; + +		if (ubi_mkvol(libubi, node, &req)) { +			failed("ubi_mkvol"); +			return -1; +		} + +		sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, +			req.vol_id); + +		if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { +			failed("ubi_get_vol_info"); +			goto remove; +		} + +		if (test_read1(&vol_info)) { +			err_msg("alignment = %d", req.alignment); +			goto remove; +		} + +		if (ubi_rmvol(libubi, node, req.vol_id)) { +			failed("ubi_rmvol"); +			return -1; +		} +	} + +	return 0; + +remove: +	ubi_rmvol(libubi, node, req.vol_id); +	return -1; +} + +static int test_read2(const struct ubi_vol_info *vol_info, int len); + +static int fd; + +/* Data lengthes to test, @io - minimal I/O unit size, @s - eraseblock size */ +#define LENGTHES(io, s)                                                        \ +	{1, (io), (io)+1, 2*(io), 3*(io)-1, 3*(io),                            \ +	 PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io),           \ +	 (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s),        \ +	 2*(s)+(io), 3*(s), 3*(s)+(io)}; + +/* + * A helper function for test_read(). + */ +static int test_read1(struct ubi_vol_info *vol_info) +{ +	int i, written = 0; +	char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; +	int lengthes[] = LENGTHES(dev_info.min_io_size, vol_info->eb_size); + +	sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, +		vol_info->vol_id); + +	fd = open(vol_node, O_RDWR); +	if (fd == -1) { +		failed("open"); +		err_msg("cannot open \"%s\"\n", node); +		return -1; +	} + +	/* Write some pattern to the volume */ +	if (ubi_update_start(libubi, fd, vol_info->rsvd_bytes)) { +		failed("ubi_update_start"); +		err_msg("bytes = %lld", vol_info->rsvd_bytes); +		goto close; +	} + +	while (written < vol_info->rsvd_bytes) { +		int i, ret; +		unsigned char buf[512]; + +		for (i = 0; i < 512; i++) +			buf[i] = (unsigned char)(written + i); + +		ret = write(fd, &buf[0], 512); +		if (ret == -1) { +			failed("write"); +			err_msg("written = %d, ret = %d", written, ret); +			goto close; +		} +		written += ret; +	} + +	close(fd); + +	if (ubi_get_vol_info(libubi, vol_node, vol_info)) { +		failed("ubi_get_vol_info"); +		return -1; +	} + +	fd = open(vol_node, O_RDONLY); +	if (fd == -1) { +		failed("open"); +		err_msg("cannot open \"%s\"\n", node); +		return -1; +	} + +	for (i = 0; i < sizeof(lengthes)/sizeof(int); i++) { +		if (test_read2(vol_info, lengthes[i])) { +			err_msg("length = %d", lengthes[i]); +			goto close; +		} +	} + +	close(fd); +	return 0; + +close: +	close(fd); +	return -1; +} + +static int test_read3(const struct ubi_vol_info *vol_info, int len, off_t off); + +/* + * Offsets to test, @io - minimal I/O unit size, @s - eraseblock size, @sz - + * volume size. + */ +#define OFFSETS(io, s, sz)                                                     \ +	{0, (io)-1, (io), (io)+1, 2*(io)-1, 2*(io), 3*(io)-1, 3*(io),          \ +	 PAGE_SIZE-1, PAGE_SIZE-(io), 2*PAGE_SIZE, 2*PAGE_SIZE-(io),           \ +	 (s)/2-1, (s)/2, (s)/2+1, (s)-1, (s), (s)+1, 2*(s)-(io), 2*(s),        \ +	 2*(s)+(io), 3*(s), (sz)-(s)-1, (sz)-(io)-1, (sz)-PAGE_SIZE-1}; + +/* + * A helper function for test_read1(). + */ +static int test_read2(const struct ubi_vol_info *vol_info, int len) +{ +	int i; +	off_t offsets[] = OFFSETS(dev_info.min_io_size, vol_info->eb_size, +				  vol_info->data_bytes); + +	for (i = 0; i < sizeof(offsets)/sizeof(off_t); i++) { +		if (test_read3(vol_info, len, offsets[i])) { +			err_msg("offset = %d", offsets[i]); +			return -1; +		} +	} + +	return 0; +} + +/* + * A helper function for test_read2(). + */ +static int test_read3(const struct ubi_vol_info *vol_info, int len, off_t off) +{ +	int i, len1; +	unsigned char ck_buf[len], buf[len]; +	off_t new_off; + +	if (off + len > vol_info->data_bytes) +		len1 = vol_info->data_bytes - off; +	else +		len1 = len; + +	if (lseek(fd, off, SEEK_SET) != off) { +		failed("seek"); +		err_msg("len = %d", len); +		return -1; +	} +	if (read(fd, &buf[0], len) != len1) { +		failed("read"); +		err_msg("len = %d", len); +		return -1; +	} + +	new_off = lseek(fd, 0, SEEK_CUR); +	if (new_off != off + len1) { +		if (new_off == -1) +			failed("lseek"); +		else +			err_msg("read %d bytes from %lld, but resulting " +				"offset is %lld", len1, (long long) off, (long long) new_off); +		return -1; +	} + +	for (i = 0; i < len1; i++) +		ck_buf[i] = (unsigned char)(off + i); + +	if (memcmp(&buf[0], &ck_buf[0], len1)) { +		err_msg("incorrect data read from offset %lld", +			(long long)off); +		err_msg("len = %d", len); +		return -1; +	} + +	return 0; +} diff --git a/tests/ubi-tests/io_update.c b/tests/ubi-tests/io_update.c new file mode 100644 index 0000000..2e3422a --- /dev/null +++ b/tests/ubi-tests/io_update.c @@ -0,0 +1,370 @@ +/* + * 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 + * + * Test UBI volume update. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "libubi.h" +#define TESTNAME "io_update" +#include "common.h" + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; + +static int test_update(int type); +static int test_update_ff(void); + +int main(int argc, char * const argv[]) +{ +	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; +	} + +	if (test_update(UBI_DYNAMIC_VOLUME)) +		goto close; +	if (test_update(UBI_STATIC_VOLUME)) +		goto close; +	if (test_update_ff()) +		goto close; + +	libubi_close(libubi); +	return 0; + +close: +	libubi_close(libubi); +	return 1; +} + +static int test_update1(struct ubi_vol_info *vol_info); + +/** + * test_update - check volume update capabilities. + * + * @type  volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int test_update(int type) +{ +	struct ubi_mkvol_request req; +	const char *name = TESTNAME ":io_update()"; +	int alignments[] = ALIGNMENTS(dev_info.eb_size); +	struct ubi_vol_info vol_info; +	char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; +	int i; + +	for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { +		int eb_size; + +		req.vol_id = UBI_VOL_NUM_AUTO; +		req.vol_type = type; +		req.name = name; + +		req.alignment = alignments[i]; +		req.alignment -= req.alignment % dev_info.min_io_size; +		if (req.alignment == 0) +			req.alignment = dev_info.min_io_size; + +		eb_size = dev_info.eb_size - dev_info.eb_size % req.alignment; +		req.bytes =  MIN_AVAIL_EBS * eb_size; + +		if (ubi_mkvol(libubi, node, &req)) { +			failed("ubi_mkvol"); +			return -1; +		} + +		sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, +			req.vol_id); +		if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { +			failed("ubi_get_vol_info"); +			goto remove; +		} + +		if (test_update1(&vol_info)) { +			err_msg("alignment = %d", req.alignment); +			goto remove; +		} + +		if (ubi_rmvol(libubi, node, req.vol_id)) { +			failed("ubi_rmvol"); +			return -1; +		} +	} + +	return 0; + +remove: +	ubi_rmvol(libubi, node, req.vol_id); +	return -1; +} + +#define SEQUENCES(io, s) {           \ +	{3*(s)-(io)-1, 1},           \ +	{512},                       \ +	{666},                       \ +	{2048},                      \ +	{(io), (io), PAGE_SIZE},     \ +	{(io)+1, (io)+1, PAGE_SIZE}, \ +	{PAGE_SIZE},                 \ +	{PAGE_SIZE-1},               \ +	{PAGE_SIZE+(io)},            \ +	{(s)},                       \ +	{(s)-1},                     \ +	{(s)+1},                     \ +	{(io), (s)+1},               \ +	{(s)+(io), PAGE_SIZE},       \ +	{2*(s), PAGE_SIZE},          \ +	{PAGE_SIZE, 2*(s), 1},       \ +	{PAGE_SIZE, 2*(s)},          \ +	{2*(s)-1, 2*(s)-1},          \ +	{3*(s), PAGE_SIZE + 1},      \ +	{1, PAGE_SIZE},              \ +	{(io), (s)}                  \ +} +#define SEQ_SZ 21 + +/* + * test_update1 - helper function for test_update(). + */ +static int test_update1(struct ubi_vol_info *vol_info) +{ +	int sequences[SEQ_SZ][3] = SEQUENCES(dev_info.min_io_size, +					     vol_info->eb_size); +	char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; +	unsigned char buf[vol_info->rsvd_bytes]; +	int fd, i, j; + +	sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, +		vol_info->vol_id); + +	for (i = 0; i < vol_info->rsvd_bytes; i++) +		buf[i] = (unsigned char)i; + +	fd = open(vol_node, O_RDWR); +	if (fd == -1) { +		failed("open"); +		err_msg("cannot open \"%s\"\n", node); +		return -1; +	} + +	for (i = 0; i < SEQ_SZ; i++) { +		int ret, stop = 0, len; +		off_t off = 0; +		unsigned char buf1[vol_info->rsvd_bytes]; + +		if (ubi_update_start(libubi, fd, vol_info->rsvd_bytes)) { +			failed("ubi_update_start"); +			goto close; +		} + +		for (j = 0; off < vol_info->rsvd_bytes; j++) { +			if (!stop) { +				if (sequences[i][j] != 0) +					len = sequences[i][j]; +				else +					stop = 1; +			} + +			ret = write(fd, &buf[off], len); +			if (ret < 0) { +				failed("write"); +				err_msg("failed to write %d bytes at offset " +					"%lld", len, (long long) off); +				goto close; +			} +			if (off + len > vol_info->rsvd_bytes) +				len = vol_info->rsvd_bytes - off; +			if (ret != len) { +				err_msg("failed to write %d bytes at offset " +					"%lld, wrote %d", len, (long long) off, ret); +				goto close; +			} +			off += len; +		} + +		/* Check data */ +		if ((ret = lseek(fd, SEEK_SET, 0)) != 0) { +			if (ret < 0) +				failed("lseek"); +			err_msg("cannot seek to 0"); +			goto close; +		} +		memset(&buf1[0], 0x01, vol_info->rsvd_bytes); +		ret = read(fd, &buf1[0], vol_info->rsvd_bytes + 1); +		if (ret < 0) { +			failed("read"); +			err_msg("failed to read %d bytes", +				vol_info->rsvd_bytes + 1); +			goto close; +		} +		if (ret != vol_info->rsvd_bytes) { +			err_msg("failed to read %d bytes, read %d", +				vol_info->rsvd_bytes, ret); +			goto close; +		} +		if (memcmp(&buf[0], &buf1[0], vol_info->rsvd_bytes)) { +			err_msg("data corruption"); +			goto close; +		} +	} + +	close(fd); +	return 0; + +close: +	close(fd); +	return -1; +} + +/** + * test_update_ff - check volume with 0xFF data + * + * This function returns %0 in case of success and %-1 in case of failure. + */ +static int test_update_ff(void) +{ +	struct ubi_mkvol_request req; +	const char *name = TESTNAME ":io_update()"; +	struct ubi_vol_info vol_info; +	char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; +	int i, fd, ret, types[2]; +	int upd_len = MIN_AVAIL_EBS * dev_info.eb_size; +	char buf[upd_len], buf1[upd_len]; + +	for(i = 0; i < MIN_AVAIL_EBS; i++) { +		if (i % 1) +			memset(&buf[0], 0xAB, upd_len); +		else +			memset(&buf[0], 0xFF, upd_len); +	} + +	types[0] = UBI_DYNAMIC_VOLUME; +	types[1] = UBI_STATIC_VOLUME; + +	for (i = 0; i < 2; i++) { +		req.vol_id = UBI_VOL_NUM_AUTO; +		req.vol_type = types[i]; +		req.name = name; + +		req.alignment = 1; +		req.bytes = upd_len; + +		if (ubi_mkvol(libubi, node, &req)) { +			failed("ubi_mkvol"); +			return -1; +		} + +		sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, +			req.vol_id); +		if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { +			failed("ubi_get_vol_info"); +			goto remove; +		} + +		fd = open(vol_node, O_RDWR); +		if (fd == -1) { +			failed("open"); +			err_msg("cannot open \"%s\"\n", node); +			goto remove; +		} + +		if (ubi_update_start(libubi, fd, upd_len)) { +			failed("ubi_update_start"); +			goto close; +		} + + +		ret = write(fd, &buf[0], upd_len); +		if (ret < 0 || ret != upd_len) { +			failed("write"); +			err_msg("failed to write %d bytes", upd_len); +			goto close; +		} + +		/* Check data */ +		if ((ret = lseek(fd, SEEK_SET, 0)) != 0) { +			if (ret < 0) +				failed("lseek"); +			err_msg("cannot seek to 0"); +			goto close; +		} + +		close(fd); + +		fd = open(vol_node, O_RDWR); +		if (fd == -1) { +			failed("open"); +			err_msg("cannot open \"%s\"\n", node); +			goto remove; +		} + +		memset(&buf1[0], 0x00, upd_len); +		ret = read(fd, &buf1[0], upd_len); +		if (ret < 0) { +			failed("read"); +			err_msg("failed to read %d bytes", upd_len); +			goto close; +		} +		if (ret != upd_len) { +			err_msg("failed to read %d bytes, read %d", +				upd_len, ret); +			goto close; +		} +		if (memcmp(&buf[0], &buf1[0], upd_len)) { +			err_msg("data corruption"); +			goto close; +		} + +		close(fd); + +		if (ubi_rmvol(libubi, node, req.vol_id)) { +			failed("ubi_rmvol"); +			return -1; +		} +	} + +	return 0; + +close: +	close(fd); +remove: +	ubi_rmvol(libubi, node, req.vol_id); +	return -1; +} diff --git a/tests/ubi-tests/mkvol_bad.c b/tests/ubi-tests/mkvol_bad.c new file mode 100644 index 0000000..023b06b --- /dev/null +++ b/tests/ubi-tests/mkvol_bad.c @@ -0,0 +1,304 @@ +/* + * 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 + * + * Test UBI volume creation and deletion ioctl()s with bad input and in case of + * incorrect usage. + */ + +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include "libubi.h" +#define TESTNAME "mkvol_bad" +#include "common.h" + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; + +static int test_mkvol(void); +static int test_rmvol(void); + +int main(int argc, char * const argv[]) +{ +	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; +	} + +	if (test_mkvol()) +		goto close; + +	if (test_rmvol()) +		goto close; + +	libubi_close(libubi); +	return 0; + +close: +	libubi_close(libubi); +	return 1; +} + +/** + * test_mkvol - test that UBI mkvol ioctl rejects bad input parameters. + * + * This function returns %0 if the test passed and %-1 if not. + */ +static int test_mkvol(void) +{ +	int ret, i; +	struct ubi_mkvol_request req; +	const char *name = TESTNAME ":test_mkvol()"; + +	req.alignment = 1; +	req.bytes = dev_info.avail_bytes; +	req.vol_type = UBI_DYNAMIC_VOLUME; +	req.name = name; + +	/* Bad volume ID */ +	req.vol_id = -2; +	ret = ubi_mkvol(libubi, node, &req); +	if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id)) +		return -1; + +	req.vol_id = dev_info.max_vol_count; +	ret = ubi_mkvol(libubi, node, &req); +	if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_id = %d", req.vol_id)) +		return -1; + +	/* Bad alignment */ +	req.vol_id = 0; +	req.alignment = 0; +	ret = ubi_mkvol(libubi, node, &req); +	if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", +			 req.alignment)) +		return -1; + +	req.alignment = -1; +	ret = ubi_mkvol(libubi, node, &req); +	if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", +			 req.alignment)) +		return -1; + +	req.alignment = dev_info.eb_size + 1; +	ret = ubi_mkvol(libubi, node, &req); +	if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", +			 req.alignment)) +		return -1; + +	if (dev_info.min_io_size > 1) { +		req.alignment = dev_info.min_io_size + 1; +		ret = ubi_mkvol(libubi, node, &req); +		if (check_failed(ret, EINVAL, "ubi_mkvol", "alignment = %d", +				 req.alignment)) +			return -1; +	} + +	/* Bad bytes */ +	req.alignment = 1; +	req.bytes = -1; +	ret = ubi_mkvol(libubi, node, &req); +	if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes)) +		return -1; + +	req.bytes = 0; +	ret = ubi_mkvol(libubi, node, &req); +	if (check_failed(ret, EINVAL, "ubi_mkvol", "bytes = %lld", req.bytes)) +		return -1; + +	req.bytes = dev_info.avail_bytes + 1; +	ret = ubi_mkvol(libubi, node, &req); +	if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes)) +		return -1; + +	req.alignment = dev_info.eb_size - dev_info.min_io_size; +	req.bytes = (dev_info.eb_size - dev_info.eb_size % req.alignment) * +		    dev_info.avail_ebs + 1; +	ret = ubi_mkvol(libubi, node, &req); +	if (check_failed(ret, ENOSPC, "ubi_mkvol", "bytes = %lld", req.bytes)) +		return -1; + +	/* Bad vol_type */ +	req.alignment = 1; +	req.bytes = dev_info.eb_size; +	req.vol_type = UBI_DYNAMIC_VOLUME + UBI_STATIC_VOLUME; +	ret = ubi_mkvol(libubi, node, &req); +	if (check_failed(ret, EINVAL, "ubi_mkvol", "vol_type = %d", +			 req.vol_type)) +		return -1; + +	req.vol_type = UBI_DYNAMIC_VOLUME; + +	/* Too long name */ +	{ +		char name[UBI_VOL_NAME_MAX + 5]; + +		memset(&name[0], 'x', UBI_VOL_NAME_MAX + 1); +		name[UBI_VOL_NAME_MAX + 1] = '\0'; + +		req.name = &name[0]; +		ret = ubi_mkvol(libubi, node, &req); +		if (check_failed(ret, EINVAL, "ubi_mkvol", "name_len = %d", +				 UBI_VOL_NAME_MAX + 1)) +		return -1; +	} + +	/* Try to create 2 volumes with the same ID and name */ +	req.name = name; +	req.vol_id = 0; +	if (ubi_mkvol(libubi, node, &req)) { +		failed("ubi_mkvol"); +		return -1; +	} + +	ret = ubi_mkvol(libubi, node, &req); +	if (check_failed(ret, EEXIST, "ubi_mkvol", +			 "volume with ID 0 created twice")) +		return -1; + +	req.vol_id = 1; +	ret = ubi_mkvol(libubi, node, &req); +	if (check_failed(ret, EEXIST, "ubi_mkvol", +			 "volume with name \"%s\" created twice", name)) +		return -1; + +	if (ubi_rmvol(libubi, node, 0)) { +		failed("ubi_rmvol"); +		return -1; +	} + +	/* Try to use too much space */ +	req.vol_id = 0; +	req.bytes = dev_info.avail_bytes; +	if (ubi_mkvol(libubi, node, &req)) { +		failed("ubi_mkvol"); +		return -1; +	} + +	req.bytes = 1; +	req.vol_id = 1; +	ret = ubi_mkvol(libubi, node, &req); +	if (check_failed(ret, EEXIST, "ubi_mkvol", +			 "created volume of maximum size %lld, but still " +			 "can create more volumes", dev_info.avail_bytes)) +		return -1; + +	if (ubi_rmvol(libubi, node, 0)) { +		failed("ubi_rmvol"); +		return -1; +	} + +	/* Try to create too many volumes */ +	for (i = 0; i < dev_info.max_vol_count; i++) { +		char nm[strlen(name) + 50]; + +		req.vol_id = UBI_VOL_NUM_AUTO; +		req.alignment = 1; +		req.bytes = 1; +		req.vol_type = UBI_STATIC_VOLUME; + +		sprintf(&nm[0], "%s:%d", name, i); +		req.name = &nm[0]; + +		if (ubi_mkvol(libubi, node, &req)) { +			/* +			 * Note, because of gluebi we may be unable to create +			 * dev_info.max_vol_count devices (MTD restrictions). +			 */ +			if (errno == ENFILE) +				break; +			failed("ubi_mkvol"); +			err_msg("vol_id %d", i); +			goto remove; +		} +	} + +	for (i = 0; i < dev_info.max_vol_count + 1; i++) +		ubi_rmvol(libubi, node, i); + +	return 0; + +remove: +	for (i = 0; i < dev_info.max_vol_count + 1; i++) +		ubi_rmvol(libubi, node, i); +	return -1; +} + +/** + * test_rmvol - test that UBI rmvol ioctl rejects bad input parameters. + * + * This function returns %0 if the test passed and %-1 if not. + */ +static int test_rmvol(void) +{ +	int ret; +	struct ubi_mkvol_request req; +	const char *name = TESTNAME ":test_rmvol()"; + +	/* Bad vol_id */ +	ret = ubi_rmvol(libubi, node, -1); +	if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = -1")) +		return -1; + +	ret = ubi_rmvol(libubi, node, dev_info.max_vol_count); +	if (check_failed(ret, EINVAL, "ubi_rmvol", "vol_id = %d", +			 dev_info.max_vol_count)) +		return -1; + +	/* Try to remove non-existing volume */ +	ret = ubi_rmvol(libubi, node, 0); +	if (check_failed(ret, ENODEV, "ubi_rmvol", +			 "removed non-existing volume 0")) +		return -1; + +	/* Try to remove volume twice */ +	req.vol_id = UBI_VOL_NUM_AUTO; +	req.alignment = 1; +	req.bytes = dev_info.avail_bytes; +	req.vol_type = UBI_DYNAMIC_VOLUME; +	req.name = name; +	if (ubi_mkvol(libubi, node, &req)) { +		failed("ubi_mkvol"); +		return -1; +	} + +	if (ubi_rmvol(libubi, node, req.vol_id)) { +		failed("ubi_rmvol"); +		return -1; +	} + +	ret = ubi_rmvol(libubi, node, req.vol_id); +	if (check_failed(ret, ENODEV, "ubi_rmvol", "volume %d removed twice", +			 req.vol_id)) +		return -1; + +	return 0; +} diff --git a/tests/ubi-tests/mkvol_basic.c b/tests/ubi-tests/mkvol_basic.c new file mode 100644 index 0000000..e2120e9 --- /dev/null +++ b/tests/ubi-tests/mkvol_basic.c @@ -0,0 +1,253 @@ +/* + * 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 + * + * Test test checks basic volume creation and deletion capabilities. + */ + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include "libubi.h" +#define TESTNAME "mkvol_basic" +#include "common.h" + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; + +static int mkvol_basic(void); +static int mkvol_alignment(void); +static int mkvol_multiple(void); + +int main(int argc, char * const argv[]) +{ +	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; +	} + +	if (mkvol_basic()) +		goto close; + +	if (mkvol_alignment()) +		goto close; + +	if (mkvol_multiple()) +		goto close; + +	libubi_close(libubi); +	return 0; + +close: +	libubi_close(libubi); +	return 1; +} + +/** + * mkvol_alignment - create volumes with different alignments. + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int mkvol_alignment(void) +{ +	struct ubi_mkvol_request req; +	int i, vol_id, ebsz; +	const char *name = TESTNAME ":mkvol_alignment()"; +	int alignments[] = ALIGNMENTS(dev_info.eb_size); + +	for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { +		req.vol_id = UBI_VOL_NUM_AUTO; + +		/* Alignment should actually be multiple of min. I/O size */ +		req.alignment = alignments[i]; +		req.alignment -= req.alignment % dev_info.min_io_size; +		if (req.alignment == 0) +			req.alignment = dev_info.min_io_size; + +		/* Bear in mind alignment reduces EB size */ +		ebsz = dev_info.eb_size - dev_info.eb_size % req.alignment; +		req.bytes = dev_info.avail_ebs * ebsz; + +		req.vol_type = UBI_DYNAMIC_VOLUME; +		req.name = name; + +		if (ubi_mkvol(libubi, node, &req)) { +			failed("ubi_mkvol"); +			err_msg("alignment %d", req.alignment); +			return -1; +		} + +		vol_id = req.vol_id; +		if (check_volume(vol_id, &req)) +			goto remove; + +		if (ubi_rmvol(libubi, node, vol_id)) { +			failed("ubi_rmvol"); +			return -1; +		} +	} + +	return 0; + +remove: +	ubi_rmvol(libubi, node, vol_id); +	return -1; +} + +/** + * mkvol_basic - simple test that checks basic volume creation capability. + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int mkvol_basic(void) +{ +	struct ubi_mkvol_request req; +	struct ubi_vol_info vol_info; +	int vol_id, ret; +	const char *name = TESTNAME ":mkvol_basic()"; + +	/* Create dynamic volume of maximum size */ +	req.vol_id = UBI_VOL_NUM_AUTO; +	req.alignment = 1; +	req.bytes = dev_info.avail_bytes; +	req.vol_type = UBI_DYNAMIC_VOLUME; +	req.name = name; + +	if (ubi_mkvol(libubi, node, &req)) { +		failed("ubi_mkvol"); +		return -1; +	} + +	vol_id = req.vol_id; +	if (check_volume(vol_id, &req)) +		goto remove; + +	if (ubi_rmvol(libubi, node, vol_id)) { +		failed("ubi_rmvol"); +		return -1; +	} + +	/* Create static volume of maximum size */ +	req.vol_id = UBI_VOL_NUM_AUTO; +	req.alignment = 1; +	req.bytes = dev_info.avail_bytes; +	req.vol_type = UBI_STATIC_VOLUME; +	req.name = name; + +	if (ubi_mkvol(libubi, node, &req)) { +		failed("ubi_mkvol"); +		return -1; +	} + +	vol_id = req.vol_id; +	if (check_volume(vol_id, &req)) +		goto remove; + +	if (ubi_rmvol(libubi, node, vol_id)) { +		failed("ubi_rmvol"); +		return -1; +	} + +	/* Make sure volume does not exist */ +	ret = ubi_get_vol_info1(libubi, dev_info.dev_num, vol_id, &vol_info); +	if (ret == 0) { +		err_msg("removed volume %d exists", vol_id); +		goto remove; +	} + +	return 0; + +remove: +	ubi_rmvol(libubi, node, vol_id); +	return -1; +} + +/** + * mkvol_multiple - test multiple volumes creation + * + * Thus function returns %0 if the test passed and %-1 if not. + */ +static int mkvol_multiple(void) +{ +	struct ubi_mkvol_request req; +	int i, ret, max = dev_info.max_vol_count; +	const char *name = TESTNAME ":mkvol_multiple()"; + +	/* Create maximum number of volumes */ +	for (i = 0; i < max; i++) { +		char nm[strlen(name) + 50]; + +		req.vol_id = UBI_VOL_NUM_AUTO; +		req.alignment = 1; +		req.bytes = 1; +		req.vol_type = UBI_STATIC_VOLUME; + +		sprintf(&nm[0], "%s:%d", name, i); +		req.name = &nm[0]; + +		if (ubi_mkvol(libubi, node, &req)) { +			if (errno == ENFILE) { +				max = i; +				break; +			} +			failed("ubi_mkvol"); +			err_msg("vol_id %d", i); +			goto remove; +		} + +		if (check_volume(req.vol_id, &req)) { +			err_msg("vol_id %d", i); +			goto remove; +		} +	} + +	for (i = 0; i < max; i++) { +		struct ubi_vol_info vol_info; + +		if (ubi_rmvol(libubi, node, i)) { +			failed("ubi_rmvol"); +			return -1; +		} + +		/* Make sure volume does not exist */ +		ret = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); +		if (ret == 0) { +			err_msg("removed volume %d exists", i); +			goto remove; +		} +	} + +	return 0; + +remove: +	for (i = 0; i < dev_info.max_vol_count + 1; i++) +		ubi_rmvol(libubi, node, i); +	return -1; +} diff --git a/tests/ubi-tests/mkvol_paral.c b/tests/ubi-tests/mkvol_paral.c new file mode 100644 index 0000000..faf085c --- /dev/null +++ b/tests/ubi-tests/mkvol_paral.c @@ -0,0 +1,112 @@ +/* + * 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 creates and deletes volumes in parallel. + */ + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <pthread.h> +#include "libubi.h" +#define TESTNAME "mkvol_paral" +#include "common.h" + +#define THREADS_NUM 4 +#define ITERATIONS  500 + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; +static int iterations = ITERATIONS; + +static void * the_thread(void *ptr); + +int main(int argc, char * const argv[]) +{ +	int i, 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; +	} + +	for (i = 0; i < THREADS_NUM; i++) { +		ret = pthread_create(&threads[i], NULL, &the_thread, (void*)i); +		if (ret) { +			failed("pthread_create"); +			goto close; +		} +	} + +	for (i = 0; i < THREADS_NUM; i++) +		pthread_join(threads[i], NULL); + +	libubi_close(libubi); +	return 0; + +close: +	libubi_close(libubi); +	return 1; +} + +/** + * the_thread - the testing thread. + * + * @ptr  thread number + */ +static void * the_thread(void *ptr) +{ +	int n = (int)ptr, iter = iterations; +	struct ubi_mkvol_request req; +	const char *name =  TESTNAME ":the_thread()"; +	char nm[strlen(name) + 50]; + +	req.alignment = 1; +	req.bytes = dev_info.avail_bytes/ITERATIONS; +	req.vol_type = UBI_DYNAMIC_VOLUME; +	sprintf(&nm[0], "%s:%d", name, n); +	req.name = &nm[0]; + +	while (iter--) { +		req.vol_id = UBI_VOL_NUM_AUTO; +		if (ubi_mkvol(libubi, node, &req)) { +			failed("ubi_mkvol"); +			return NULL; +		} +		if (ubi_rmvol(libubi, node, req.vol_id)) { +			failed("ubi_rmvol"); +			return NULL; +		} +	} + +	return NULL; +} diff --git a/tests/ubi-tests/rmvol.c b/tests/ubi-tests/rmvol.c new file mode 100644 index 0000000..fb9b344 --- /dev/null +++ b/tests/ubi-tests/rmvol.c @@ -0,0 +1,308 @@ +/* + * Copyright (c) Nokia Corporation, 2007 + * + * 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 + * + * Test volume reference counting - create a volume, open a sysfs file + * belonging to the volume, delete the volume but do not close the file, make + * sure the file cannot be read, make sure the volume cannot be open, close the + * file, make sure the volume disappeard, make sure its sysfs subtree + * disappeared. + */ + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include "libubi.h" +#define TESTNAME "rmvol" +#include "common.h" + +#define SYSFS_FILE "/sys/class/ubi/ubi%d_%d/usable_eb_size" + +int main(int argc, char * const argv[]) +{ +	int ret, fd; +	char fname[sizeof(SYSFS_FILE) + 20]; +	const char *node; +	libubi_t libubi; +	struct ubi_dev_info dev_info; +	struct ubi_mkvol_request req; +	char tmp[100]; + +	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 out_libubi; +	} + +	/* Create a small dynamic volume */ +	req.vol_id = UBI_VOL_NUM_AUTO; +	req.alignment = dev_info.min_io_size; +	req.bytes = dev_info.eb_size; +	req.vol_type = UBI_DYNAMIC_VOLUME; +	req.name = "rmvol"; + +	if (ubi_mkvol(libubi, node, &req)) { +		failed("ubi_mkvol"); +		perror("ubi_mkvol"); +		goto out_libubi; +	} + +	/* Open volume-related sysfs file */ +	sprintf(fname, SYSFS_FILE, dev_info.dev_num, req.vol_id); +	fd = open(fname, O_RDONLY); +	if (fd == -1) { +		failed("open"); +		perror("open"); +		goto out_rmvol; +	} + +	/* Remove the volume, but do not close the file */ +	if (ubi_rmvol(libubi, node, req.vol_id)) { +		failed("ubi_rmvol"); +		perror("ubi_rmvol"); +		goto out_close; +	} + +	/* Try to read from the file, this should fail */ +	ret = read(fd, tmp, 100); +	if (ret != -1) { +		failed("read"); +		err_msg("read returned %d, expected -1", ret); +		goto out_close; +	} + +	/* Close the file and try to open it again, should fail */ +	close(fd); +	fd = open(fname, O_RDONLY); +	if (fd != -1) { +		failed("open"); +		err_msg("opened %s again, open returned %d, expected -1", +			fname, fd); +		goto out_libubi; +	} + +	libubi_close(libubi); +	return 0; + +out_rmvol: +	ubi_rmvol(libubi, node, req.vol_id); +out_libubi: +	libubi_close(libubi); +	return 1; + +out_close: +	close(fd); +	libubi_close(libubi); +	return 1; +} + +#if 0 +/** + * mkvol_alignment - create volumes with different alignments. + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int mkvol_alignment(void) +{ +	struct ubi_mkvol_request req; +	int i, vol_id, ebsz; +	const char *name = TESTNAME ":mkvol_alignment()"; +	int alignments[] = ALIGNMENTS(dev_info.eb_size); + +	for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { +		req.vol_id = UBI_VOL_NUM_AUTO; + +		/* Alignment should actually be multiple of min. I/O size */ +		req.alignment = alignments[i]; +		req.alignment -= req.alignment % dev_info.min_io_size; +		if (req.alignment == 0) +			req.alignment = dev_info.min_io_size; + +		/* Bear in mind alignment reduces EB size */ +		ebsz = dev_info.eb_size - dev_info.eb_size % req.alignment; +		req.bytes = dev_info.avail_ebs * ebsz; + +		req.vol_type = UBI_DYNAMIC_VOLUME; +		req.name = name; + +		if (ubi_mkvol(libubi, node, &req)) { +			failed("ubi_mkvol"); +			err_msg("alignment %d", req.alignment); +			return -1; +		} + +		vol_id = req.vol_id; +		if (check_volume(vol_id, &req)) +			goto remove; + +		if (ubi_rmvol(libubi, node, vol_id)) { +			failed("ubi_rmvol"); +			return -1; +		} +	} + +	return 0; + +remove: +	ubi_rmvol(libubi, node, vol_id); +	return -1; +} + +/** + * mkvol_basic - simple test that checks basic volume creation capability. + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int mkvol_basic(void) +{ +	struct ubi_mkvol_request req; +	struct ubi_vol_info vol_info; +	int vol_id, ret; +	const char *name = TESTNAME ":mkvol_basic()"; + +	/* Create dynamic volume of maximum size */ +	req.vol_id = UBI_VOL_NUM_AUTO; +	req.alignment = 1; +	req.bytes = dev_info.avail_bytes; +	req.vol_type = UBI_DYNAMIC_VOLUME; +	req.name = name; + +	if (ubi_mkvol(libubi, node, &req)) { +		failed("ubi_mkvol"); +		return -1; +	} + +	vol_id = req.vol_id; +	if (check_volume(vol_id, &req)) +		goto remove; + +	if (ubi_rmvol(libubi, node, vol_id)) { +		failed("ubi_rmvol"); +		return -1; +	} + +	/* Create static volume of maximum size */ +	req.vol_id = UBI_VOL_NUM_AUTO; +	req.alignment = 1; +	req.bytes = dev_info.avail_bytes; +	req.vol_type = UBI_STATIC_VOLUME; +	req.name = name; + +	if (ubi_mkvol(libubi, node, &req)) { +		failed("ubi_mkvol"); +		return -1; +	} + +	vol_id = req.vol_id; +	if (check_volume(vol_id, &req)) +		goto remove; + +	if (ubi_rmvol(libubi, node, vol_id)) { +		failed("ubi_rmvol"); +		return -1; +	} + +	/* Make sure volume does not exist */ +	ret = ubi_get_vol_info1(libubi, dev_info.dev_num, vol_id, &vol_info); +	if (ret == 0) { +		err_msg("removed volume %d exists", vol_id); +		goto remove; +	} + +	return 0; + +remove: +	ubi_rmvol(libubi, node, vol_id); +	return -1; +} + +/** + * mkvol_multiple - test multiple volumes creation + * + * Thus function returns %0 if the test passed and %-1 if not. + */ +static int mkvol_multiple(void) +{ +	struct ubi_mkvol_request req; +	int i, ret, max = dev_info.max_vol_count; +	const char *name = TESTNAME ":mkvol_multiple()"; + +	/* Create maximum number of volumes */ +	for (i = 0; i < max; i++) { +		char nm[strlen(name) + 50]; + +		req.vol_id = UBI_VOL_NUM_AUTO; +		req.alignment = 1; +		req.bytes = 1; +		req.vol_type = UBI_STATIC_VOLUME; + +		sprintf(&nm[0], "%s:%d", name, i); +		req.name = &nm[0]; + +		if (ubi_mkvol(libubi, node, &req)) { +			if (errno == ENFILE) { +				max = i; +				break; +			} +			failed("ubi_mkvol"); +			err_msg("vol_id %d", i); +			goto remove; +		} + +		if (check_volume(req.vol_id, &req)) { +			err_msg("vol_id %d", i); +			goto remove; +		} +	} + +	for (i = 0; i < max; i++) { +		struct ubi_vol_info vol_info; + +		if (ubi_rmvol(libubi, node, i)) { +			failed("ubi_rmvol"); +			return -1; +		} + +		/* Make sure volume does not exist */ +		ret = ubi_get_vol_info1(libubi, dev_info.dev_num, i, &vol_info); +		if (ret == 0) { +			err_msg("removed volume %d exists", i); +			goto remove; +		} +	} + +	return 0; + +remove: +	for (i = 0; i < dev_info.max_vol_count + 1; i++) +		ubi_rmvol(libubi, node, i); +	return -1; +} +#endif diff --git a/tests/ubi-tests/rsvol.c b/tests/ubi-tests/rsvol.c new file mode 100644 index 0000000..7a9e5ea --- /dev/null +++ b/tests/ubi-tests/rsvol.c @@ -0,0 +1,310 @@ +/* + * 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 + * + * Tes UBI volume re-size. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "libubi.h" +#define TESTNAME "rsvol" +#include "common.h" + +static libubi_t libubi; +static struct ubi_dev_info dev_info; +const char *node; + +static int test_basic(int type); +static int test_rsvol(int type); + +int main(int argc, char * const argv[]) +{ +	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; +	} + +	if (test_basic(UBI_DYNAMIC_VOLUME)) +		goto close; +	if (test_basic(UBI_STATIC_VOLUME)) +		goto close; +	if (test_rsvol(UBI_DYNAMIC_VOLUME)) +		goto close; +	if (test_rsvol(UBI_STATIC_VOLUME)) +		goto close; + +	libubi_close(libubi); +	return 0; + +close: +	libubi_close(libubi); +	return 1; +} + +/** + * test_basic - check volume re-size capability. + * + * @type  volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int test_basic(int type) +{ +	struct ubi_mkvol_request req; +	const char *name = TESTNAME ":test_basic()"; + +	req.vol_id = UBI_VOL_NUM_AUTO; +	req.alignment = 1; +	req.bytes = MIN_AVAIL_EBS * dev_info.eb_size; +	req.vol_type = type; +	req.name = name; + +	if (ubi_mkvol(libubi, node, &req)) { +		failed("ubi_mkvol"); +		return -1; +	} + +	req.bytes = dev_info.eb_size; +	if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) { +		failed("ubi_rsvol"); +		goto remove; +	} + +	if (check_volume(req.vol_id, &req)) +		goto remove; + +	req.bytes = (MIN_AVAIL_EBS + 1) * dev_info.eb_size; +	if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) { +		failed("ubi_rsvol"); +		goto remove; +	} + +	if (check_volume(req.vol_id, &req)) +		goto remove; + +	req.bytes -= 1; +	if (ubi_rsvol(libubi, node, req.vol_id, req.bytes)) { +		failed("ubi_rsvol"); +		goto remove; +	} + +	if (check_volume(req.vol_id, &req)) +		goto remove; + +	if (ubi_rmvol(libubi, node, req.vol_id)) { +		failed("ubi_rmvol"); +		return -1; +	} + +	return 0; + +remove: +	ubi_rmvol(libubi, node, req.vol_id); +	return -1; +} + +static int test_rsvol1(struct ubi_vol_info *vol_info); + +/** + * test_rsvol - test UBI volume re-size. + * + * @type  volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * + * Thus function returns %0 in case of success and %-1 in case of failure. + */ +static int test_rsvol(int type) +{ +	const char *name = TESTNAME "test_rsvol:()"; +	int alignments[] = ALIGNMENTS(dev_info.eb_size); +	char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; +	struct ubi_mkvol_request req; +	int i; + +	for (i = 0; i < sizeof(alignments)/sizeof(int); i++) { +		int eb_size; +		struct ubi_vol_info vol_info; + +		req.vol_id = UBI_VOL_NUM_AUTO; +		req.vol_type = type; +		req.name = name; + +		req.alignment = alignments[i]; +		req.alignment -= req.alignment % dev_info.min_io_size; +		if (req.alignment == 0) +			req.alignment = dev_info.min_io_size; + +		eb_size = dev_info.eb_size - dev_info.eb_size % req.alignment; +		req.bytes =  MIN_AVAIL_EBS * eb_size; + +		if (ubi_mkvol(libubi, node, &req)) { +			failed("ubi_mkvol"); +			return -1; +		} + +		sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, +			req.vol_id); + +		if (ubi_get_vol_info(libubi, vol_node, &vol_info)) { +			failed("ubi_get_vol_info"); +			goto remove; +		} + +		if (test_rsvol1(&vol_info)) { +			err_msg("alignment = %d", req.alignment); +			goto remove; +		} + +		if (ubi_rmvol(libubi, node, req.vol_id)) { +			failed("ubi_rmvol"); +			return -1; +		} +	} + +	return 0; + +remove: +	ubi_rmvol(libubi, node, req.vol_id); +	return -1; +} + +/* + * Helper function for test_rsvol(). + */ +static int test_rsvol1(struct ubi_vol_info *vol_info) +{ +	long long bytes; +	struct ubi_vol_info vol_info1; +	char vol_node[strlen(UBI_VOLUME_PATTERN) + 100]; +	unsigned char buf[vol_info->rsvd_bytes]; +	int fd, i, ret; + +	/* Make the volume smaller and check basic volume I/O */ +	bytes = vol_info->rsvd_bytes - vol_info->eb_size; +	if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes - 1)) { +		failed("ubi_rsvol"); +		return -1; +	} + +	if (ubi_get_vol_info1(libubi, vol_info->dev_num, vol_info->vol_id, +			     &vol_info1)) { +		failed("ubi_get_vol_info"); +		return -1; +	} + +	if (vol_info1.rsvd_bytes != bytes) { +		err_msg("rsvd_bytes %lld, must be %lld", +			vol_info1.rsvd_bytes, bytes); +		return -1; +	} + +	if (vol_info1.rsvd_ebs != vol_info->rsvd_ebs - 1) { +		err_msg("rsvd_ebs %d, must be %d", +			vol_info1.rsvd_ebs, vol_info->rsvd_ebs - 1); +		return -1; +	} + +	/* Write data to the volume */ +	sprintf(&vol_node[0], UBI_VOLUME_PATTERN, dev_info.dev_num, +			vol_info->vol_id); + +	fd = open(vol_node, O_RDWR); +	if (fd == -1) { +		failed("open"); +		err_msg("cannot open \"%s\"\n", vol_node); +		return -1; +	} + +	bytes = vol_info->rsvd_bytes - vol_info->eb_size - 1; +	if (ubi_update_start(libubi, fd, bytes)) { +		failed("ubi_update_start"); +		goto close; +	} + +	for (i = 0; i < bytes; i++) +		buf[i] = (unsigned char)i; + +	ret = write(fd, &buf[0], bytes); +	if (ret != bytes) { +		failed("write"); +		goto close; +	} + +	close(fd); + +	if (ubi_rsvol(libubi, node, vol_info->vol_id, bytes)) { +		failed("ubi_rsvol"); +		return -1; +	} + +	if (ubi_rsvol(libubi, node, vol_info->vol_id, +		      vol_info->eb_size * dev_info.avail_ebs)) { +		failed("ubi_rsvol"); +		return -1; +	} + +	fd = open(vol_node, O_RDWR); +	if (fd == -1) { +		failed("open"); +		err_msg("cannot open \"%s\"\n", vol_node); +		return -1; +	} + +	/* Read data back */ +	if (lseek(fd, 0, SEEK_SET) != 0) { +		failed("seek"); +		goto close; +	} +	memset(&buf[0], 0, bytes); +	ret = read(fd, &buf[0], bytes); +	if (ret != bytes) { +		failed("read"); +		goto close; +	} + +	for (i = 0; i < bytes; i++) { +		if (buf[i] != (unsigned char)i) { +			err_msg("bad data"); +			goto close; +		} +	} + +	close(fd); +	return 0; + +close: +	close(fd); +	return -1; +} diff --git a/tests/ubi-tests/runtests.sh b/tests/ubi-tests/runtests.sh new file mode 100755 index 0000000..7072e03 --- /dev/null +++ b/tests/ubi-tests/runtests.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +ubidev="$1" +tests="mkvol_basic mkvol_bad mkvol_paral rsvol io_basic io_read io_update +io_paral rmvol" + +if test -z "$ubidev"; +then +	echo "Usage:" +	echo "$0 <UBI device> <ubi module load command>" +	exit 1 +fi + +ubiname=`echo $ubidev | cut -d/ -f3` + +major=`cat /sys/class/ubi/$ubiname/dev | cut -d: -f1` + +for minor in `seq 0 4`; do +	if test ! -e ${ubidev}_${minor} ; +	then +		mknod ${ubidev}_${minor} c $major $(($minor + 1)) +	fi +done + +if ! test -c "$ubidev"; +then +	echo "Error: $ubidev is not character device" +	exit 1 +fi + +for t in `echo $tests`; +do +	echo "Running $t $ubidev" +	"./$t" "$ubidev" || exit 1 +done + +echo SUCCESS + +exit 0  | 
