linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 01/27] afs: Fix missing put_page()
@ 2017-03-09 18:55 David Howells
  2017-03-09 18:55 ` [PATCH 02/27] afs: Fix page overput in afs_fill_page() David Howells
                   ` (25 more replies)
  0 siblings, 26 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:55 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, linux-afs, linux-kernel

In afs_writepages_region(), inside the loop where we find dirty pages to
deal with, one of the if-statements is missing a put_page().

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/write.c |    1 +
 1 file changed, 1 insertion(+)

diff --git a/fs/afs/write.c b/fs/afs/write.c
index c83c1a0e851f..e919e64cd4e0 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -513,6 +513,7 @@ static int afs_writepages_region(struct address_space *mapping,
 
 		if (PageWriteback(page) || !PageDirty(page)) {
 			unlock_page(page);
+			put_page(page);
 			continue;
 		}
 

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 02/27] afs: Fix page overput in afs_fill_page()
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
@ 2017-03-09 18:55 ` David Howells
  2017-03-09 18:56 ` [PATCH 03/27] afs: Populate group ID from vnode status David Howells
                   ` (24 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:55 UTC (permalink / raw)
  To: viro; +Cc: Marc Dionne, dhowells, linux-fsdevel, linux-afs, linux-kernel

afs_fill_page() loads the page it wants to fill into the afs_read request
without incrementing its refcount - but then calls afs_put_read() to clean
up afterwards, which then releases a ref on the page.

Fix this by getting a ref on the page before calling
afs_vnode_fetch_data().

This causes sync after a write to hang in afs_writepages_region() because
find_get_pages_tag() gets confused and doesn't return.

Fixes: 196ee9cd2d04 ("afs: Make afs_fs_fetch_data() take a list of pages")
Reported-by: Marc Dionne <marc.dionne@auristor.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Marc Dionne <marc.dionne@auristor.com>
---

 fs/afs/write.c |    1 +
 1 file changed, 1 insertion(+)

diff --git a/fs/afs/write.c b/fs/afs/write.c
index e919e64cd4e0..3ac52f6a96ff 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -101,6 +101,7 @@ static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
 	req->pos = pos;
 	req->nr_pages = 1;
 	req->pages[0] = page;
+	get_page(page);
 
 	i_size = i_size_read(&vnode->vfs_inode);
 	if (pos + PAGE_SIZE > i_size)

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 03/27] afs: Populate group ID from vnode status
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
  2017-03-09 18:55 ` [PATCH 02/27] afs: Fix page overput in afs_fill_page() David Howells
@ 2017-03-09 18:56 ` David Howells
  2017-03-09 18:56 ` [PATCH 04/27] afs: Adjust mode bits processing David Howells
                   ` (23 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:56 UTC (permalink / raw)
  To: viro; +Cc: Marc Dionne, dhowells, linux-fsdevel, linux-afs, linux-kernel

From: Marc Dionne <marc.dionne@auristor.com>

The group was hard coded to GLOBAL_ROOT_GID; use the group
ID that was received from the server.

Signed-off-by: Marc Dionne <marc.dionne@auristor.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/inode.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index 1e4897a048d2..299dbaeb2e2a 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -70,7 +70,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
 
 	set_nlink(inode, vnode->status.nlink);
 	inode->i_uid		= vnode->status.owner;
-	inode->i_gid		= GLOBAL_ROOT_GID;
+	inode->i_gid            = vnode->status.group;
 	inode->i_size		= vnode->status.size;
 	inode->i_ctime.tv_sec	= vnode->status.mtime_server;
 	inode->i_ctime.tv_nsec	= 0;

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 04/27] afs: Adjust mode bits processing
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
  2017-03-09 18:55 ` [PATCH 02/27] afs: Fix page overput in afs_fill_page() David Howells
  2017-03-09 18:56 ` [PATCH 03/27] afs: Populate group ID from vnode status David Howells
@ 2017-03-09 18:56 ` David Howells
  2017-03-09 18:56 ` [PATCH 05/27] afs: Deal with an empty callback array David Howells
                   ` (22 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:56 UTC (permalink / raw)
  To: viro; +Cc: Marc Dionne, dhowells, linux-fsdevel, linux-afs, linux-kernel

From: Marc Dionne <marc.dionne@auristor.com>

Mode bits for an afs file should not be enforced in the usual
way.

For files, the absence of user bits can restrict file access
with respect to what is granted by the server.

These bits apply regardless of the owner or the current uid; the
rest of the mode bits (group, other) are ignored.

Signed-off-by: Marc Dionne <marc.dionne@auristor.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/security.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/fs/afs/security.c b/fs/afs/security.c
index 8d010422dc89..bfa9d3428383 100644
--- a/fs/afs/security.c
+++ b/fs/afs/security.c
@@ -340,17 +340,22 @@ int afs_permission(struct inode *inode, int mask)
 	} else {
 		if (!(access & AFS_ACE_LOOKUP))
 			goto permission_denied;
+		if ((mask & MAY_EXEC) && !(inode->i_mode & S_IXUSR))
+			goto permission_denied;
 		if (mask & (MAY_EXEC | MAY_READ)) {
 			if (!(access & AFS_ACE_READ))
 				goto permission_denied;
+			if (!(inode->i_mode & S_IRUSR))
+				goto permission_denied;
 		} else if (mask & MAY_WRITE) {
 			if (!(access & AFS_ACE_WRITE))
 				goto permission_denied;
+			if (!(inode->i_mode & S_IWUSR))
+				goto permission_denied;
 		}
 	}
 
 	key_put(key);
-	ret = generic_permission(inode, mask);
 	_leave(" = %d", ret);
 	return ret;
 

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 05/27] afs: Deal with an empty callback array
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (2 preceding siblings ...)
  2017-03-09 18:56 ` [PATCH 04/27] afs: Adjust mode bits processing David Howells
@ 2017-03-09 18:56 ` David Howells
  2017-03-09 18:56 ` [PATCH 06/27] afs: Handle better the server returning excess or short data David Howells
                   ` (21 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:56 UTC (permalink / raw)
  To: viro; +Cc: Marc Dionne, dhowells, linux-fsdevel, linux-afs, linux-kernel

From: Marc Dionne <marc.dionne@auristor.com>

Servers may send a callback array that is the same size as
the FID array, or an empty array.  If the callback count is
0, the code would attempt to read (fid_count * 12) bytes of
data, which would fail and result in an unmarshalling error.
This would lead to stale data for remotely modified files
or directories.

Store the callback array size in the internal afs_call
structure and use that to determine the amount of data to
read.

Signed-off-by: Marc Dionne <marc.dionne@auristor.com>
---

 fs/afs/cmservice.c |   11 +++++------
 fs/afs/internal.h  |    5 ++++-
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c
index 2edbdcbf6432..3062cceb5c2a 100644
--- a/fs/afs/cmservice.c
+++ b/fs/afs/cmservice.c
@@ -187,7 +187,6 @@ static int afs_deliver_cb_callback(struct afs_call *call)
 	struct afs_callback *cb;
 	struct afs_server *server;
 	__be32 *bp;
-	u32 tmp;
 	int ret, loop;
 
 	_enter("{%u}", call->unmarshall);
@@ -249,9 +248,9 @@ static int afs_deliver_cb_callback(struct afs_call *call)
 		if (ret < 0)
 			return ret;
 
-		tmp = ntohl(call->tmp);
-		_debug("CB count: %u", tmp);
-		if (tmp != call->count && tmp != 0)
+		call->count2 = ntohl(call->tmp);
+		_debug("CB count: %u", call->count2);
+		if (call->count2 != call->count && call->count2 != 0)
 			return -EBADMSG;
 		call->offset = 0;
 		call->unmarshall++;
@@ -259,14 +258,14 @@ static int afs_deliver_cb_callback(struct afs_call *call)
 	case 4:
 		_debug("extract CB array");
 		ret = afs_extract_data(call, call->buffer,
-				       call->count * 3 * 4, false);
+				       call->count2 * 3 * 4, false);
 		if (ret < 0)
 			return ret;
 
 		_debug("unmarshall CB array");
 		cb = call->request;
 		bp = call->buffer;
