1/*
2 *   Copyright (c) International Business Machines Corp., 2001-2004
3 *
4 *   This program is free software;  you can redistribute it and/or modify
5 *   it under the terms of the GNU General Public License as published by
6 *   the Free Software Foundation; either version 2 of the License, or
7 *   (at your option) any later version.
8 *
9 *   This program is distributed in the hope that it will be useful,
10 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
11 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12 *   the GNU General Public License for more details.
13 *
14 *   You should have received a copy of the GNU General Public License
15 *   along with this program;  if not, write to the Free Software
16 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18#define _LARGEFILE64_SOURCE
19#include <sys/stat.h>
20#include <sys/types.h>
21#include <pthread.h>
22#include <string.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <assert.h>
26#include <errno.h>
27
28#include "fh.h"
29#include "util.h"
30#include "ffsb.h"
31#include "fileops.h"
32#include "ffsb_op.h"
33
34static void do_stats(struct timeval *start, struct timeval *end,
35		     ffsb_thread_t * ft, ffsb_fs_t * fs, syscall_t sys)
36{
37	struct timeval diff;
38	uint32_t value = 0;
39
40	if (!ft && !fs)
41		return;
42
43	timersub(end, start, &diff);
44
45	value = 1000000 * diff.tv_sec + diff.tv_usec;
46
47	if (ft && ft_needs_stats(ft, sys))
48		ft_add_stat(ft, sys, value);
49	if (fs && fs_needs_stats(fs, sys))
50		fs_add_stat(fs, sys, value);
51}
52
53void fop_bench(ffsb_fs_t * fs, unsigned opnum)
54{
55	fs_set_opdata(fs, fs_get_datafiles(fs), opnum);
56}
57
58void fop_age(ffsb_fs_t * fs, unsigned opnum)
59{
60	fs_set_opdata(fs, fs_get_agefiles(fs), opnum);
61}
62
63static unsigned readfile_helper(int fd, uint64_t size, uint32_t blocksize,
64				char *buf, ffsb_thread_t * ft, ffsb_fs_t * fs)
65{
66	int iterations, a;
67	int last;
68
69	iterations = size / blocksize;
70	last = size % blocksize;
71
72	for (a = 0; a < iterations; a++)
73		fhread(fd, buf, blocksize, ft, fs);
74	if (last)
75		fhread(fd, buf, last, ft, fs);
76	return iterations;
77}
78
79static uint64_t get_random_offset(randdata_t * rd, uint64_t filesize,
80				  int aligned)
81{
82	if (!aligned)
83		return getllrandom(rd, filesize);
84
85	filesize /= 4096;
86	return getllrandom(rd, filesize) * 4096;
87}
88
89void ffsb_readfile(ffsb_thread_t * ft, ffsb_fs_t * fs, unsigned opnum)
90{
91	struct benchfiles *bf = (struct benchfiles *)fs_get_opdata(fs, opnum);
92	struct ffsb_file *curfile = NULL;
93
94	int fd;
95	uint64_t filesize;
96
97	char *buf = ft_getbuf(ft);
98	int read_random = ft_get_read_random(ft);
99	uint64_t read_size = ft_get_read_size(ft);
100	uint32_t read_blocksize = ft_get_read_blocksize(ft);
101	uint32_t read_skipsize = ft_get_read_skipsize(ft);
102	int skip_reads = ft_get_read_skip(ft);
103	struct randdata *rd = ft_get_randdata(ft);
104
105	uint64_t iterations = 0;
106
107	curfile = choose_file_reader(bf, rd);
108	fd = fhopenread(curfile->name, ft, fs);
109
110	filesize = ffsb_get_filesize(curfile->name);
111
112	assert(filesize >= read_size);
113
114	/* Sequential read, starting at a random point */
115	if (!read_random) {
116		uint64_t range = filesize - read_size;
117		uint64_t offset = 0;
118		/* Skip or "stride" reads option */
119		if (skip_reads) {
120			unsigned i, last;
121			uint64_t minfilesize;
122			iterations = read_size / read_blocksize;
123			last = read_size % read_blocksize;
124
125			/* Double check that the user hasn't specified
126			 * a read_size that is too large when combined
127			 * with the seeks
128			 */
129			if (last)
130				minfilesize = last + iterations *
131				    (read_blocksize + read_skipsize);
132			else
133				minfilesize = read_blocksize + iterations - 1 *
134				    (read_blocksize + read_skipsize);
135
136			if (minfilesize > filesize) {
137				printf("Error: read size %llu bytes too big "
138				       "w/ skipsize %u and blocksize %u,"
139				       " for file of size %llu bytes\n"
140				       " aborting\n\n", read_size,
141				       read_skipsize, read_blocksize, filesize);
142				printf("minimum file size must be at least "
143				       " %llu bytes\n", minfilesize);
144				exit(1);
145			}
146
147			for (i = 0; i < iterations; i++) {
148				fhread(fd, buf, read_blocksize, ft, fs);
149				fhseek(fd, (uint64_t) read_skipsize, SEEK_CUR,
150				       ft, fs);
151			}
152			if (last) {
153				fhread(fd, buf, (uint64_t) last, ft, fs);
154				iterations++;
155			}
156		} else {
157			/* Regular sequential reads */
158			if (range) {
159				offset = get_random_offset(rd, range,
160							   fs_get_alignio(fs));
161				fhseek(fd, offset, SEEK_SET, ft, fs);
162			}
163			iterations = readfile_helper(fd, read_size,
164						     read_blocksize, buf,
165						     ft, fs);
166		}
167	} else {
168		/* Randomized read */
169		uint64_t range = filesize - read_blocksize;
170		int i;
171
172		iterations = read_size / read_blocksize;
173
174		for (i = 0; i < iterations; i++) {
175			uint64_t offset = get_random_offset(rd, range,
176							    fs_get_alignio(fs));
177			fhseek(fd, offset, SEEK_SET, ft, fs);
178			fhread(fd, buf, read_blocksize, ft, fs);
179		}
180	}
181
182	unlock_file_reader(curfile);
183	fhclose(fd, ft, fs);
184
185	ft_incr_op(ft, opnum, iterations, read_size);
186	ft_add_readbytes(ft, read_size);
187}
188
189/* Just like ffsb_readfile but we read the whole file from start to
190 * finish regardless of file size.
191 */
192void ffsb_readall(ffsb_thread_t * ft, ffsb_fs_t * fs, unsigned opnum)
193{
194	struct benchfiles *bf = (struct benchfiles *)fs_get_opdata(fs, opnum);
195	struct ffsb_file *curfile = NULL;
196	int fd;
197	uint64_t filesize;
198
199	char *buf = ft_getbuf(ft);
200	uint32_t read_blocksize = ft_get_read_blocksize(ft);
201	struct randdata *rd = ft_get_randdata(ft);
202
203	unsigned iterations = 0;
204
205	curfile = choose_file_reader(bf, rd);
206	fd = fhopenread(curfile->name, ft, fs);
207
208	filesize = ffsb_get_filesize(curfile->name);
209	iterations = readfile_helper(fd, filesize, read_blocksize, buf, ft, fs);
210
211	unlock_file_reader(curfile);
212	fhclose(fd, ft, fs);
213
214	ft_incr_op(ft, opnum, iterations, filesize);
215	ft_add_readbytes(ft, filesize);
216}
217
218/* Shared core between ffsb_writefile and ffsb_writefile_fsync.*/
219
220static unsigned ffsb_writefile_core(ffsb_thread_t * ft, ffsb_fs_t * fs,
221				    unsigned opnum, uint64_t * filesize_ret,
222				    int fsync_file)
223{
224	struct benchfiles *bf = (struct benchfiles *)fs_get_opdata(fs, opnum);
225	struct ffsb_file *curfile = NULL;
226
227	int fd;
228	uint64_t filesize;
229
230	char *buf = ft_getbuf(ft);
231	int write_random = ft_get_write_random(ft);
232	uint32_t write_size = ft_get_write_size(ft);
233	uint32_t write_blocksize = ft_get_write_blocksize(ft);
234	struct randdata *rd = ft_get_randdata(ft);
235	unsigned iterations = 0;
236
237	curfile = choose_file_reader(bf, rd);
238	fd = fhopenwrite(curfile->name, ft, fs);
239
240	filesize = ffsb_get_filesize(curfile->name);
241
242	assert(filesize >= write_size);
243
244	/* Sequential write, starting at a random point  */
245	if (!write_random) {
246		uint64_t range = filesize - write_size;
247		uint64_t offset = 0;
248		if (range) {
249			offset = get_random_offset(rd, range,
250						   fs_get_alignio(fs));
251			fhseek(fd, offset, SEEK_SET, ft, fs);
252		}
253		iterations = writefile_helper(fd, write_size, write_blocksize,
254					      buf, ft, fs);
255	} else {
256		/* Randomized write */
257		uint64_t range = filesize - write_blocksize;
258		int i;
259		iterations = write_size / write_blocksize;
260
261		for (i = 0; i < iterations; i++) {
262			uint64_t offset = get_random_offset(rd, range,
263							    fs_get_alignio(fs));
264			fhseek(fd, offset, SEEK_SET, ft, fs);
265			fhwrite(fd, buf, write_blocksize, ft, fs);
266		}
267	}
268
269	if (fsync_file) {
270		if (fsync(fd)) {
271			perror("fsync");
272			printf("aborting\n");
273			exit(1);
274		}
275	}
276	unlock_file_reader(curfile);
277	fhclose(fd, ft, fs);
278	*filesize_ret = filesize;
279	return iterations;
280}
281
282void ffsb_writefile(ffsb_thread_t * ft, ffsb_fs_t * fs, unsigned opnum)
283{
284	unsigned iterations;
285	uint64_t filesize;
286
287	iterations = ffsb_writefile_core(ft, fs, opnum, &filesize, 0);
288	ft_incr_op(ft, opnum, iterations, filesize);
289	ft_add_writebytes(ft, filesize);
290}
291
292void ffsb_writefile_fsync(ffsb_thread_t * ft, ffsb_fs_t * fs, unsigned opnum)
293{
294	unsigned iterations;
295	uint64_t filesize;
296
297	iterations = ffsb_writefile_core(ft, fs, opnum, &filesize, 1);
298	ft_incr_op(ft, opnum, iterations, filesize);
299	ft_add_writebytes(ft, filesize);
300}
301
302/* Shared core between ffsb_writeall and ffsb_writeall_fsync.*/
303
304static unsigned ffsb_writeall_core(ffsb_thread_t * ft, ffsb_fs_t * fs,
305				   unsigned opnum, uint64_t * filesize_ret,
306				   int fsync_file)
307{
308	struct benchfiles *bf = (struct benchfiles *)fs_get_opdata(fs, opnum);
309	struct ffsb_file *curfile = NULL;
310	int fd;
311	uint64_t filesize;
312
313	char *buf = ft_getbuf(ft);
314	uint32_t write_blocksize = ft_get_write_blocksize(ft);
315	struct randdata *rd = ft_get_randdata(ft);
316
317	unsigned iterations = 0;
318
319	curfile = choose_file_reader(bf, rd);
320	fd = fhopenwrite(curfile->name, ft, fs);
321
322	filesize = ffsb_get_filesize(curfile->name);
323	iterations = writefile_helper(fd, filesize, write_blocksize, buf,
324				      ft, fs);
325	if (fsync_file)
326		if (fsync(fd)) {
327			perror("fsync");
328			printf("aborting\n");
329			exit(1);
330		}
331
332	unlock_file_reader(curfile);
333	fhclose(fd, ft, fs);
334	*filesize_ret = filesize;
335	return iterations;
336}
337
338/* Just like ffsb_writefile but we write the whole file from start to
339 * finish regardless of file size
340 */
341void ffsb_writeall(ffsb_thread_t * ft, ffsb_fs_t * fs, unsigned opnum)
342{
343	unsigned iterations;
344	uint64_t filesize;
345
346	iterations = ffsb_writeall_core(ft, fs, opnum, &filesize, 0);
347	ft_incr_op(ft, opnum, iterations, filesize);
348	ft_add_writebytes(ft, filesize);
349}
350
351void ffsb_writeall_fsync(ffsb_thread_t * ft, ffsb_fs_t * fs, unsigned opnum)
352{
353	unsigned iterations;
354	uint64_t filesize;
355
356	iterations = ffsb_writeall_core(ft, fs, opnum, &filesize, 1);
357	ft_incr_op(ft, opnum, iterations, filesize);
358	ft_add_writebytes(ft, filesize);
359}
360
361static unsigned ffsb_appendfile_core(ffsb_thread_t * ft, ffsb_fs_t * fs,
362				     unsigned opnum, uint64_t * filesize_ret,
363				     int fsync_file)
364{
365	struct benchfiles *bf = (struct benchfiles *)fs_get_opdata(fs, opnum);
366	struct ffsb_file *curfile;
367
368	int fd;
369
370	char *buf = ft_getbuf(ft);
371	uint32_t write_size = ft_get_write_size(ft);
372	uint32_t write_blocksize = ft_get_write_blocksize(ft);
373	struct randdata *rd = ft_get_randdata(ft);
374	unsigned iterations = 0;
375
376	curfile = choose_file_reader(bf, rd);
377	fd = fhopenappend(curfile->name, ft, fs);
378
379	unlock_file_reader(curfile);
380
381	curfile->size += (uint64_t) write_size;
382
383	iterations = writefile_helper(fd, write_size, write_blocksize, buf,
384				      ft, fs);
385	if (fsync_file)
386		if (fsync(fd)) {
387			perror("fsync");
388			printf("aborting\n");
389			exit(1);
390		}
391
392	fhclose(fd, ft, fs);
393	*filesize_ret = write_size;
394	return iterations;
395}
396
397void ffsb_appendfile(ffsb_thread_t * ft, ffsb_fs_t * fs, unsigned opnum)
398{
399	unsigned iterations;
400	uint64_t filesize;
401
402	iterations = ffsb_appendfile_core(ft, fs, opnum, &filesize, 0);
403	ft_incr_op(ft, opnum, iterations, filesize);
404	ft_add_writebytes(ft, filesize);
405}
406
407void ffsb_appendfile_fsync(ffsb_thread_t * ft, ffsb_fs_t * fs, unsigned opnum)
408{
409	unsigned iterations;
410	uint64_t filesize;
411
412	iterations = ffsb_appendfile_core(ft, fs, opnum, &filesize, 1);
413	ft_incr_op(ft, opnum, iterations, filesize);
414	ft_add_writebytes(ft, filesize);
415}
416
417static unsigned ffsb_createfile_core(ffsb_thread_t * ft, ffsb_fs_t * fs,
418				     unsigned opnum, uint64_t * filesize_ret,
419				     int fsync_file)
420{
421	struct benchfiles *bf = (struct benchfiles *)fs_get_opdata(fs, opnum);
422	struct ffsb_file *newfile = NULL;
423
424	int fd;
425	uint64_t size;
426
427	char *buf = ft_getbuf(ft);
428	uint32_t write_blocksize = ft_get_write_blocksize(ft);
429	struct randdata *rd = ft_get_randdata(ft);
430	unsigned iterations = 0;
431
432	if (fs->num_weights) {
433		int num = 1 + getrandom(rd, fs->sum_weights);
434		int curop = 0;
435
436		while (fs->size_weights[curop].weight < num) {
437			num -= fs->size_weights[curop].weight;
438			curop++;
439		}
440		size = fs->size_weights[curop].size;
441	} else {
442		uint64_t range =
443		    fs_get_max_filesize(fs) - fs_get_min_filesize(fs);
444		size = fs_get_min_filesize(fs);
445		if (range != 0)
446			size += getllrandom(rd, range);
447	}
448
449	newfile = add_file(bf, size, rd);
450	fd = fhopencreate(newfile->name, ft, fs);
451	iterations = writefile_helper(fd, size, write_blocksize, buf, ft, fs);
452
453	if (fsync_file)
454		if (fsync(fd)) {
455			perror("fsync");
456			printf("aborting\n");
457			exit(1);
458		}
459
460	fhclose(fd, ft, fs);
461	unlock_file_writer(newfile);
462	*filesize_ret = size;
463	return iterations;
464}
465
466void ffsb_createfile(ffsb_thread_t * ft, ffsb_fs_t * fs, unsigned opnum)
467{
468	unsigned iterations;
469	uint64_t filesize;
470
471	iterations = ffsb_createfile_core(ft, fs, opnum, &filesize, 0);
472	ft_incr_op(ft, opnum, iterations, filesize);
473	ft_add_writebytes(ft, filesize);
474}
475
476void ffsb_createfile_fsync(ffsb_thread_t * ft, ffsb_fs_t * fs, unsigned opnum)
477{
478	unsigned iterations;
479	uint64_t filesize;
480
481	iterations = ffsb_createfile_core(ft, fs, opnum, &filesize, 1);
482	ft_incr_op(ft, opnum, iterations, filesize);
483	ft_add_writebytes(ft, filesize);
484}
485
486void ffsb_deletefile(ffsb_thread_t * ft, ffsb_fs_t * fs, unsigned opnum)
487{
488	struct benchfiles *bf = (struct benchfiles *)fs_get_opdata(fs, opnum);
489	struct ffsb_file *curfile = NULL;
490	randdata_t *rd = ft_get_randdata(ft);
491	struct timeval start, end;
492	int need_stats = ft_needs_stats(ft, SYS_UNLINK) ||
493	    fs_needs_stats(fs, SYS_UNLINK);
494
495	curfile = choose_file_writer(bf, rd);
496	remove_file(bf, curfile);
497
498	if (need_stats)
499		gettimeofday(&start, NULL);
500
501	if (unlink(curfile->name) == -1) {
502		printf("error deleting %s in deletefile\n", curfile->name);
503		perror("deletefile");
504		exit(0);
505	}
506
507	if (need_stats) {
508		gettimeofday(&end, NULL);
509		do_stats(&start, &end, ft, fs, SYS_UNLINK);
510	}
511
512	rw_unlock_write(&curfile->lock);
513
514	ft_incr_op(ft, opnum, 1, 0);
515}
516
517void ffsb_open_close(ffsb_thread_t * ft, ffsb_fs_t * fs, unsigned opnum)
518{
519	struct benchfiles *bf = (struct benchfiles *)fs_get_opdata(fs, opnum);
520	struct ffsb_file *curfile = NULL;
521	randdata_t *rd = ft_get_randdata(ft);
522	int fd;
523
524	curfile = choose_file_reader(bf, rd);
525	fd = fhopenread(curfile->name, ft, fs);
526	fhclose(fd, ft, fs);
527	unlock_file_reader(curfile);
528	ft_incr_op(ft, opnum, 1, 0);
529}
530
531void ffsb_stat(ffsb_thread_t * ft, ffsb_fs_t * fs, unsigned opnum)
532{
533	struct benchfiles *bf = (struct benchfiles *)fs_get_opdata(fs, opnum);
534	struct ffsb_file *curfile = NULL;
535	randdata_t *rd = ft_get_randdata(ft);
536
537	curfile = choose_file_reader(bf, rd);
538	fhstat(curfile->name, ft, fs);
539	unlock_file_reader(curfile);
540
541	ft_incr_op(ft, opnum, 1, 0);
542}
543