Home | History | Annotate | Download | only in engines
      1 /*
      2  * MTD engine
      3  *
      4  * IO engine that reads/writes from MTD character devices.
      5  *
      6  */
      7 #include <assert.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <unistd.h>
     11 #include <errno.h>
     12 #include <sys/ioctl.h>
     13 #include <mtd/mtd-user.h>
     14 
     15 #include "../fio.h"
     16 #include "../verify.h"
     17 #include "../oslib/libmtd.h"
     18 
     19 static libmtd_t desc;
     20 
     21 struct fio_mtd_data {
     22 	struct mtd_dev_info info;
     23 };
     24 
     25 static int fio_mtd_maybe_mark_bad(struct thread_data *td,
     26 				  struct fio_mtd_data *fmd,
     27 				  struct io_u *io_u, int eb)
     28 {
     29 	int ret;
     30 	if (errno == EIO) {
     31 		ret = mtd_mark_bad(&fmd->info, io_u->file->fd, eb);
     32 		if (ret != 0) {
     33 			io_u->error = errno;
     34 			td_verror(td, errno, "mtd_mark_bad");
     35 			return -1;
     36 		}
     37 	}
     38 	return 0;
     39 }
     40 
     41 static int fio_mtd_is_bad(struct thread_data *td,
     42 			  struct fio_mtd_data *fmd,
     43 			  struct io_u *io_u, int eb)
     44 {
     45 	int ret = mtd_is_bad(&fmd->info, io_u->file->fd, eb);
     46 	if (ret == -1) {
     47 		io_u->error = errno;
     48 		td_verror(td, errno, "mtd_is_bad");
     49 	} else if (ret == 1)
     50 		io_u->error = EIO;	/* Silent failure--don't flood stderr */
     51 	return ret;
     52 }
     53 
     54 static int fio_mtd_queue(struct thread_data *td, struct io_u *io_u)
     55 {
     56 	struct fio_file *f = io_u->file;
     57 	struct fio_mtd_data *fmd = FILE_ENG_DATA(f);
     58 	int local_offs = 0;
     59 	int ret;
     60 
     61 	fio_ro_check(td, io_u);
     62 
     63 	/*
     64 	 * Errors tend to pertain to particular erase blocks, so divide up
     65 	 * I/O to erase block size.
     66 	 * If an error is encountered, log it and keep going onto the next
     67 	 * block because the error probably just pertains to that block.
     68 	 * TODO(dehrenberg): Divide up reads and writes into page-sized
     69 	 * operations to get more fine-grained information about errors.
     70 	 */
     71 	while (local_offs < io_u->buflen) {
     72 		int eb = (io_u->offset + local_offs) / fmd->info.eb_size;
     73 		int eb_offs = (io_u->offset + local_offs) % fmd->info.eb_size;
     74 		/* The length is the smaller of the length remaining in the
     75 		 * buffer and the distance to the end of the erase block */
     76 		int len = min((int)io_u->buflen - local_offs,
     77 			      (int)fmd->info.eb_size - eb_offs);
     78 		char *buf = ((char *)io_u->buf) + local_offs;
     79 
     80 		if (td->o.skip_bad) {
     81 			ret = fio_mtd_is_bad(td, fmd, io_u, eb);
     82 			if (ret == -1)
     83 				break;
     84 			else if (ret == 1)
     85 				goto next;
     86 		}
     87 		if (io_u->ddir == DDIR_READ) {
     88 			ret = mtd_read(&fmd->info, f->fd, eb, eb_offs, buf, len);
     89 			if (ret != 0) {
     90 				io_u->error = errno;
     91 				td_verror(td, errno, "mtd_read");
     92 				if (fio_mtd_maybe_mark_bad(td, fmd, io_u, eb))
     93 					break;
     94 			}
     95 		} else if (io_u->ddir == DDIR_WRITE) {
     96 			ret = mtd_write(desc, &fmd->info, f->fd, eb,
     97 					    eb_offs, buf, len, NULL, 0, 0);
     98 			if (ret != 0) {
     99 				io_u->error = errno;
    100 				td_verror(td, errno, "mtd_write");
    101 				if (fio_mtd_maybe_mark_bad(td, fmd, io_u, eb))
    102 					break;
    103 			}
    104 		} else if (io_u->ddir == DDIR_TRIM) {
    105 			if (eb_offs != 0 || len != fmd->info.eb_size) {
    106 				io_u->error = EINVAL;
    107 				td_verror(td, EINVAL,
    108 					  "trim on MTD must be erase block-aligned");
    109 			}
    110 			ret = mtd_erase(desc, &fmd->info, f->fd, eb);
    111 			if (ret != 0) {
    112 				io_u->error = errno;
    113 				td_verror(td, errno, "mtd_erase");
    114 				if (fio_mtd_maybe_mark_bad(td, fmd, io_u, eb))
    115 					break;
    116 			}
    117 		} else {
    118 			io_u->error = ENOTSUP;
    119 			td_verror(td, io_u->error, "operation not supported on mtd");
    120 		}
    121 
    122 next:
    123 		local_offs += len;
    124 	}
    125 
    126 	return FIO_Q_COMPLETED;
    127 }
    128 
    129 static int fio_mtd_open_file(struct thread_data *td, struct fio_file *f)
    130 {
    131 	struct fio_mtd_data *fmd;
    132 	int ret;
    133 
    134 	ret = generic_open_file(td, f);
    135 	if (ret)
    136 		return ret;
    137 
    138 	fmd = calloc(1, sizeof(*fmd));
    139 	if (!fmd)
    140 		goto err_close;
    141 
    142 	ret = mtd_get_dev_info(desc, f->file_name, &fmd->info);
    143 	if (ret != 0) {
    144 		td_verror(td, errno, "mtd_get_dev_info");
    145 		goto err_free;
    146 	}
    147 
    148 	FILE_SET_ENG_DATA(f, fmd);
    149 	return 0;
    150 
    151 err_free:
    152 	free(fmd);
    153 err_close:
    154 	{
    155 		int fio_unused __ret;
    156 		__ret = generic_close_file(td, f);
    157 		return 1;
    158 	}
    159 }
    160 
    161 static int fio_mtd_close_file(struct thread_data *td, struct fio_file *f)
    162 {
    163 	struct fio_mtd_data *fmd = FILE_ENG_DATA(f);
    164 
    165 	FILE_SET_ENG_DATA(f, NULL);
    166 	free(fmd);
    167 
    168 	return generic_close_file(td, f);
    169 }
    170 
    171 static int fio_mtd_get_file_size(struct thread_data *td, struct fio_file *f)
    172 {
    173 	struct mtd_dev_info info;
    174 
    175 	int ret = mtd_get_dev_info(desc, f->file_name, &info);
    176 	if (ret != 0) {
    177 		td_verror(td, errno, "mtd_get_dev_info");
    178 		return errno;
    179 	}
    180 	f->real_file_size = info.size;
    181 
    182 	return 0;
    183 }
    184 
    185 static struct ioengine_ops ioengine = {
    186 	.name		= "mtd",
    187 	.version	= FIO_IOOPS_VERSION,
    188 	.queue		= fio_mtd_queue,
    189 	.open_file	= fio_mtd_open_file,
    190 	.close_file	= fio_mtd_close_file,
    191 	.get_file_size	= fio_mtd_get_file_size,
    192 	.flags		= FIO_SYNCIO | FIO_NOEXTEND,
    193 };
    194 
    195 static void fio_init fio_mtd_register(void)
    196 {
    197 	desc = libmtd_open();
    198 	register_ioengine(&ioengine);
    199 }
    200 
    201 static void fio_exit fio_mtd_unregister(void)
    202 {
    203 	unregister_ioengine(&ioengine);
    204 	libmtd_close(desc);
    205 	desc = NULL;
    206 }
    207 
    208 
    209 
    210