-		for (loop = call->count; loop > 0; loop--, cb++) {
+		for (loop = call->count2; loop > 0; loop--, cb++) {
 			cb->version	= ntohl(*bp++);
 			cb->expiry	= ntohl(*bp++);
 			cb->type	= ntohl(*bp++);
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 5dfa56903a2d..8499870147ef 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -90,7 +90,10 @@ struct afs_call {
 	unsigned		request_size;	/* size of request data */
 	unsigned		reply_max;	/* maximum size of reply */
 	unsigned		first_offset;	/* offset into mapping[first] */
-	unsigned		last_to;	/* amount of mapping[last] */
+	union {
+		unsigned	last_to;	/* amount of mapping[last] */
+		unsigned	count2;		/* count used in unmarshalling */
+	};
 	unsigned char		unmarshall;	/* unmarshalling phase */
 	bool			incoming;	/* T if incoming call */
 	bool			send_pages;	/* T if data from mapping should be sent */

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 06/27] afs: Handle better the server returning excess or short data
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (3 preceding siblings ...)
  2017-03-09 18:56 ` [PATCH 05/27] afs: Deal with an empty callback array David Howells
@ 2017-03-09 18:56 ` David Howells
  2017-03-09 18:56 ` [PATCH 07/27] afs: Kill struct afs_read::pg_offset David Howells
                   ` (20 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:56 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, linux-afs, linux-kernel

When an AFS server is given an FS.FetchData{,64} request to read data from
a file, it is permitted by the protocol to return more or less than was
requested.  kafs currently relies on the latter behaviour in readpage{,s}
to handle a partial page at the end of the file (we just ask for a whole
page and clear space beyond the short read).

However, we don't handle all cases.  Add:

 (1) Handle excess data by discarding it rather than aborting.  Note that
     we use a common static buffer to discard into so that the decryption
     algorithm advances the PCBC state.

 (2) Handle a short read that affects more than just the last page.

Note that if a read comes up unexpectedly short of long, it's possible that
the server's copy of the file changed - in which case the data version
number will have been incremented and the callback will have been broken -
in which case all the pages currently attached to the inode will be zapped
anyway at some point.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/file.c     |    7 +++++--
 fs/afs/fsclient.c |   49 +++++++++++++++++++++++++++++++++++--------------
 2 files changed, 40 insertions(+), 16 deletions(-)

diff --git a/fs/afs/file.c b/fs/afs/file.c
index ba7b71fba34b..a38e1c30d110 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -184,10 +184,13 @@ int afs_page_filler(void *data, struct page *page)
 		if (!req)
 			goto enomem;
 
+		/* We request a full page.  If the page is a partial one at the
+		 * end of the file, the server will return a short read and the
+		 * unmarshalling code will clear the unfilled space.
+		 */
 		atomic_set(&req->usage, 1);
 		req->pos = (loff_t)page->index << PAGE_SHIFT;
-		req->len = min_t(size_t, i_size_read(inode) - req->pos,
-				 PAGE_SIZE);
+		req->len = PAGE_SIZE;
 		req->nr_pages = 1;
 		req->pages[0] = page;
 		get_page(page);
diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index ac8e766978dc..bf8904a1a58f 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -17,6 +17,12 @@
 #include "afs_fs.h"
 
 /*
+ * We need somewhere to discard into in case the server helpfully returns more
+ * than we asked for in FS.FetchData{,64}.
+ */
+static u8 afs_discard_buffer[64];
+
+/*
  * decode an AFSFid block
  */
 static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid)
@@ -353,12 +359,6 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
 
 		req->actual_len |= ntohl(call->tmp);
 		_debug("DATA length: %llu", req->actual_len);
-		/* Check that the server didn't want to send us extra.  We
-		 * might want to just discard instead, but that requires
-		 * cooperation from AF_RXRPC.
-		 */
-		if (req->actual_len > req->len)
-			return -EBADMSG;
 
 		req->remain = req->actual_len;
 		call->offset = req->pos & (PAGE_SIZE - 1);
@@ -368,6 +368,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
 		call->unmarshall++;
 
 	begin_page:
+		ASSERTCMP(req->index, <, req->nr_pages);
 		if (req->remain > PAGE_SIZE - call->offset)
 			size = PAGE_SIZE - call->offset;
 		else
@@ -390,18 +391,37 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
 			if (req->page_done)
 				req->page_done(call, req);
 			if (req->remain > 0) {
-				req->index++;
 				call->offset = 0;
+				req->index++;
+				if (req->index >= req->nr_pages)
+					goto begin_discard;
 				goto begin_page;
 			}
 		}
+		goto no_more_data;
+
+		/* Discard any excess data the server gave us */
+	begin_discard:
+	case 4:
+		size = min_t(size_t, sizeof(afs_discard_buffer), req->remain);
+		call->count = size;
+		_debug("extract discard %u/%llu %zu/%u",
+		       req->remain, req->actual_len, call->offset, call->count);
+
+		call->offset = 0;
+		ret = afs_extract_data(call, afs_discard_buffer, call->count, true);
+		req->remain -= call->offset;
+		if (ret < 0)
+			return ret;
+		if (req->remain > 0)
+			goto begin_discard;
 
 	no_more_data:
 		call->offset = 0;
-		call->unmarshall++;
+		call->unmarshall = 5;
 
 		/* extract the metadata */
-	case 4:
+	case 5:
 		ret = afs_extract_data(call, call->buffer,
 				       (21 + 3 + 6) * 4, false);
 		if (ret < 0)
@@ -416,16 +436,17 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
 		call->offset = 0;
 		call->unmarshall++;
 
-	case 5:
+	case 6:
 		break;
 	}
 
