|
@@ -457,7 +457,7 @@ static void shmem_free_pages(struct list_head *next)
|
|
} while (next);
|
|
} while (next);
|
|
}
|
|
}
|
|
|
|
|
|
-static void shmem_truncate(struct inode *inode)
|
|
|
|
|
|
+static void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end)
|
|
{
|
|
{
|
|
struct shmem_inode_info *info = SHMEM_I(inode);
|
|
struct shmem_inode_info *info = SHMEM_I(inode);
|
|
unsigned long idx;
|
|
unsigned long idx;
|
|
@@ -475,18 +475,27 @@ static void shmem_truncate(struct inode *inode)
|
|
long nr_swaps_freed = 0;
|
|
long nr_swaps_freed = 0;
|
|
int offset;
|
|
int offset;
|
|
int freed;
|
|
int freed;
|
|
|
|
+ int punch_hole = 0;
|
|
|
|
|
|
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
|
|
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
|
|
- idx = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
|
|
|
|
|
+ idx = (start + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
|
if (idx >= info->next_index)
|
|
if (idx >= info->next_index)
|
|
return;
|
|
return;
|
|
|
|
|
|
spin_lock(&info->lock);
|
|
spin_lock(&info->lock);
|
|
info->flags |= SHMEM_TRUNCATE;
|
|
info->flags |= SHMEM_TRUNCATE;
|
|
- limit = info->next_index;
|
|
|
|
- info->next_index = idx;
|
|
|
|
|
|
+ if (likely(end == (loff_t) -1)) {
|
|
|
|
+ limit = info->next_index;
|
|
|
|
+ info->next_index = idx;
|
|
|
|
+ } else {
|
|
|
|
+ limit = (end + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
|
|
|
+ if (limit > info->next_index)
|
|
|
|
+ limit = info->next_index;
|
|
|
|
+ punch_hole = 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
topdir = info->i_indirect;
|
|
topdir = info->i_indirect;
|
|
- if (topdir && idx <= SHMEM_NR_DIRECT) {
|
|
|
|
|
|
+ if (topdir && idx <= SHMEM_NR_DIRECT && !punch_hole) {
|
|
info->i_indirect = NULL;
|
|
info->i_indirect = NULL;
|
|
nr_pages_to_free++;
|
|
nr_pages_to_free++;
|
|
list_add(&topdir->lru, &pages_to_free);
|
|
list_add(&topdir->lru, &pages_to_free);
|
|
@@ -573,11 +582,12 @@ static void shmem_truncate(struct inode *inode)
|
|
set_page_private(subdir, page_private(subdir) - freed);
|
|
set_page_private(subdir, page_private(subdir) - freed);
|
|
if (offset)
|
|
if (offset)
|
|
spin_unlock(&info->lock);
|
|
spin_unlock(&info->lock);
|
|
- BUG_ON(page_private(subdir) > offset);
|
|
|
|
|
|
+ if (!punch_hole)
|
|
|
|
+ BUG_ON(page_private(subdir) > offset);
|
|
}
|
|
}
|
|
if (offset)
|
|
if (offset)
|
|
offset = 0;
|
|
offset = 0;
|
|
- else if (subdir) {
|
|
|
|
|
|
+ else if (subdir && !page_private(subdir)) {
|
|
dir[diroff] = NULL;
|
|
dir[diroff] = NULL;
|
|
nr_pages_to_free++;
|
|
nr_pages_to_free++;
|
|
list_add(&subdir->lru, &pages_to_free);
|
|
list_add(&subdir->lru, &pages_to_free);
|
|
@@ -594,7 +604,7 @@ done2:
|
|
* Also, though shmem_getpage checks i_size before adding to
|
|
* Also, though shmem_getpage checks i_size before adding to
|
|
* cache, no recheck after: so fix the narrow window there too.
|
|
* cache, no recheck after: so fix the narrow window there too.
|
|
*/
|
|
*/
|
|
- truncate_inode_pages(inode->i_mapping, inode->i_size);
|
|
|
|
|
|
+ truncate_inode_pages_range(inode->i_mapping, start, end);
|
|
}
|
|
}
|
|
|
|
|
|
spin_lock(&info->lock);
|
|
spin_lock(&info->lock);
|
|
@@ -614,6 +624,11 @@ done2:
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void shmem_truncate(struct inode *inode)
|
|
|
|
+{
|
|
|
|
+ shmem_truncate_range(inode, inode->i_size, (loff_t)-1);
|
|
|
|
+}
|
|
|
|
+
|
|
static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
|
|
static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
|
|
{
|
|
{
|
|
struct inode *inode = dentry->d_inode;
|
|
struct inode *inode = dentry->d_inode;
|
|
@@ -2083,6 +2098,7 @@ static struct file_operations shmem_file_operations = {
|
|
static struct inode_operations shmem_inode_operations = {
|
|
static struct inode_operations shmem_inode_operations = {
|
|
.truncate = shmem_truncate,
|
|
.truncate = shmem_truncate,
|
|
.setattr = shmem_notify_change,
|
|
.setattr = shmem_notify_change,
|
|
|
|
+ .truncate_range = shmem_truncate_range,
|
|
};
|
|
};
|
|
|
|
|
|
static struct inode_operations shmem_dir_inode_operations = {
|
|
static struct inode_operations shmem_dir_inode_operations = {
|