EXTRA_KCFLAGS="$tmp_flags"
])
+# LC_FILE_UPDATE_TIME
+# 2.6.9 has inode_update_time instead of file_update_time
+AC_DEFUN([LC_FILE_UPDATE_TIME],
+[AC_MSG_CHECKING([if file_update_time is exported])
+LB_LINUX_TRY_COMPILE([
+ #include <linux/fs.h>
+],[
+ file_update_time(NULL);
+],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_FILE_UPDATE_TIME, 1,
+ [use file_update_time])
+],[
+ AC_MSG_RESULT(no)
+])
+])
+
# LC_FILE_WRITEV
# 2.6.19 replaced writev with aio_write
AC_DEFUN([LC_FILE_WRITEV],
])
])
+
# LC_GENERIC_FILE_READ
# 2.6.19 replaced readv with aio_read
AC_DEFUN([LC_FILE_READV],
# 2.6.19
LC_INODE_BLKSIZE
LC_VFS_READDIR_U64_INO
+ LC_FILE_UPDATE_TIME
LC_FILE_WRITEV
LC_FILE_READV
} else {
retval = ll_direct_IO(READ, file, iov_copy, *ppos, nr_segs, 0);
if (retval > 0) {
- lprocfs_counter_add(sbi->ll_stats,
- LPROC_LL_LOCKLESS_READ,
- (long)retval);
+ file_accessed(file);
+ lprocfs_counter_add(sbi->ll_stats,
+ LPROC_LL_LOCKLESS_READ,
+ (long)retval);
*ppos += retval;
}
}
#endif
}
+/* iov_shorten from linux kernel */
+static unsigned long ll_iov_shorten(struct iovec *iov,
+ unsigned long nr_segs,
+ size_t to)
+{
+ unsigned long seg = 0;
+ size_t len = 0;
+
+ while (seg < nr_segs) {
+ seg++;
+ if (len + iov->iov_len >= to) {
+ iov->iov_len = to - len;
+ break;
+ }
+ len += iov->iov_len;
+ iov++;
+ }
+ return seg;
+}
+
+/* 2.6.22 and 2.6.27 export this as generic_segment_checks */
+static int ll_generic_segment_checks(const struct iovec *iov,
+ unsigned long *nr_segs,
+ size_t *count,
+ int access_flags)
+{
+ unsigned long seg;
+ size_t cnt = 0;
+ for (seg = 0; seg < *nr_segs; seg++) {
+ const struct iovec *iv = &iov[seg];
+
+ /*
+ * If any segment has a negative length, or the cumulative
+ * length ever wraps negative then return -EINVAL.
+ */
+ cnt += iv->iov_len;
+ if (unlikely((ssize_t)(cnt|iv->iov_len) < 0))
+ return -EINVAL;
+ if (access_ok(access_flags, iv->iov_base, iv->iov_len))
+ continue;
+ if (seg == 0)
+ return -EFAULT;
+ *nr_segs = seg;
+ cnt -= iv->iov_len; /* This segment is no good */
+ break;
+ }
+ *count = cnt;
+ return 0;
+}
+
/*
* Write to a file (through the page cache).
*/
*ppos);
#endif
} else {
+ size_t ocount, ncount;
+
+ retval = ll_generic_segment_checks(iov_copy, &nrsegs_copy,
+ &ocount, VERIFY_READ);
+ if (retval)
+ GOTO(out, retval);
+
+ retval = generic_write_checks(file, ppos, &ncount, 0);
+ if (retval)
+ GOTO(out, retval);
+
+ if (unlikely(ocount != ncount)) {
+ /* we are allowed to modify the original iov too */
+ nrsegs_copy = ll_iov_shorten(iov_copy, nrsegs_copy,
+ ncount);
+ chunk = 0; /* no repetition after the short write */
+ }
+
+ retval = ll_remove_suid(file, file->f_vfsmnt);
+ if (retval)
+ GOTO(out, retval);
+
+ ll_update_time(file);
retval = ll_direct_IO(WRITE, file, iov_copy, *ppos, nr_segs, 0);
if (retval > 0) {
- lprocfs_counter_add(sbi->ll_stats,
- LPROC_LL_LOCKLESS_WRITE,
- (long)retval);
+ lprocfs_counter_add(sbi->ll_stats,
+ LPROC_LL_LOCKLESS_WRITE,
+ (long)retval);
*ppos += retval;
}
}
}
run_test 38 "lockless i/o with O_DIRECT and unaligned writes"
+test_39() {
+ local originaltime
+ local updatedtime
+ local delay=3
+
+ touch $DIR1/$tfile
+ originaltime=$(stat -c %Y $DIR1/$tfile)
+ log "original modification time is $originaltime"
+ sleep $delay
+ multiop $DIR1/$tfile oO_DIRECT:O_WRONLY:w$((10*1048576))c || error "multiop has failed"
+ updatedtime=$(stat -c %Y $DIR2/$tfile)
+ log "updated modification time is $updatedtime"
+ [ $((updatedtime - originaltime)) -ge $delay ] || error "invalid modification time"
+ rm -rf $DIR/$tfile
+}
+run_test 39 "direct I/O writes should update mtime ========="
+
log "cleanup: ======================================================"
check_and_cleanup_lustre