-	if (call->count < PAGE_SIZE) {
-		buffer = kmap(req->pages[req->index]);
-		memset(buffer + call->count, 0, PAGE_SIZE - call->count);
-		kunmap(req->pages[req->index]);
+	for (; req->index < req->nr_pages; req->index++) {
+		if (call->count < PAGE_SIZE)
+			zero_user_segment(req->pages[req->index],
+					  call->count, PAGE_SIZE);
 		if (req->page_done)
 			req->page_done(call, req);
+		call->count = 0;
 	}
 
 	_leave(" = 0 [done]");

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 07/27] afs: Kill struct afs_read::pg_offset
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (4 preceding siblings ...)
  2017-03-09 18:56 ` [PATCH 06/27] afs: Handle better the server returning excess or short data David Howells
@ 2017-03-09 18:56 ` David Howells
  2017-03-09 18:56 ` [PATCH 08/27] afs: Handle a short write to an AFS page David Howells
                   ` (19 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:56 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, linux-afs, linux-kernel

Kill struct afs_read::pg_offset as nothing uses it.  It's unnecessary as pos
can be masked off.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/internal.h |    1 -
 1 file changed, 1 deletion(-)

diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 8499870147ef..7784a8bc375c 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -135,7 +135,6 @@ struct afs_read {
 	atomic_t		usage;
 	unsigned int		remain;		/* Amount remaining */
 	unsigned int		index;		/* Which page we're reading into */
-	unsigned int		pg_offset;	/* Offset in page we're at */
 	unsigned int		nr_pages;
 	void (*page_done)(struct afs_call *, struct afs_read *);
 	struct page		*pages[];

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 08/27] afs: Handle a short write to an AFS page
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (5 preceding siblings ...)
  2017-03-09 18:56 ` [PATCH 07/27] afs: Kill struct afs_read::pg_offset David Howells
@ 2017-03-09 18:56 ` David Howells
  2017-03-09 18:56 ` [PATCH 09/27] afs: Flush outstanding writes when an fd is closed David Howells
                   ` (18 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:56 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, linux-afs, linux-kernel

Handle the situation where afs_write_begin() is told to expect that a
full-page write will be made, but this doesn't happen (EFAULT, CTRL-C,
etc.), and so afs_write_end() sees a partial write took place.  Currently,
no attempt is to deal with the discrepency.

Fix this by loading the gap from the server.

Reported-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/fsclient.c |    4 +++-
 fs/afs/internal.h |    2 +-
 fs/afs/write.c    |   28 +++++++++++++++++++---------
 3 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index bf8904a1a58f..6f917dd1238c 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -393,8 +393,10 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
 			if (req->remain > 0) {
 				call->offset = 0;
 				req->index++;
-				if (req->index >= req->nr_pages)
+				if (req->index >= req->nr_pages) {
+					call->unmarshall = 4;
 					goto begin_discard;
+				}
 				goto begin_page;
 			}
 		}
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 7784a8bc375c..dc2cb486e127 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -130,7 +130,7 @@ struct afs_call_type {
  */
 struct afs_read {
 	loff_t			pos;		/* Where to start reading */
-	loff_t			len;		/* How much to read */
+	loff_t			len;		/* How much we're asking for */
 	loff_t			actual_len;	/* How much we're actually getting */
 	atomic_t		usage;
 	unsigned int		remain;		/* Amount remaining */
diff --git a/fs/afs/write.c b/fs/afs/write.c
index 3ac52f6a96ff..ea66890fc188 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -84,10 +84,9 @@ void afs_put_writeback(struct afs_writeback *wb)
  * partly or wholly fill a page that's under preparation for writing
  */
 static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
-			 loff_t pos, struct page *page)
+			 loff_t pos, unsigned int len, struct page *page)
 {
 	struct afs_read *req;
-	loff_t i_size;
 	int ret;
 
 	_enter(",,%llu", (unsigned long long)pos);
@@ -99,16 +98,11 @@ static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
 
 	atomic_set(&req->usage, 1);
 	req->pos = pos;
+	req->len = len;
 	req->nr_pages = 1;
 	req->pages[0] = page;
 	get_page(page);
 
-	i_size = i_size_read(&vnode->vfs_inode);
-	if (pos + PAGE_SIZE > i_size)
-		req->len = i_size - pos;
-	else
-		req->len = PAGE_SIZE;
-
 	ret = afs_vnode_fetch_data(vnode, key, req);
 	afs_put_read(req);
 	if (ret < 0) {
@@ -164,7 +158,7 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
 	/* page won't leak in error case: it eventually gets cleaned off LRU */
 
 	if (!PageUptodate(page) && len != PAGE_SIZE) {
-		ret = afs_fill_page(vnode, key, index << PAGE_SHIFT, page);
+		ret = afs_fill_page(vnode, key, pos & PAGE_MASK, PAGE_SIZE, page);
 		if (ret < 0) {
 			kfree(candidate);
 			_leave(" = %d [prep]", ret);
@@ -258,7 +252,9 @@ int afs_write_end(struct file *file, struct address_space *mapping,
 		  struct page *page, void *fsdata)
 {
 	struct afs_vnode *vnode = AFS_FS_I(file_inode(file));
+	struct key *key = file->private_data;
 	loff_t i_size, maybe_i_size;
+	int ret;
 
 	_enter("{%x:%u},{%lx}",
 	       vnode->fid.vid, vnode->fid.vnode, page->index);
@@ -274,6 +270,20 @@ int afs_write_end(struct file *file, struct address_space *mapping,
 		spin_unlock(&vnode->writeback_lock);
 	}
 
+	if (!PageUptodate(page)) {
+		if (copied < len) {
+			/* Try and load any missing data from the server.  The
+			 * unmarshalling routine will take care of clearing any
+			 * bits that are beyond the EOF.
+			 */
+			ret = afs_fill_page(vnode, key, pos + copied,
+					    len - copied, page);
+			if (ret < 0)
+				return ret;
+		}
+		SetPageUptodate(page);
+	}
+
 	set_page_dirty(page);
 	if (PageDirty(page))
 		_debug("dirtied");

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 09/27] afs: Flush outstanding writes when an fd is closed
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (6 preceding siblings ...)
  2017-03-09 18:56 ` [PATCH 08/27] afs: Handle a short write to an AFS page David Howells
@ 2017-03-09 18:56 ` David Howells
  2017-03-09 18:56 ` [PATCH 10/27] afs: Distinguish mountpoints from symlinks by file mode alone David Howells
                   ` (17 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:56 UTC (permalink / raw)
  To: viro; +Cc: dhowells, Marc Dionne, linux-fsdevel, linux-afs, linux-kernel

Flush outstanding writes in afs when an fd is closed.  This is what NFS and
CIFS do.

Reported-by: Marc Dionne <marc.c.dionne@gmail.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/file.c     |    1 +
 fs/afs/internal.h |    1 +
 fs/afs/write.c    |   14 ++++++++++++++
 3 files changed, 16 insertions(+)

diff --git a/fs/afs/file.c b/fs/afs/file.c
index a38e1c30d110..b5829443ff69 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -30,6 +30,7 @@ static int afs_readpages(struct file *filp, struct address_space *mapping,
 
 const struct file_operations afs_file_operations = {
 	.open		= afs_open,
+	.flush		= afs_flush,
 	.release	= afs_release,
 	.llseek		= generic_file_llseek,
 	.read_iter	= generic_file_read_iter,
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index dc2cb486e127..af1d91ec7f2c 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -720,6 +720,7 @@ extern int afs_writepages(struct address_space *, struct writeback_control *);
 extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *);
 extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *);
 extern int afs_writeback_all(struct afs_vnode *);
+extern int afs_flush(struct file *, fl_owner_t);
 extern int afs_fsync(struct file *, loff_t, loff_t, int);
 
 
diff --git a/fs/afs/write.c b/fs/afs/write.c
index ea66890fc188..f1450ea09406 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -758,6 +758,20 @@ int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 }
 
 /*
+ * Flush out all outstanding writes on a file opened for writing when it is
+ * closed.
+ */
+int afs_flush(struct file *file, fl_owner_t id)
+{
+	_enter("");
+
+	if ((file->f_mode & FMODE_WRITE) == 0)
+		return 0;
+
+	return vfs_fsync(file, 0);
+}
+
+/*
  * notification that a previously read-only page is about to become writable
  * - if it returns an error, the caller will deliver a bus error signal
  */

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 10/27] afs: Distinguish mountpoints from symlinks by file mode alone
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (7 preceding siblings ...)
  2017-03-09 18:56 ` [PATCH 09/27] afs: Flush outstanding writes when an fd is closed David Howells
@ 2017-03-09 18:56 ` David Howells
  2017-03-09 18:57 ` [PATCH 11/27] afs: inode: Replace rcu_assign_pointer() with RCU_INIT_POINTER() David Howells
                   ` (16 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:56 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, Jeffrey Altman, linux-kernel, linux-afs

In AFS, mountpoints appear as symlinks with mode 0644 and normal symlinks
have mode 0777, so use this to distinguish them rather than reading the
content and parsing it.  In the case of a mountpoint, the symlink body is a
formatted string indicating the location of the target volume.

Note that with this, kAFS no longer 'pre-fetches' the contents of symlinks,
so afs_readpage() may fail with an access-denial because when the VFS calls
d_automount(), it wraps the call in an credentials override that sets the
initial creds - thereby preventing access to the caller's keyrings and the
authentication keys held therein.

To this end, a patch reverting that change to the VFS is required also.

Reported-by: Jeffrey Altman <jaltman@auristor.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/inode.c    |   29 +++++++++++++++--------------
 fs/afs/internal.h |    1 -
 fs/afs/mntpt.c    |   53 -----------------------------------------------------
 3 files changed, 15 insertions(+), 68 deletions(-)

diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index 299dbaeb2e2a..ade6ec3873cf 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -54,8 +54,21 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
 		inode->i_fop	= &afs_dir_file_operations;
 		break;
 	case AFS_FTYPE_SYMLINK:
-		inode->i_mode	= S_IFLNK | vnode->status.mode;
-		inode->i_op	= &page_symlink_inode_operations;
+		/* Symlinks with a mode of 0644 are actually mountpoints. */
+		if ((vnode->status.mode & 0777) == 0644) {
+			inode->i_flags |= S_AUTOMOUNT;
+
+			spin_lock(&vnode->lock);
+			set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
+			spin_unlock(&vnode->lock);
+
+			inode->i_mode	= S_IFDIR | 0555;
+			inode->i_op	= &afs_mntpt_inode_operations;
+			inode->i_fop	= &afs_mntpt_file_operations;
+		} else {
+			inode->i_mode	= S_IFLNK | vnode->status.mode;
+			inode->i_op	= &page_symlink_inode_operations;
+		}
 		inode_nohighmem(inode);
 		break;
 	default:
@@ -79,18 +92,6 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
 	inode->i_generation	= vnode->fid.unique;
 	inode->i_version	= vnode->status.data_version;
 	inode->i_mapping->a_ops	= &afs_fs_aops;
-
-	/* check to see whether a symbolic link is really a mountpoint */
-	if (vnode->status.type == AFS_FTYPE_SYMLINK) {
-		afs_mntpt_check_symlink(vnode, key);
-
-		if (test_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags)) {
-			inode->i_mode	= S_IFDIR | vnode->status.mode;
-			inode->i_op	= &afs_mntpt_inode_operations;
-			inode->i_fop	= &afs_mntpt_file_operations;
-		}
-	}
-
 	return 0;
 }
 
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index af1d91ec7f2c..39de154fb42e 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -559,7 +559,6 @@ extern const struct inode_operations afs_autocell_inode_operations;
 extern const struct file_operations afs_mntpt_file_operations;
 
 extern struct vfsmount *afs_d_automount(struct path *);
-extern int afs_mntpt_check_symlink(struct afs_vnode *, struct key *);
 extern void afs_mntpt_kill_timer(void);
 
 /*
diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c
index d4fb0afc0097..bd3b65cde282 100644
--- a/fs/afs/mntpt.c
+++ b/fs/afs/mntpt.c
@@ -47,59 +47,6 @@ static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out);
 static unsigned long afs_mntpt_expiry_timeout = 10 * 60;
 
 /*
- * check a symbolic link to see whether it actually encodes a mountpoint
- * - sets the AFS_VNODE_MOUNTPOINT flag on the vnode appropriately
- */
-int afs_mntpt_check_symlink(struct afs_vnode *vnode, struct key *key)
-{
-	struct page *page;
-	size_t size;
-	char *buf;
-	int ret;
-
-	_enter("{%x:%u,%u}",
-	       vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
-
-	/* read the contents of the symlink into the pagecache */
-	page = read_cache_page(AFS_VNODE_TO_I(vnode)->i_mapping, 0,
-			       afs_page_filler, key);
-	if (IS_ERR(page)) {
-		ret = PTR_ERR(page);
-		goto out;
-	}
-
-	ret = -EIO;
-	if (PageError(page))
-		goto out_free;
-
-	buf = kmap(page);
-
-	/* examine the symlink's contents */
-	size = vnode->status.size;
-	_debug("symlink to %*.*s", (int) size, (int) size, buf);
-
-	if (size > 2 &&
-	    (buf[0] == '%' || buf[0] == '#') &&
-	    buf[size - 1] == '.'
-	    ) {
-		_debug("symlink is a mountpoint");
-		spin_lock(&vnode->lock);
-		set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
-		vnode->vfs_inode.i_flags |= S_AUTOMOUNT;
-		spin_unlock(&vnode->lock);
-	}
-
-	ret = 0;
-
-	kunmap(page);
-out_free:
-	put_page(page);
-out:
-	_leave(" = %d", ret);
-	return ret;
-}
-
-/*
  * no valid lookup procedure on this sort of dir
  */
 static struct dentry *afs_mntpt_lookup(struct inode *dir,

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 11/27] afs: inode: Replace rcu_assign_pointer() with RCU_INIT_POINTER()
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (8 preceding siblings ...)
  2017-03-09 18:56 ` [PATCH 10/27] afs: Distinguish mountpoints from symlinks by file mode alone David Howells
@ 2017-03-09 18:57 ` David Howells
  2017-03-09 18:57 ` [PATCH 12/27] afs: security: " David Howells
                   ` (15 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:57 UTC (permalink / raw)
  To: viro
  Cc: dhowells, Andreea-Cristina Bernat, linux-fsdevel, linux-afs,
	linux-kernel

From: Andreea-Cristina Bernat <bernat.ada@gmail.com>

The use of "rcu_assign_pointer()" is NULLing out the pointer.
According to RCU_INIT_POINTER()'s block comment:
"1.   This use of RCU_INIT_POINTER() is NULLing out the pointer"
it is better to use it instead of rcu_assign_pointer() because it has a
smaller overhead.

The following Coccinelle semantic patch was used:
@@
@@

- rcu_assign_pointer
+ RCU_INIT_POINTER
  (..., NULL)

Signed-off-by: Andreea-Cristina Bernat <bernat.ada@gmail.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/inode.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index ade6ec3873cf..e083e086b7ca 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -445,7 +445,7 @@ void afs_evict_inode(struct inode *inode)
 
 	mutex_lock(&vnode->permits_lock);
 	permits = vnode->permits;
-	rcu_assign_pointer(vnode->permits, NULL);
+	RCU_INIT_POINTER(vnode->permits, NULL);
 	mutex_unlock(&vnode->permits_lock);
 	if (permits)
 		call_rcu(&permits->rcu, afs_zap_permits);

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 12/27] afs: security: Replace rcu_assign_pointer() with RCU_INIT_POINTER()
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (9 preceding siblings ...)
  2017-03-09 18:57 ` [PATCH 11/27] afs: inode: Replace rcu_assign_pointer() with RCU_INIT_POINTER() David Howells
@ 2017-03-09 18:57 ` David Howells
  2017-03-09 18:57 ` [PATCH 13/27] afs: Migrate vlocation fields to 64-bit David Howells
                   ` (14 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:57 UTC (permalink / raw)
  To: viro
  Cc: dhowells, Andreea-Cristina Bernat, linux-fsdevel, linux-afs,
	linux-kernel

From: Andreea-Cristina Bernat <bernat.ada@gmail.com>

The use of "rcu_assign_pointer()" is NULLing out the pointer.
According to RCU_INIT_POINTER()'s block comment:
"1.   This use of RCU_INIT_POINTER() is NULLing out the pointer"
it is better to use it instead of rcu_assign_pointer() because it has a
smaller overhead.

The following Coccinelle semantic patch was used:
@@
@@

- rcu_assign_pointer
+ RCU_INIT_POINTER
  (..., NULL)

Signed-off-by: Andreea-Cristina Bernat <bernat.ada@gmail.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/security.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/afs/security.c b/fs/afs/security.c
index bfa9d3428383..ecb86a670180 100644
--- a/fs/afs/security.c
+++ b/fs/afs/security.c
@@ -114,7 +114,7 @@ void afs_clear_permits(struct afs_vnode *vnode)
 
 	mutex_lock(&vnode->permits_lock);
 	permits = vnode->permits;
-	rcu_assign_pointer(vnode->permits, NULL);
+	RCU_INIT_POINTER(vnode->permits, NULL);
 	mutex_unlock(&vnode->permits_lock);
 
 	if (permits)

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 13/27] afs: Migrate vlocation fields to 64-bit
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (10 preceding siblings ...)
  2017-03-09 18:57 ` [PATCH 12/27] afs: security: " David Howells
@ 2017-03-09 18:57 ` David Howells
  2017-03-09 18:57 ` [PATCH 14/27] afs: Prevent callback expiry timer overflow David Howells
                   ` (13 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:57 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, Tina Ruchandani, linux-kernel, linux-afs

From: Tina Ruchandani <ruchandani.tina@gmail.com>

get_seconds() returns real wall-clock seconds. On 32-bit systems
this value will overflow in year 2038 and beyond. This patch changes
afs's vlocation record to use ktime_get_real_seconds() instead, for the
fields time_of_death and update_at.

Signed-off-by: Tina Ruchandani <ruchandani.tina@gmail.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/callback.c  |    7 ++++---
 fs/afs/internal.h  |    7 ++++---
 fs/afs/server.c    |    6 +++---
 fs/afs/vlocation.c |   16 +++++++++-------
 4 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/fs/afs/callback.c b/fs/afs/callback.c
index b29447e03ede..25d404d22cae 100644
--- a/fs/afs/callback.c
+++ b/fs/afs/callback.c
@@ -362,7 +362,7 @@ static void afs_callback_updater(struct work_struct *work)
 {
 	struct afs_server *server;
 	struct afs_vnode *vnode, *xvnode;
-	time_t now;
+	time64_t now;
 	long timeout;
 	int ret;
 
@@ -370,7 +370,7 @@ static void afs_callback_updater(struct work_struct *work)
 
 	_enter("");
 
-	now = get_seconds();
+	now = ktime_get_real_seconds();
 
 	/* find the first vnode to update */
 	spin_lock(&server->cb_lock);
@@ -424,7 +424,8 @@ static void afs_callback_updater(struct work_struct *work)
 
 	/* and then reschedule */
 	_debug("reschedule");
-	vnode->update_at = get_seconds() + afs_vnode_update_timeout;
+	vnode->update_at = ktime_get_real_seconds() +
+			afs_vnode_update_timeout;
 
 	spin_lock(&server->cb_lock);
 
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 39de154fb42e..97a16ce200be 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -11,6 +11,7 @@
 
 #include <linux/compiler.h>
 #include <linux/kernel.h>
+#include <linux/ktime.h>
 #include <linux/fs.h>
 #include <linux/pagemap.h>
 #include <linux/rxrpc.h>
@@ -249,7 +250,7 @@ struct afs_cache_vhash {
  */
 struct afs_vlocation {
 	atomic_t		usage;
-	time_t			time_of_death;	/* time at which put reduced usage to 0 */
+	time64_t		time_of_death;	/* time at which put reduced usage to 0 */
 	struct list_head	link;		/* link in cell volume location list */
 	struct list_head	grave;		/* link in master graveyard list */
 	struct list_head	update;		/* link in master update list */
@@ -260,7 +261,7 @@ struct afs_vlocation {
 	struct afs_cache_vlocation vldb;	/* volume information DB record */
 	struct afs_volume	*vols[3];	/* volume access record pointer (index by type) */
 	wait_queue_head_t	waitq;		/* status change waitqueue */
-	time_t			update_at;	/* time at which record should be updated */
+	time64_t		update_at;	/* time at which record should be updated */
 	spinlock_t		lock;		/* access lock */
 	afs_vlocation_state_t	state;		/* volume location state */
 	unsigned short		upd_rej_cnt;	/* ENOMEDIUM count during update */
@@ -273,7 +274,7 @@ struct afs_vlocation {
  */
 struct afs_server {
 	atomic_t		usage;
-	time_t			time_of_death;	/* time at which put reduced usage to 0 */
+	time64_t		time_of_death;	/* time at which put reduced usage to 0 */
 	struct in_addr		addr;		/* server address */
 	struct afs_cell		*cell;		/* cell in which server resides */
 	struct list_head	link;		/* link in cell's server list */
diff --git a/fs/afs/server.c b/fs/afs/server.c
index d4066ab7dd55..c001b1f2455f 100644
--- a/fs/afs/server.c
+++ b/fs/afs/server.c
@@ -242,7 +242,7 @@ void afs_put_server(struct afs_server *server)
 	spin_lock(&afs_server_graveyard_lock);
 	if (atomic_read(&server->usage) == 0) {
 		list_move_tail(&server->grave, &afs_server_graveyard);
-		server->time_of_death = get_seconds();
+		server->time_of_death = ktime_get_real_seconds();
 		queue_delayed_work(afs_wq, &afs_server_reaper,
 				   afs_server_timeout * HZ);
 	}
@@ -277,9 +277,9 @@ static void afs_reap_server(struct work_struct *work)
 	LIST_HEAD(corpses);
 	struct afs_server *server;
 	unsigned long delay, expiry;
-	time_t now;
+	time64_t now;
 
-	now = get_seconds();
+	now = ktime_get_real_seconds();
 	spin_lock(&afs_server_graveyard_lock);
 
 	while (!list_empty(&afs_server_graveyard)) {
diff --git a/fs/afs/vlocation.c b/fs/afs/vlocation.c
index d7d8dd8c0b31..37b7c3b342a6 100644
--- a/fs/afs/vlocation.c
+++ b/fs/afs/vlocation.c
@@ -340,7 +340,8 @@ static void afs_vlocation_queue_for_updates(struct afs_vlocation *vl)
 	struct afs_vlocation *xvl;
 
 	/* wait at least 10 minutes before updating... */
-	vl->update_at = get_seconds() + afs_vlocation_update_timeout;
+	vl->update_at = ktime_get_real_seconds() +
+			afs_vlocation_update_timeout;
 
 	spin_lock(&afs_vlocation_updates_lock);
 
@@ -506,7 +507,7 @@ void afs_put_vlocation(struct afs_vlocation *vl)
 	if (atomic_read(&vl->usage) == 0) {
 		_debug("buried");
 		list_move_tail(&vl->grave, &afs_vlocation_graveyard);
-		vl->time_of_death = get_seconds();
+		vl->time_of_death = ktime_get_real_seconds();
 		queue_delayed_work(afs_wq, &afs_vlocation_reap,
 				   afs_vlocation_timeout * HZ);
 
@@ -543,11 +544,11 @@ static void afs_vlocation_reaper(struct work_struct *work)
 	LIST_HEAD(corpses);
 	struct afs_vlocation *vl;
 	unsigned long delay, expiry;
-	time_t now;
+	time64_t now;
 
 	_enter("");
 
-	now = get_seconds();
+	now = ktime_get_real_seconds();
 	spin_lock(&afs_vlocation_graveyard_lock);
 
 	while (!list_empty(&afs_vlocation_graveyard)) {
@@ -622,13 +623,13 @@ static void afs_vlocation_updater(struct work_struct *work)
 {
 	struct afs_cache_vlocation vldb;
 	struct afs_vlocation *vl, *xvl;
-	time_t now;
+	time64_t now;
 	long timeout;
 	int ret;
 
 	_enter("");
 
-	now = get_seconds();
+	now = ktime_get_real_seconds();
 
 	/* find a record to update */
 	spin_lock(&afs_vlocation_updates_lock);
@@ -684,7 +685,8 @@ static void afs_vlocation_updater(struct work_struct *work)
 
 	/* and then reschedule */
 	_debug("reschedule");
-	vl->update_at = get_seconds() + afs_vlocation_update_timeout;
+	vl->update_at = ktime_get_real_seconds() +
+			afs_vlocation_update_timeout;
 
 	spin_lock(&afs_vlocation_updates_lock);
 

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 14/27] afs: Prevent callback expiry timer overflow
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (11 preceding siblings ...)
  2017-03-09 18:57 ` [PATCH 13/27] afs: Migrate vlocation fields to 64-bit David Howells
@ 2017-03-09 18:57 ` David Howells
  2017-03-09 18:57 ` [PATCH 15/27] afs: Fix AFS read bug David Howells
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:57 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, Tina Ruchandani, linux-kernel, linux-afs

From: Tina Ruchandani <ruchandani.tina@gmail.com>

get_seconds() returns real wall-clock seconds. On 32-bit systems
this value will overflow in year 2038 and beyond. This patch changes
afs_vnode record to use ktime_get_real_seconds() instead, for the
fields cb_expires and cb_expires_at.

Signed-off-by: Tina Ruchandani <ruchandani.tina@gmail.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/fsclient.c |    2 +-
 fs/afs/inode.c    |    7 ++++---
 fs/afs/internal.h |    4 ++--
 3 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index 6f917dd1238c..c05452a09398 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -145,7 +145,7 @@ static void xdr_decode_AFSCallBack(const __be32 **_bp, struct afs_vnode *vnode)
 	vnode->cb_version	= ntohl(*bp++);
 	vnode->cb_expiry	= ntohl(*bp++);
 	vnode->cb_type		= ntohl(*bp++);
-	vnode->cb_expires	= vnode->cb_expiry + get_seconds();
+	vnode->cb_expires	= vnode->cb_expiry + ktime_get_real_seconds();
 	*_bp = bp;
 }
 
diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index e083e086b7ca..4079c832ff27 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -246,12 +246,13 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
 			vnode->cb_version = 0;
 			vnode->cb_expiry = 0;
 			vnode->cb_type = 0;
-			vnode->cb_expires = get_seconds();
+			vnode->cb_expires = ktime_get_real_seconds();
 		} else {
 			vnode->cb_version = cb->version;
 			vnode->cb_expiry = cb->expiry;
 			vnode->cb_type = cb->type;
-			vnode->cb_expires = vnode->cb_expiry + get_seconds();
+			vnode->cb_expires = vnode->cb_expiry +
+				ktime_get_real_seconds();
 		}
 	}
 
@@ -324,7 +325,7 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
 	    !test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
 	    !test_bit(AFS_VNODE_MODIFIED, &vnode->flags) &&
 	    !test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) {
-		if (vnode->cb_expires < get_seconds() + 10) {
+		if (vnode->cb_expires < ktime_get_real_seconds() + 10) {
 			_debug("callback expired");
 			set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
 		} else {
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 97a16ce200be..832555003d03 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -377,8 +377,8 @@ struct afs_vnode {
 	struct rb_node		server_rb;	/* link in server->fs_vnodes */
 	struct rb_node		cb_promise;	/* link in server->cb_promises */
 	struct work_struct	cb_broken_work;	/* work to be done on callback break */
-	time_t			cb_expires;	/* time at which callback expires */
-	time_t			cb_expires_at;	/* time used to order cb_promise */
+	time64_t		cb_expires;	/* time at which callback expires */
+	time64_t		cb_expires_at;	/* time used to order cb_promise */
 	unsigned		cb_version;	/* callback version */
 	unsigned		cb_expiry;	/* callback expiry time */
 	afs_callback_type_t	cb_type;	/* type of callback */

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 15/27] afs: Fix AFS read bug
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (12 preceding siblings ...)
  2017-03-09 18:57 ` [PATCH 14/27] afs: Prevent callback expiry timer overflow David Howells
@ 2017-03-09 18:57 ` David Howells
  2017-03-09 18:57 ` [PATCH 16/27] afs: Make struct afs_read::remain 64-bit David Howells
                   ` (11 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:57 UTC (permalink / raw)
  To: viro; +Cc: Marc Dionne, dhowells, linux-fsdevel, linux-afs, linux-kernel

Fix a bug in AFS read whereby the request page afs_read::index isn't
incremented after calling ->page_done() if ->remain reaches 0, indicating
that the data read is complete.

Without this a NULL pointer exception happens when ->page_done() is called
twice for the last page because the page clearing loop will call it also
and afs_readpages_page_done() clears the current entry in the page list.

BUG: unable to handle kernel NULL pointer dereference at           (null)
IP: afs_readpages_page_done+0x21/0xa4 [kafs]
PGD 0
Oops: 0002 [#1] SMP
Modules linked in: kafs(E)
CPU: 2 PID: 3002 Comm: md5sum Tainted: G            E   4.10.0-fscache #485
Hardware name: ASUS All Series/H97-PLUS, BIOS 2306 10/09/2014
task: ffff8804017d86c0 task.stack: ffff8803fc1d8000
RIP: 0010:afs_readpages_page_done+0x21/0xa4 [kafs]
RSP: 0018:ffff8803fc1db978 EFLAGS: 00010282
RAX: ffff880405d39af8 RBX: 0000000000000000 RCX: ffff880407d83ed4
RDX: 0000000000000000 RSI: ffff880405d39a00 RDI: ffff880405c6f400
RBP: ffff8803fc1db988 R08: 0000000000000000 R09: 0000000000000001
R10: ffff8803fc1db820 R11: ffff88040cf56000 R12: ffff8804088f1780
R13: ffff8804017d86c0 R14: ffff8804088f1780 R15: 0000000000003840
FS:  00007f8154469700(0000) GS:ffff88041fb00000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000000 CR3: 00000004016ec000 CR4: 00000000001406e0
Call Trace:
 afs_deliver_fs_fetch_data+0x5b9/0x60e [kafs]
 ? afs_make_call+0x316/0x4e8 [kafs]
 ? afs_make_call+0x359/0x4e8 [kafs]
 afs_deliver_to_call+0x173/0x2e8 [kafs]
 ? afs_make_call+0x316/0x4e8 [kafs]
 afs_make_call+0x37a/0x4e8 [kafs]
 ? wake_up_q+0x4f/0x4f
 ? __init_waitqueue_head+0x36/0x49
 afs_fs_fetch_data+0x21c/0x227 [kafs]
 ? afs_fs_fetch_data+0x21c/0x227 [kafs]
 afs_vnode_fetch_data+0xf3/0x1d2 [kafs]
 afs_readpages+0x314/0x3fd [kafs]
 __do_page_cache_readahead+0x208/0x2c5
 ondemand_readahead+0x3a2/0x3b7
 ? ondemand_readahead+0x3a2/0x3b7
 page_cache_async_readahead+0x5e/0x67
 generic_file_read_iter+0x23b/0x70c
 ? __inode_security_revalidate+0x2f/0x62
 __vfs_read+0xc4/0xe8
 vfs_read+0xd1/0x15a
 SyS_read+0x4c/0x89
 do_syscall_64+0x80/0x191
 entry_SYSCALL64_slow_path+0x25/0x25

Reported-by: Marc Dionne <marc.dionne@auristor.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Marc Dionne <marc.dionne@auristor.com>
---

 fs/afs/fsclient.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index c05452a09398..4314f9e63a2c 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -390,9 +390,9 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
 		if (call->offset == PAGE_SIZE) {
 			if (req->page_done)
 				req->page_done(call, req);
+			req->index++;
 			if (req->remain > 0) {
 				call->offset = 0;
-				req->index++;
 				if (req->index >= req->nr_pages) {
 					call->unmarshall = 4;
 					goto begin_discard;

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 16/27] afs: Make struct afs_read::remain 64-bit
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (13 preceding siblings ...)
  2017-03-09 18:57 ` [PATCH 15/27] afs: Fix AFS read bug David Howells
@ 2017-03-09 18:57 ` David Howells
  2017-03-09 18:57 ` [PATCH 17/27] afs: Use a bvec rather than a kvec in afs_send_pages() David Howells
                   ` (10 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:57 UTC (permalink / raw)
  To: viro; +Cc: Marc Dionne, dhowells, linux-fsdevel, linux-afs, linux-kernel

Make struct afs_read::remain 64-bit so that it can handle huge transfers if
we ever request them or the server decides to give us a bit extra data (the
other fields there are already 64-bit).

Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Marc Dionne <marc.dionne@auristor.com>
---

 fs/afs/fsclient.c |    8 ++++----
 fs/afs/internal.h |    2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index 4314f9e63a2c..0778c5b6b59b 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -321,7 +321,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
 	void *buffer;
 	int ret;
 
-	_enter("{%u,%zu/%u;%u/%llu}",
+	_enter("{%u,%zu/%u;%llu/%llu}",
 	       call->unmarshall, call->offset, call->count,
 	       req->remain, req->actual_len);
 
@@ -379,7 +379,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
 
 		/* extract the returned data */
 	case 3:
-		_debug("extract data %u/%llu %zu/%u",
+		_debug("extract data %llu/%llu %zu/%u",
 		       req->remain, req->actual_len, call->offset, call->count);
 
 		buffer = kmap(req->pages[req->index]);
@@ -405,9 +405,9 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
 		/* Discard any excess data the server gave us */
 	begin_discard:
 	case 4:
-		size = min_t(size_t, sizeof(afs_discard_buffer), req->remain);
+		size = min_t(loff_t, sizeof(afs_discard_buffer), req->remain);
 		call->count = size;
-		_debug("extract discard %u/%llu %zu/%u",
+		_debug("extract discard %llu/%llu %zu/%u",
 		       req->remain, req->actual_len, call->offset, call->count);
 
 		call->offset = 0;
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 832555003d03..a6901360fb81 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -133,8 +133,8 @@ struct afs_read {
 	loff_t			pos;		/* Where to start reading */
 	loff_t			len;		/* How much we're asking for */
 	loff_t			actual_len;	/* How much we're actually getting */
+	loff_t			remain;		/* Amount remaining */
 	atomic_t		usage;
-	unsigned int		remain;		/* Amount remaining */
 	unsigned int		index;		/* Which page we're reading into */
 	unsigned int		nr_pages;
 	void (*page_done)(struct afs_call *, struct afs_read *);

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 17/27] afs: Use a bvec rather than a kvec in afs_send_pages()
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (14 preceding siblings ...)
  2017-03-09 18:57 ` [PATCH 16/27] afs: Make struct afs_read::remain 64-bit David Howells
@ 2017-03-09 18:57 ` David Howells
  2017-03-09 18:57 ` [PATCH 18/27] afs: Fix the maths in afs_fs_store_data() David Howells
                   ` (9 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:57 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, linux-afs, linux-kernel

Use a bvec rather than a kvec in afs_send_pages() as we don't then have to
call kmap() in advance.  This allows us to pass the array of contiguous
pages that we extracted through to rxrpc in one go rather than passing a
single page at a time.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/rxrpc.c |   97 ++++++++++++++++++++++++++++++--------------------------
 1 file changed, 52 insertions(+), 45 deletions(-)

diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index 419ef05dcb5e..bf45307ff201 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -259,67 +259,74 @@ void afs_flat_call_destructor(struct afs_call *call)
 	call->buffer = NULL;
 }
 
+#define AFS_BVEC_MAX 8
+
+/*
+ * Load the given bvec with the next few pages.
+ */
+static void afs_load_bvec(struct afs_call *call, struct msghdr *msg,
+			  struct bio_vec *bv, pgoff_t first, pgoff_t last,
+			  unsigned offset)
+{
+	struct page *pages[AFS_BVEC_MAX];
+	unsigned int nr, n, i, to, bytes = 0;
+
+	nr = min_t(pgoff_t, last - first + 1, AFS_BVEC_MAX);
+	n = find_get_pages_contig(call->mapping, first, nr, pages);
+	ASSERTCMP(n, ==, nr);
+
+	msg->msg_flags |= MSG_MORE;
+	for (i = 0; i < nr; i++) {
+		to = PAGE_SIZE;
+		if (first + i >= last) {
+			to = call->last_to;
+			msg->msg_flags &= ~MSG_MORE;
+		}
+		bv[i].bv_page = pages[i];
+		bv[i].bv_len = to - offset;
+		bv[i].bv_offset = offset;
+		bytes += to - offset;
+		offset = 0;
+	}
+
+	iov_iter_bvec(&msg->msg_iter, WRITE | ITER_BVEC, bv, nr, bytes);
+}
+
 /*
  * attach the data from a bunch of pages on an inode to a call
  */
 static int afs_send_pages(struct afs_call *call, struct msghdr *msg)
 {
-	struct page *pages[8];
-	unsigned count, n, loop, offset, to;
+	struct bio_vec bv[AFS_BVEC_MAX];
+	unsigned int bytes, nr, loop, offset;
 	pgoff_t first = call->first, last = call->last;
 	int ret;
 
-	_enter("");
-
 	offset = call->first_offset;
 	call->first_offset = 0;
 
 	do {
-		_debug("attach %lx-%lx", first, last);
-
-		count = last - first + 1;
-		if (count > ARRAY_SIZE(pages))
-			count = ARRAY_SIZE(pages);
-		n = find_get_pages_contig(call->mapping, first, count, pages);
-		ASSERTCMP(n, ==, count);
-
-		loop = 0;
-		do {
-			struct bio_vec bvec = {.bv_page = pages[loop],
-					       .bv_offset = offset};
-			msg->msg_flags = 0;
-			to = PAGE_SIZE;
-			if (first + loop >= last)
-				to = call->last_to;
-			else
-				msg->msg_flags = MSG_MORE;
-			bvec.bv_len = to - offset;
-			offset = 0;
-
-			_debug("- range %u-%u%s",
-			       offset, to, msg->msg_flags ? " [more]" : "");
-			iov_iter_bvec(&msg->msg_iter, WRITE | ITER_BVEC,
-				      &bvec, 1, to - offset);
-
-			/* have to change the state *before* sending the last
-			 * packet as RxRPC might give us the reply before it
-			 * returns from sending the request */
-			if (first + loop >= last)
-				call->state = AFS_CALL_AWAIT_REPLY;
-			ret = rxrpc_kernel_send_data(afs_socket, call->rxcall,
-						     msg, to - offset);
-			if (ret < 0)
-				break;
-		} while (++loop < count);
-		first += count;
-
-		for (loop = 0; loop < count; loop++)
-			put_page(pages[loop]);
+		afs_load_bvec(call, msg, bv, first, last, offset);
+		offset = 0;
+		bytes = msg->msg_iter.count;
+		nr = msg->msg_iter.nr_segs;
+
+		/* Have to change the state *before* sending the last
+		 * packet as RxRPC might give us the reply before it
+		 * returns from sending the request.
+		 */
+		if (first + nr >= last)
+			call->state = AFS_CALL_AWAIT_REPLY;
+		ret = rxrpc_kernel_send_data(afs_socket, call->rxcall,
+					     msg, bytes);
+		for (loop = 0; loop < nr; loop++)
+			put_page(bv[loop].bv_page);
 		if (ret < 0)
 			break;
+
+		first += nr;
 	} while (first <= last);
 
-	_leave(" = %d", ret);
 	return ret;
 }
 

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 18/27] afs: Fix the maths in afs_fs_store_data()
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (15 preceding siblings ...)
  2017-03-09 18:57 ` [PATCH 17/27] afs: Use a bvec rather than a kvec in afs_send_pages() David Howells
@ 2017-03-09 18:57 ` David Howells
  2017-03-09 18:58 ` [PATCH 19/27] afs: Invalid op ID should abort with RXGEN_OPCODE David Howells
                   ` (8 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:57 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, linux-afs, linux-kernel

afs_fs_store_data() works out of the size of the write it's going to make,
but it uses 32-bit unsigned subtraction in one place that gets
automatically cast to loff_t.

However, if to < offset, then the number goes negative, but as the result
isn't signed, this doesn't get sign-extended to 64-bits when placed in a
loff_t.

Fix by casting the operands to loff_t.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/fsclient.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index 0778c5b6b59b..d9234b767287 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -1236,7 +1236,7 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
 	_enter(",%x,{%x:%u},,",
 	       key_serial(wb->key), vnode->fid.vid, vnode->fid.vnode);
 
-	size = to - offset;
+	size = (loff_t)to - (loff_t)offset;
 	if (first != last)
 		size += (loff_t)(last - first) << PAGE_SHIFT;
 	pos = (loff_t)first << PAGE_SHIFT;

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 19/27] afs: Invalid op ID should abort with RXGEN_OPCODE
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (16 preceding siblings ...)
  2017-03-09 18:57 ` [PATCH 18/27] afs: Fix the maths in afs_fs_store_data() David Howells
@ 2017-03-09 18:58 ` David Howells
  2017-03-09 18:58 ` [PATCH 20/27] afs: Better abort and net error handling David Howells
                   ` (7 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:58 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, linux-afs, linux-kernel

When we are given an invalid operation ID, we should abort that with
RXGEN_OPCODE rather than RX_INVALID_OPERATION.

Also map RXGEN_OPCODE to -ENOTSUPP.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/misc.c  |    2 ++
 fs/afs/rxrpc.c |    2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/fs/afs/misc.c b/fs/afs/misc.c
index 91ea1aa0d8b3..100b207efc9e 100644
--- a/fs/afs/misc.c
+++ b/fs/afs/misc.c
@@ -84,6 +84,8 @@ int afs_abort_to_error(u32 abort_code)
 	case RXKADDATALEN:	return -EKEYREJECTED;
 	case RXKADILLEGALLEVEL:	return -EKEYREJECTED;
 
+	case RXGEN_OPCODE:	return -ENOTSUPP;
+
 	default:		return -EREMOTEIO;
 	}
 }
diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index bf45307ff201..bf7761fe6ef5 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -465,7 +465,7 @@ static void afs_deliver_to_call(struct afs_call *call)
 						abort_code, -ret, "KNC");
 			goto do_abort;
 		case -ENOTSUPP:
-			abort_code = RX_INVALID_OPERATION;
+			abort_code = RXGEN_OPCODE;
 			rxrpc_kernel_abort_call(afs_socket, call->rxcall,
 						abort_code, -ret, "KIV");
 			goto do_abort;

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 20/27] afs: Better abort and net error handling
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (17 preceding siblings ...)
  2017-03-09 18:58 ` [PATCH 19/27] afs: Invalid op ID should abort with RXGEN_OPCODE David Howells
@ 2017-03-09 18:58 ` David Howells
  2017-03-09 18:58 ` [PATCH 21/27] afs: Populate and use client modification time David Howells
                   ` (6 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:58 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, linux-afs, linux-kernel

If we receive a network error, a remote abort or a protocol error whilst
we're still transmitting data, make sure we return an appropriate error to
the caller rather than ESHUTDOWN or ECONNABORTED.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/rxrpc.c |   35 +++++++++++++++++++++++++++--------
 1 file changed, 27 insertions(+), 8 deletions(-)

diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index bf7761fe6ef5..22d26b369070 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -340,6 +340,8 @@ int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp,
 	struct rxrpc_call *rxcall;
 	struct msghdr msg;
 	struct kvec iov[1];
+	size_t offset;
+	u32 abort_code;
 	int ret;
 
 	_enter("%x,{%d},", addr->s_addr, ntohs(call->port));
@@ -388,9 +390,11 @@ int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp,
 	msg.msg_controllen	= 0;
 	msg.msg_flags		= (call->send_pages ? MSG_MORE : 0);
 
-	/* have to change the state *before* sending the last packet as RxRPC
-	 * might give us the reply before it returns from sending the
-	 * request */
+	/* We have to change the state *before* sending the last packet as
+	 * rxrpc might give us the reply before it returns from sending the
+	 * request.  Further, if the send fails, we may already have been given
+	 * a notification and may have collected it.
+	 */
 	if (!call->send_pages)
 		call->state = AFS_CALL_AWAIT_REPLY;
 	ret = rxrpc_kernel_send_data(afs_socket, rxcall,
@@ -412,7 +416,17 @@ int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp,
 	return afs_wait_for_call_to_complete(call);
 
 error_do_abort:
-	rxrpc_kernel_abort_call(afs_socket, rxcall, RX_USER_ABORT, -ret, "KSD");
+	call->state = AFS_CALL_COMPLETE;
+	if (ret != -ECONNABORTED) {
+		rxrpc_kernel_abort_call(afs_socket, rxcall, RX_USER_ABORT,
+					-ret, "KSD");
+	} else {
+		abort_code = 0;
+		offset = 0;
+		rxrpc_kernel_recv_data(afs_socket, rxcall, NULL, 0, &offset,
+				       false, &abort_code);
+		ret = call->type->abort_to_error(abort_code);
+	}
 error_kill_call:
 	afs_put_call(call);
 	_leave(" = %d", ret);
@@ -459,16 +473,18 @@ static void afs_deliver_to_call(struct afs_call *call)
 		case -EINPROGRESS:
 		case -EAGAIN:
 			goto out;
+		case -ECONNABORTED:
+			goto call_complete;
 		case -ENOTCONN:
 			abort_code = RX_CALL_DEAD;
 			rxrpc_kernel_abort_call(afs_socket, call->rxcall,
 						abort_code, -ret, "KNC");
-			goto do_abort;
+			goto save_error;
 		case -ENOTSUPP:
 			abort_code = RXGEN_OPCODE;
 			rxrpc_kernel_abort_call(afs_socket, call->rxcall,
 						abort_code, -ret, "KIV");
-			goto do_abort;
+			goto save_error;
 		case -ENODATA:
 		case -EBADMSG:
 		case -EMSGSIZE:
@@ -478,7 +494,7 @@ static void afs_deliver_to_call(struct afs_call *call)
 				abort_code = RXGEN_SS_UNMARSHAL;
 			rxrpc_kernel_abort_call(afs_socket, call->rxcall,
 						abort_code, EBADMSG, "KUM");
-			goto do_abort;
+			goto save_error;
 		}
 	}
 
@@ -489,8 +505,9 @@ static void afs_deliver_to_call(struct afs_call *call)
 	_leave("");
 	return;
 
-do_abort:
+save_error:
 	call->error = ret;
+call_complete:
 	call->state = AFS_CALL_COMPLETE;
 	goto done;
 }
@@ -538,6 +555,8 @@ static int afs_wait_for_call_to_complete(struct afs_call *call)
 		_debug("call incomplete");
 		rxrpc_kernel_abort_call(afs_socket, call->rxcall,
 					RX_CALL_DEAD, -ret, abort_why);
+	} else if (call->error < 0) {
+		ret = call->error;
 	}
 
 	_debug("call complete");

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 21/27] afs: Populate and use client modification time
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (18 preceding siblings ...)
  2017-03-09 18:58 ` [PATCH 20/27] afs: Better abort and net error handling David Howells
@ 2017-03-09 18:58 ` David Howells
  2017-03-09 18:58 ` [PATCH 22/27] afs: Don't set PG_error on local EINTR or ENOMEM when filling a page David Howells
                   ` (5 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:58 UTC (permalink / raw)
  To: viro; +Cc: Marc Dionne, dhowells, linux-fsdevel, linux-afs, linux-kernel

From: Marc Dionne <marc.dionne@auristor.com>

The inode timestamps should be set from the client time
in the status received from the server, rather than the
server time which is meant for internal server use.

Set AFS_SET_MTIME and populate the mtime for operations
that take an input status, such as file/dir creation
and StoreData.  If an input time is not provided the
server will set the vnode times based on the current server
time.

In a situation where the server has some skew with the
client, this could lead to the client seeing a timestamp
in the future for a file that it just created or wrote.

Signed-off-by: Marc Dionne <marc.dionne@auristor.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/fsclient.c |   18 +++++++++---------
 fs/afs/inode.c    |    2 +-
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index d9234b767287..19f76ae36982 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -111,7 +111,7 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
 			vnode->vfs_inode.i_mode = mode;
 		}
 
-		vnode->vfs_inode.i_ctime.tv_sec	= status->mtime_server;
+		vnode->vfs_inode.i_ctime.tv_sec	= status->mtime_client;
 		vnode->vfs_inode.i_mtime	= vnode->vfs_inode.i_ctime;
 		vnode->vfs_inode.i_atime	= vnode->vfs_inode.i_ctime;
 		vnode->vfs_inode.i_version	= data_version;
@@ -734,8 +734,8 @@ int afs_fs_create(struct afs_server *server,
 		memset(bp, 0, padsz);
 		bp = (void *) bp + padsz;
 	}
-	*bp++ = htonl(AFS_SET_MODE);
-	*bp++ = 0; /* mtime */
+	*bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME);
+	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
 	*bp++ = 0; /* owner */
 	*bp++ = 0; /* group */
 	*bp++ = htonl(mode & S_IALLUGO); /* unix mode */
@@ -1003,8 +1003,8 @@ int afs_fs_symlink(struct afs_server *server,
 		memset(bp, 0, c_padsz);
 		bp = (void *) bp + c_padsz;
 	}
-	*bp++ = htonl(AFS_SET_MODE);
-	*bp++ = 0; /* mtime */
+	*bp++ = htonl(AFS_SET_MODE | AFS_SET_MTIME);
+	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
 	*bp++ = 0; /* owner */
 	*bp++ = 0; /* group */
 	*bp++ = htonl(S_IRWXUGO); /* unix mode */
@@ -1203,8 +1203,8 @@ static int afs_fs_store_data64(struct afs_server *server,
 	*bp++ = htonl(vnode->fid.vnode);
 	*bp++ = htonl(vnode->fid.unique);
 
-	*bp++ = 0; /* mask */
-	*bp++ = 0; /* mtime */
+	*bp++ = htonl(AFS_SET_MTIME); /* mask */
+	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
 	*bp++ = 0; /* owner */
 	*bp++ = 0; /* group */
 	*bp++ = 0; /* unix mode */
@@ -1280,8 +1280,8 @@ int afs_fs_store_data(struct afs_server *server, struct afs_writeback *wb,
 	*bp++ = htonl(vnode->fid.vnode);
 	*bp++ = htonl(vnode->fid.unique);
 
-	*bp++ = 0; /* mask */
-	*bp++ = 0; /* mtime */
+	*bp++ = htonl(AFS_SET_MTIME); /* mask */
+	*bp++ = htonl(vnode->vfs_inode.i_mtime.tv_sec); /* mtime */
 	*bp++ = 0; /* owner */
 	*bp++ = 0; /* group */
 	*bp++ = 0; /* unix mode */
diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index 4079c832ff27..aae55dd15108 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -85,7 +85,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
 	inode->i_uid		= vnode->status.owner;
 	inode->i_gid            = vnode->status.group;
 	inode->i_size		= vnode->status.size;
-	inode->i_ctime.tv_sec	= vnode->status.mtime_server;
+	inode->i_ctime.tv_sec	= vnode->status.mtime_client;
 	inode->i_ctime.tv_nsec	= 0;
 	inode->i_atime		= inode->i_mtime = inode->i_ctime;
 	inode->i_blocks		= 0;

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 22/27] afs: Don't set PG_error on local EINTR or ENOMEM when filling a page
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (19 preceding siblings ...)
  2017-03-09 18:58 ` [PATCH 21/27] afs: Populate and use client modification time David Howells
@ 2017-03-09 18:58 ` David Howells
  2017-03-09 18:58 ` [PATCH 23/27] afs: Fix page leak in afs_write_begin() David Howells
                   ` (4 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:58 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, linux-afs, linux-kernel

Don't set PG_error on a page if we get local EINTR or ENOMEM when filling a
page for writing.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/file.c |   12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/fs/afs/file.c b/fs/afs/file.c
index b5829443ff69..0d5b8508869b 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -212,7 +212,13 @@ int afs_page_filler(void *data, struct page *page)
 			fscache_uncache_page(vnode->cache, page);
 #endif
 			BUG_ON(PageFsCache(page));
-			goto error;
+
+			if (ret == -EINTR ||
+			    ret == -ENOMEM ||
+			    ret == -ERESTARTSYS ||
+			    ret == -EAGAIN)
+				goto error;
+			goto io_error;
 		}
 
 		SetPageUptodate(page);
@@ -231,10 +237,12 @@ int afs_page_filler(void *data, struct page *page)
 	_leave(" = 0");
 	return 0;
 
+io_error:
+	SetPageError(page);
+	goto error;
 enomem:
 	ret = -ENOMEM;
 error:
-	SetPageError(page);
 	unlock_page(page);
 	_leave(" = %d", ret);
 	return ret;

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 23/27] afs: Fix page leak in afs_write_begin()
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (20 preceding siblings ...)
  2017-03-09 18:58 ` [PATCH 22/27] afs: Don't set PG_error on local EINTR or ENOMEM when filling a page David Howells
@ 2017-03-09 18:58 ` David Howells
  2017-03-09 18:58 ` [PATCH 24/27] afs: afs_fsync() does two flushes, one of which is redundant David Howells
                   ` (3 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:58 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, linux-afs, linux-kernel

afs_write_begin() leaks a ref and a lock on a page if afs_fill_page()
fails.  Fix the leak by unlocking and releasing the page in the error path.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/write.c |    7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/fs/afs/write.c b/fs/afs/write.c
index f1450ea09406..6e13e96c3db0 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -154,12 +154,12 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
 		kfree(candidate);
 		return -ENOMEM;
 	}
-	*pagep = page;
-	/* page won't leak in error case: it eventually gets cleaned off LRU */
 
 	if (!PageUptodate(page) && len != PAGE_SIZE) {
 		ret = afs_fill_page(vnode, key, pos & PAGE_MASK, PAGE_SIZE, page);
 		if (ret < 0) {
+			unlock_page(page);
+			put_page(page);
 			kfree(candidate);
 			_leave(" = %d [prep]", ret);
 			return ret;
@@ -167,6 +167,9 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
 		SetPageUptodate(page);
 	}
 
+	/* page won't leak in error case: it eventually gets cleaned off LRU */
+	*pagep = page;
+
 try_again:
 	spin_lock(&vnode->writeback_lock);
 

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 24/27] afs: afs_fsync() does two flushes, one of which is redundant
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (21 preceding siblings ...)
  2017-03-09 18:58 ` [PATCH 23/27] afs: Fix page leak in afs_write_begin() David Howells
@ 2017-03-09 18:58 ` David Howells
  2017-03-09 18:58 ` [PATCH 25/27] afs: Fix afs_kill_pages() David Howells
                   ` (2 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:58 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, linux-afs, linux-kernel

afs_fsync() calls filemap_write_and_wait_range() and then does a walk
through the writeback records and flushes those - which should achieve
exactly the same thing.

Get rid of the filemap_write_and_wait_range() since that's uninterruptible,
whereas the wait for the writeback records is interruptible.

Further, we can at least contract the inode-locked region to just the
afs_writeback_call().

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/write.c |   61 ++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 39 insertions(+), 22 deletions(-)

diff --git a/fs/afs/write.c b/fs/afs/write.c
index 6e13e96c3db0..ab89551ab356 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -700,21 +700,17 @@ int afs_writeback_all(struct afs_vnode *vnode)
  * - the return status from this call provides a reliable indication of
  *   whether any write errors occurred for this process.
  */
-int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+static int afs_sync_file(struct file *file, loff_t start, loff_t end, bool sync)
 {
 	struct inode *inode = file_inode(file);
 	struct afs_writeback *wb, *xwb;
 	struct afs_vnode *vnode = AFS_FS_I(inode);
+	bool do_sync = false;
 	int ret;
 
 	_enter("{%x:%u},{n=%pD},%d",
 	       vnode->fid.vid, vnode->fid.vnode, file,
-	       datasync);
-
-	ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
-	if (ret)
-		return ret;
-	inode_lock(inode);
+	       sync);
 
 	/* use a writeback record as a marker in the queue - when this reaches
 	 * the front of the queue, all the outstanding writes are either
@@ -732,34 +728,55 @@ int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 	wb->usage = 1;
 	wb->state = AFS_WBACK_SYNCING;
 	init_waitqueue_head(&wb->waitq);
+	INIT_LIST_HEAD(&wb->link);
 
 	spin_lock(&vnode->writeback_lock);
 	list_for_each_entry(xwb, &vnode->writebacks, link) {
-		if (xwb->state == AFS_WBACK_PENDING)
+		switch (xwb->state) {
+		case AFS_WBACK_PENDING:
 			xwb->state = AFS_WBACK_CONFLICTING;
+			do_sync = true;
+			break;
+		default:
+			do_sync |= sync;
+			break;
+		case AFS_WBACK_SYNCING:
+			break;
+		case AFS_WBACK_COMPLETE:
+			kdebug("Shouldn't see completed records");
+			break;
+		}
 	}
-	list_add_tail(&wb->link, &vnode->writebacks);
+	if (do_sync)
+		list_add_tail(&wb->link, &vnode->writebacks);
 	spin_unlock(&vnode->writeback_lock);
 
-	/* push all the outstanding writebacks to the server */
-	ret = afs_writeback_all(vnode);
-	if (ret < 0) {
-		afs_put_writeback(wb);
-		_leave(" = %d [wb]", ret);
-		goto out;
+	ret = 0;
+	if (do_sync) {
+		/* push all the outstanding writebacks to the server */
+		inode_lock(inode);
+		ret = afs_writeback_all(vnode);
+		inode_unlock(inode);
+		if (ret < 0)
+			goto out;
+
+		/* wait for the preceding writes to actually complete */
+		ret = wait_event_interruptible(wb->waitq,
+					       wb->state == AFS_WBACK_COMPLETE ||
+					       vnode->writebacks.next == &wb->link);
 	}
 
-	/* wait for the preceding writes to actually complete */
-	ret = wait_event_interruptible(wb->waitq,
-				       wb->state == AFS_WBACK_COMPLETE ||
-				       vnode->writebacks.next == &wb->link);
+out:
 	afs_put_writeback(wb);
 	_leave(" = %d", ret);
-out:
-	inode_unlock(inode);
 	return ret;
 }
 
+int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+	return afs_sync_file(file, start, end, true);
+}
+
 /*
  * Flush out all outstanding writes on a file opened for writing when it is
  * closed.
@@ -771,7 +788,7 @@ int afs_flush(struct file *file, fl_owner_t id)
 	if ((file->f_mode & FMODE_WRITE) == 0)
 		return 0;
 
-	return vfs_fsync(file, 0);
+	return afs_sync_file(file, 0, LLONG_MAX, false);
 }
 
 /*

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 25/27] afs: Fix afs_kill_pages()
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (22 preceding siblings ...)
  2017-03-09 18:58 ` [PATCH 24/27] afs: afs_fsync() does two flushes, one of which is redundant David Howells
@ 2017-03-09 18:58 ` David Howells
  2017-03-09 18:58 ` [PATCH 26/27] afs: Fix an off-by-one error in afs_send_pages() David Howells
  2017-03-09 18:58 ` [PATCH 27/27] afs: Fix abort on signal while waiting for call completion David Howells
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:58 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, linux-afs, linux-kernel

Fix afs_kill_pages() in two ways:

 (1) If a writeback has been partially flushed, then if we try and kill the
     pages it contains, some of them may no longer be undergoing writeback
     and end_page_writeback() will assert.

     Fix this by checking to see whether the page in question is actually
     undergoing writeback before ending that writeback.

 (2) The loop that scans for pages to kill doesn't increase the first page
     index, and so the loop may not terminate, but it will try to process
     the same pages over and over again.

     Fix this by increasing the first page index to one after the last page
     we processed.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/write.c |   10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/fs/afs/write.c b/fs/afs/write.c
index ab89551ab356..16c01c720ca9 100644
--- a/fs/afs/write.c
+++ b/fs/afs/write.c
@@ -321,10 +321,14 @@ static void afs_kill_pages(struct afs_vnode *vnode, bool error,
 		ASSERTCMP(pv.nr, ==, count);
 
 		for (loop = 0; loop < count; loop++) {
-			ClearPageUptodate(pv.pages[loop]);
+			struct page *page = pv.pages[loop];
+			ClearPageUptodate(page);
 			if (error)
-				SetPageError(pv.pages[loop]);
-			end_page_writeback(pv.pages[loop]);
+				SetPageError(page);
+			if (PageWriteback(page))
+				end_page_writeback(page);
+			if (page->index >= first)
+				first = page->index + 1;
 		}
 
 		__pagevec_release(&pv);

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 26/27] afs: Fix an off-by-one error in afs_send_pages()
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (23 preceding siblings ...)
  2017-03-09 18:58 ` [PATCH 25/27] afs: Fix afs_kill_pages() David Howells
@ 2017-03-09 18:58 ` David Howells
  2017-03-09 18:58 ` [PATCH 27/27] afs: Fix abort on signal while waiting for call completion David Howells
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:58 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, linux-afs, linux-kernel

afs_send_pages() should only put the call into the AFS_CALL_AWAIT_REPLY
state if it has sent all the pages - but the check it makes is incorrect
and sometimes it will finish the loop early.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/rxrpc.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index 22d26b369070..b12da6aa5412 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -315,7 +315,7 @@ static int afs_send_pages(struct afs_call *call, struct msghdr *msg)
 		 * packet as RxRPC might give us the reply before it
 		 * returns from sending the request.
 		 */
-		if (first + nr >= last)
+		if (first + nr - 1 >= last)
 			call->state = AFS_CALL_AWAIT_REPLY;
 		ret = rxrpc_kernel_send_data(afs_socket, call->rxcall,
 					     msg, bytes);

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 27/27] afs: Fix abort on signal while waiting for call completion
  2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
                   ` (24 preceding siblings ...)
  2017-03-09 18:58 ` [PATCH 26/27] afs: Fix an off-by-one error in afs_send_pages() David Howells
@ 2017-03-09 18:58 ` David Howells
  25 siblings, 0 replies; 27+ messages in thread
From: David Howells @ 2017-03-09 18:58 UTC (permalink / raw)
  To: viro; +Cc: dhowells, linux-fsdevel, Marc Dionne, linux-afs, linux-kernel

Fix the way in which a call that's in progress and being waited for is
aborted in the case that EINTR is detected.  We should be sending
RX_USER_ABORT rather than RX_CALL_DEAD as the abort code.

Note that since the only two ways out of the loop are if the call completes
or if a signal happens, the kill-the-call clause after the loop has
finished can only happen in the case of EINTR.  This means that we only
have one abort case to deal with, not two, and the "KWC" case can never
happen and so can be deleted.

Note further that simply aborting the call isn't necessarily the best thing
here since at this point: the request has been entirely sent and it's
likely the server will do the operation anyway - whether we abort it or
not.  In future, we should punt the handling of the remainder of the call
off to a background thread.

Reported-by: Marc Dionne <marc.c.dionne@auristor.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---

 fs/afs/rxrpc.c |   19 ++++++-------------
 1 file changed, 6 insertions(+), 13 deletions(-)

diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index b12da6aa5412..8f76b13d5549 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -517,7 +517,6 @@ static void afs_deliver_to_call(struct afs_call *call)
  */
 static int afs_wait_for_call_to_complete(struct afs_call *call)
 {
-	const char *abort_why;
 	int ret;
 
 	DECLARE_WAITQUEUE(myself, current);
@@ -536,13 +535,8 @@ static int afs_wait_for_call_to_complete(struct afs_call *call)
 			continue;
 		}
 
-		abort_why = "KWC";
-		ret = call->error;
-		if (call->state == AFS_CALL_COMPLETE)
-			break;
-		abort_why = "KWI";
-		ret = -EINTR;
-		if (signal_pending(current))
+		if (call->state == AFS_CALL_COMPLETE ||
+		    signal_pending(current))
 			break;
 		schedule();
 	}
@@ -550,15 +544,14 @@ static int afs_wait_for_call_to_complete(struct afs_call *call)
 	remove_wait_queue(&call->waitq, &myself);
 	__set_current_state(TASK_RUNNING);
 
-	/* kill the call */
+	/* Kill off the call if it's still live. */
 	if (call->state < AFS_CALL_COMPLETE) {
-		_debug("call incomplete");
+		_debug("call interrupted");
 		rxrpc_kernel_abort_call(afs_socket, call->rxcall,
-					RX_CALL_DEAD, -ret, abort_why);
-	} else if (call->error < 0) {
-		ret = call->error;
+					RX_USER_ABORT, -EINTR, "KWI");
 	}
 
+	ret = call->error;
 	_debug("call complete");
 	afs_put_call(call);
 	_leave(" = %d", ret);

^ permalink raw reply related	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2017-03-09 18:59 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-09 18:55 [PATCH 01/27] afs: Fix missing put_page() David Howells
2017-03-09 18:55 ` [PATCH 02/27] afs: Fix page overput in afs_fill_page() David Howells
2017-03-09 18:56 ` [PATCH 03/27] afs: Populate group ID from vnode status David Howells
2017-03-09 18:56 ` [PATCH 04/27] afs: Adjust mode bits processing David Howells
2017-03-09 18:56 ` [PATCH 05/27] afs: Deal with an empty callback array David Howells
2017-03-09 18:56 ` [PATCH 06/27] afs: Handle better the server returning excess or short data David Howells
2017-03-09 18:56 ` [PATCH 07/27] afs: Kill struct afs_read::pg_offset David Howells
2017-03-09 18:56 ` [PATCH 08/27] afs: Handle a short write to an AFS page David Howells
2017-03-09 18:56 ` [PATCH 09/27] afs: Flush outstanding writes when an fd is closed David Howells
2017-03-09 18:56 ` [PATCH 10/27] afs: Distinguish mountpoints from symlinks by file mode alone David Howells
2017-03-09 18:57 ` [PATCH 11/27] afs: inode: Replace rcu_assign_pointer() with RCU_INIT_POINTER() David Howells
2017-03-09 18:57 ` [PATCH 12/27] afs: security: " David Howells
2017-03-09 18:57 ` [PATCH 13/27] afs: Migrate vlocation fields to 64-bit David Howells
2017-03-09 18:57 ` [PATCH 14/27] afs: Prevent callback expiry timer overflow David Howells
2017-03-09 18:57 ` [PATCH 15/27] afs: Fix AFS read bug David Howells
2017-03-09 18:57 ` [PATCH 16/27] afs: Make struct afs_read::remain 64-bit David Howells
2017-03-09 18:57 ` [PATCH 17/27] afs: Use a bvec rather than a kvec in afs_send_pages() David Howells
2017-03-09 18:57 ` [PATCH 18/27] afs: Fix the maths in afs_fs_store_data() David Howells
2017-03-09 18:58 ` [PATCH 19/27] afs: Invalid op ID should abort with RXGEN_OPCODE David Howells
2017-03-09 18:58 ` [PATCH 20/27] afs: Better abort and net error handling David Howells
2017-03-09 18:58 ` [PATCH 21/27] afs: Populate and use client modification time David Howells
2017-03-09 18:58 ` [PATCH 22/27] afs: Don't set PG_error on local EINTR or ENOMEM when filling a page David Howells
2017-03-09 18:58 ` [PATCH 23/27] afs: Fix page leak in afs_write_begin() David Howells
2017-03-09 18:58 ` [PATCH 24/27] afs: afs_fsync() does two flushes, one of which is redundant David Howells
2017-03-09 18:58 ` [PATCH 25/27] afs: Fix afs_kill_pages() David Howells
2017-03-09 18:58 ` [PATCH 26/27] afs: Fix an off-by-one error in afs_send_pages() David Howells
2017-03-09 18:58 ` [PATCH 27/27] afs: Fix abort on signal while waiting for call completion David Howells

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).