|
@@ -964,7 +964,7 @@ xfs_vm_page_mkwrite(
|
|
|
|
|
|
/*
|
|
/*
|
|
* This type is designed to indicate the type of offset we would like
|
|
* This type is designed to indicate the type of offset we would like
|
|
- * to search from page cache for either xfs_seek_data() or xfs_seek_hole().
|
|
|
|
|
|
+ * to search from page cache for xfs_seek_hole_data().
|
|
*/
|
|
*/
|
|
enum {
|
|
enum {
|
|
HOLE_OFF = 0,
|
|
HOLE_OFF = 0,
|
|
@@ -1021,7 +1021,7 @@ xfs_lookup_buffer_offset(
|
|
/*
|
|
/*
|
|
* This routine is called to find out and return a data or hole offset
|
|
* This routine is called to find out and return a data or hole offset
|
|
* from the page cache for unwritten extents according to the desired
|
|
* from the page cache for unwritten extents according to the desired
|
|
- * type for xfs_seek_data() or xfs_seek_hole().
|
|
|
|
|
|
+ * type for xfs_seek_hole_data().
|
|
*
|
|
*
|
|
* The argument offset is used to tell where we start to search from the
|
|
* The argument offset is used to tell where we start to search from the
|
|
* page cache. Map is used to figure out the end points of the range to
|
|
* page cache. Map is used to figure out the end points of the range to
|
|
@@ -1181,9 +1181,10 @@ out:
|
|
}
|
|
}
|
|
|
|
|
|
STATIC loff_t
|
|
STATIC loff_t
|
|
-xfs_seek_data(
|
|
|
|
|
|
+xfs_seek_hole_data(
|
|
struct file *file,
|
|
struct file *file,
|
|
- loff_t start)
|
|
|
|
|
|
+ loff_t start,
|
|
|
|
+ int whence)
|
|
{
|
|
{
|
|
struct inode *inode = file->f_mapping->host;
|
|
struct inode *inode = file->f_mapping->host;
|
|
struct xfs_inode *ip = XFS_I(inode);
|
|
struct xfs_inode *ip = XFS_I(inode);
|
|
@@ -1195,6 +1196,9 @@ xfs_seek_data(
|
|
uint lock;
|
|
uint lock;
|
|
int error;
|
|
int error;
|
|
|
|
|
|
|
|
+ if (XFS_FORCED_SHUTDOWN(mp))
|
|
|
|
+ return -EIO;
|
|
|
|
+
|
|
lock = xfs_ilock_data_map_shared(ip);
|
|
lock = xfs_ilock_data_map_shared(ip);
|
|
|
|
|
|
isize = i_size_read(inode);
|
|
isize = i_size_read(inode);
|
|
@@ -1209,6 +1213,7 @@ xfs_seek_data(
|
|
*/
|
|
*/
|
|
fsbno = XFS_B_TO_FSBT(mp, start);
|
|
fsbno = XFS_B_TO_FSBT(mp, start);
|
|
end = XFS_B_TO_FSB(mp, isize);
|
|
end = XFS_B_TO_FSB(mp, isize);
|
|
|
|
+
|
|
for (;;) {
|
|
for (;;) {
|
|
struct xfs_bmbt_irec map[2];
|
|
struct xfs_bmbt_irec map[2];
|
|
int nmap = 2;
|
|
int nmap = 2;
|
|
@@ -1229,29 +1234,48 @@ xfs_seek_data(
|
|
offset = max_t(loff_t, start,
|
|
offset = max_t(loff_t, start,
|
|
XFS_FSB_TO_B(mp, map[i].br_startoff));
|
|
XFS_FSB_TO_B(mp, map[i].br_startoff));
|
|
|
|
|
|
- /* Landed in a data extent */
|
|
|
|
- if (map[i].br_startblock == DELAYSTARTBLOCK ||
|
|
|
|
- (map[i].br_state == XFS_EXT_NORM &&
|
|
|
|
- !isnullstartblock(map[i].br_startblock)))
|
|
|
|
|
|
+ /* Landed in the hole we wanted? */
|
|
|
|
+ if (whence == SEEK_HOLE &&
|
|
|
|
+ map[i].br_startblock == HOLESTARTBLOCK)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ /* Landed in the data extent we wanted? */
|
|
|
|
+ if (whence == SEEK_DATA &&
|
|
|
|
+ (map[i].br_startblock == DELAYSTARTBLOCK ||
|
|
|
|
+ (map[i].br_state == XFS_EXT_NORM &&
|
|
|
|
+ !isnullstartblock(map[i].br_startblock))))
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
/*
|
|
/*
|
|
- * Landed in an unwritten extent, try to search data
|
|
|
|
- * from page cache.
|
|
|
|
|
|
+ * Landed in an unwritten extent, try to search
|
|
|
|
+ * for hole or data from page cache.
|
|
*/
|
|
*/
|
|
if (map[i].br_state == XFS_EXT_UNWRITTEN) {
|
|
if (map[i].br_state == XFS_EXT_UNWRITTEN) {
|
|
if (xfs_find_get_desired_pgoff(inode, &map[i],
|
|
if (xfs_find_get_desired_pgoff(inode, &map[i],
|
|
- DATA_OFF, &offset))
|
|
|
|
|
|
+ whence == SEEK_HOLE ? HOLE_OFF : DATA_OFF,
|
|
|
|
+ &offset))
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
- * map[0] is hole or its an unwritten extent but
|
|
|
|
- * without data in page cache. Probably means that
|
|
|
|
- * we are reading after EOF if nothing in map[1].
|
|
|
|
|
|
+ * We only received one extent out of the two requested. This
|
|
|
|
+ * means we've hit EOF and didn't find what we are looking for.
|
|
*/
|
|
*/
|
|
if (nmap == 1) {
|
|
if (nmap == 1) {
|
|
|
|
+ /*
|
|
|
|
+ * If we were looking for a hole, set offset to
|
|
|
|
+ * the end of the file (i.e., there is an implicit
|
|
|
|
+ * hole at the end of any file).
|
|
|
|
+ */
|
|
|
|
+ if (whence == SEEK_HOLE) {
|
|
|
|
+ offset = isize;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ /*
|
|
|
|
+ * If we were looking for data, it's nowhere to be found
|
|
|
|
+ */
|
|
|
|
+ ASSERT(whence == SEEK_DATA);
|
|
error = -ENXIO;
|
|
error = -ENXIO;
|
|
goto out_unlock;
|
|
goto out_unlock;
|
|
}
|
|
}
|
|
@@ -1260,125 +1284,30 @@ xfs_seek_data(
|
|
|
|
|
|
/*
|
|
/*
|
|
* Nothing was found, proceed to the next round of search
|
|
* Nothing was found, proceed to the next round of search
|
|
- * if reading offset not beyond or hit EOF.
|
|
|
|
|
|
+ * if the next reading offset is not at or beyond EOF.
|
|
*/
|
|
*/
|
|
fsbno = map[i - 1].br_startoff + map[i - 1].br_blockcount;
|
|
fsbno = map[i - 1].br_startoff + map[i - 1].br_blockcount;
|
|
start = XFS_FSB_TO_B(mp, fsbno);
|
|
start = XFS_FSB_TO_B(mp, fsbno);
|
|
if (start >= isize) {
|
|
if (start >= isize) {
|
|
|
|
+ if (whence == SEEK_HOLE) {
|
|
|
|
+ offset = isize;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ ASSERT(whence == SEEK_DATA);
|
|
error = -ENXIO;
|
|
error = -ENXIO;
|
|
goto out_unlock;
|
|
goto out_unlock;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-out:
|
|
|
|
- offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
|
|
|
|
-
|
|
|
|
-out_unlock:
|
|
|
|
- xfs_iunlock(ip, lock);
|
|
|
|
-
|
|
|
|
- if (error)
|
|
|
|
- return error;
|
|
|
|
- return offset;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-STATIC loff_t
|
|
|
|
-xfs_seek_hole(
|
|
|
|
- struct file *file,
|
|
|
|
- loff_t start)
|
|
|
|
-{
|
|
|
|
- struct inode *inode = file->f_mapping->host;
|
|
|
|
- struct xfs_inode *ip = XFS_I(inode);
|
|
|
|
- struct xfs_mount *mp = ip->i_mount;
|
|
|
|
- loff_t uninitialized_var(offset);
|
|
|
|
- xfs_fsize_t isize;
|
|
|
|
- xfs_fileoff_t fsbno;
|
|
|
|
- xfs_filblks_t end;
|
|
|
|
- uint lock;
|
|
|
|
- int error;
|
|
|
|
-
|
|
|
|
- if (XFS_FORCED_SHUTDOWN(mp))
|
|
|
|
- return -EIO;
|
|
|
|
-
|
|
|
|
- lock = xfs_ilock_data_map_shared(ip);
|
|
|
|
-
|
|
|
|
- isize = i_size_read(inode);
|
|
|
|
- if (start >= isize) {
|
|
|
|
- error = -ENXIO;
|
|
|
|
- goto out_unlock;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- fsbno = XFS_B_TO_FSBT(mp, start);
|
|
|
|
- end = XFS_B_TO_FSB(mp, isize);
|
|
|
|
-
|
|
|
|
- for (;;) {
|
|
|
|
- struct xfs_bmbt_irec map[2];
|
|
|
|
- int nmap = 2;
|
|
|
|
- unsigned int i;
|
|
|
|
-
|
|
|
|
- error = xfs_bmapi_read(ip, fsbno, end - fsbno, map, &nmap,
|
|
|
|
- XFS_BMAPI_ENTIRE);
|
|
|
|
- if (error)
|
|
|
|
- goto out_unlock;
|
|
|
|
-
|
|
|
|
- /* No extents at given offset, must be beyond EOF */
|
|
|
|
- if (nmap == 0) {
|
|
|
|
- error = -ENXIO;
|
|
|
|
- goto out_unlock;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- for (i = 0; i < nmap; i++) {
|
|
|
|
- offset = max_t(loff_t, start,
|
|
|
|
- XFS_FSB_TO_B(mp, map[i].br_startoff));
|
|
|
|
-
|
|
|
|
- /* Landed in a hole */
|
|
|
|
- if (map[i].br_startblock == HOLESTARTBLOCK)
|
|
|
|
- goto out;
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * Landed in an unwritten extent, try to search hole
|
|
|
|
- * from page cache.
|
|
|
|
- */
|
|
|
|
- if (map[i].br_state == XFS_EXT_UNWRITTEN) {
|
|
|
|
- if (xfs_find_get_desired_pgoff(inode, &map[i],
|
|
|
|
- HOLE_OFF, &offset))
|
|
|
|
- goto out;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * map[0] contains data or its unwritten but contains
|
|
|
|
- * data in page cache, probably means that we are
|
|
|
|
- * reading after EOF. We should fix offset to point
|
|
|
|
- * to the end of the file(i.e., there is an implicit
|
|
|
|
- * hole at the end of any file).
|
|
|
|
- */
|
|
|
|
- if (nmap == 1) {
|
|
|
|
- offset = isize;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ASSERT(i > 1);
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * Both mappings contains data, proceed to the next round of
|
|
|
|
- * search if the current reading offset not beyond or hit EOF.
|
|
|
|
- */
|
|
|
|
- fsbno = map[i - 1].br_startoff + map[i - 1].br_blockcount;
|
|
|
|
- start = XFS_FSB_TO_B(mp, fsbno);
|
|
|
|
- if (start >= isize) {
|
|
|
|
- offset = isize;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
out:
|
|
out:
|
|
/*
|
|
/*
|
|
- * At this point, we must have found a hole. However, the returned
|
|
|
|
|
|
+ * If at this point we have found the hole we wanted, the returned
|
|
* offset may be bigger than the file size as it may be aligned to
|
|
* offset may be bigger than the file size as it may be aligned to
|
|
- * page boundary for unwritten extents, we need to deal with this
|
|
|
|
|
|
+ * page boundary for unwritten extents. We need to deal with this
|
|
* situation in particular.
|
|
* situation in particular.
|
|
*/
|
|
*/
|
|
- offset = min_t(loff_t, offset, isize);
|
|
|
|
|
|
+ if (whence == SEEK_HOLE)
|
|
|
|
+ offset = min_t(loff_t, offset, isize);
|
|
offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
|
|
offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
|
|
|
|
|
|
out_unlock:
|
|
out_unlock:
|
|
@@ -1400,10 +1329,9 @@ xfs_file_llseek(
|
|
case SEEK_CUR:
|
|
case SEEK_CUR:
|
|
case SEEK_SET:
|
|
case SEEK_SET:
|
|
return generic_file_llseek(file, offset, origin);
|
|
return generic_file_llseek(file, offset, origin);
|
|
- case SEEK_DATA:
|
|
|
|
- return xfs_seek_data(file, offset);
|
|
|
|
case SEEK_HOLE:
|
|
case SEEK_HOLE:
|
|
- return xfs_seek_hole(file, offset);
|
|
|
|
|
|
+ case SEEK_DATA:
|
|
|
|
+ return xfs_seek_hole_data(file, offset, origin);
|
|
default:
|
|
default:
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
}
|
|
}
|