diff options
author | Jeff Layton <jlayton@redhat.com> | 2012-07-11 09:09:36 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-07-17 00:57:14 -0400 |
commit | 3cf003c08be785af4bee9ac05891a15bcbff856a (patch) | |
tree | f54e5a39dd782af881a4e5927225f2fd3a704889 /fs | |
parent | 3ae629d98bd5ed77585a878566f04f310adbc591 (diff) |
cifs: when CONFIG_HIGHMEM is set, serialize the read/write kmaps
Jian found that when he ran fsx on a 32 bit arch with a large wsize the
process and one of the bdi writeback kthreads would sometimes deadlock
with a stack trace like this:
crash> bt
PID: 2789 TASK: f02edaa0 CPU: 3 COMMAND: "fsx"
#0 [eed63cbc] schedule at c083c5b3
#1 [eed63d80] kmap_high at c0500ec8
#2 [eed63db0] cifs_async_writev at f7fabcd7 [cifs]
#3 [eed63df0] cifs_writepages at f7fb7f5c [cifs]
#4 [eed63e50] do_writepages at c04f3e32
#5 [eed63e54] __filemap_fdatawrite_range at c04e152a
#6 [eed63ea4] filemap_fdatawrite at c04e1b3e
#7 [eed63eb4] cifs_file_aio_write at f7fa111a [cifs]
#8 [eed63ecc] do_sync_write at c052d202
#9 [eed63f74] vfs_write at c052d4ee
#10 [eed63f94] sys_write at c052df4c
#11 [eed63fb0] ia32_sysenter_target at c0409a98
EAX: 00000004 EBX: 00000003 ECX: abd73b73 EDX: 012a65c6
DS: 007b ESI: 012a65c6 ES: 007b EDI: 00000000
SS: 007b ESP: bf8db178 EBP: bf8db1f8 GS: 0033
CS: 0073 EIP: 40000424 ERR: 00000004 EFLAGS: 00000246
Each task would kmap part of its address array before getting stuck, but
not enough to actually issue the write.
This patch fixes this by serializing the marshal_iov operations for
async reads and writes. The idea here is to ensure that cifs
aggressively tries to populate a request before attempting to fulfill
another one. As soon as all of the pages are kmapped for a request, then
we can unlock and allow another one to proceed.
There's no need to do this serialization on non-CONFIG_HIGHMEM arches
however, so optimize all of this out when CONFIG_HIGHMEM isn't set.
Cc: <stable@vger.kernel.org>
Reported-by: Jian Li <jiali@redhat.com>
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/cifssmb.c | 30 |
1 files changed, 29 insertions, 1 deletions
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 5b400730c213..4ee522b3f66f 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -86,7 +86,31 @@ static struct { | |||
86 | #endif /* CONFIG_CIFS_WEAK_PW_HASH */ | 86 | #endif /* CONFIG_CIFS_WEAK_PW_HASH */ |
87 | #endif /* CIFS_POSIX */ | 87 | #endif /* CIFS_POSIX */ |
88 | 88 | ||
89 | /* Forward declarations */ | 89 | #ifdef CONFIG_HIGHMEM |
90 | /* | ||
91 | * On arches that have high memory, kmap address space is limited. By | ||
92 | * serializing the kmap operations on those arches, we ensure that we don't | ||
93 | * end up with a bunch of threads in writeback with partially mapped page | ||
94 | * arrays, stuck waiting for kmap to come back. That situation prevents | ||
95 | * progress and can deadlock. | ||
96 | */ | ||
97 | static DEFINE_MUTEX(cifs_kmap_mutex); | ||
98 | |||
99 | static inline void | ||
100 | cifs_kmap_lock(void) | ||
101 | { | ||
102 | mutex_lock(&cifs_kmap_mutex); | ||
103 | } | ||
104 | |||
105 | static inline void | ||
106 | cifs_kmap_unlock(void) | ||
107 | { | ||
108 | mutex_unlock(&cifs_kmap_mutex); | ||
109 | } | ||
110 | #else /* !CONFIG_HIGHMEM */ | ||
111 | #define cifs_kmap_lock() do { ; } while(0) | ||
112 | #define cifs_kmap_unlock() do { ; } while(0) | ||
113 | #endif /* CONFIG_HIGHMEM */ | ||
90 | 114 | ||
91 | /* Mark as invalid, all open files on tree connections since they | 115 | /* Mark as invalid, all open files on tree connections since they |
92 | were closed when session to server was lost */ | 116 | were closed when session to server was lost */ |
@@ -1503,7 +1527,9 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) | |||
1503 | } | 1527 | } |
1504 | 1528 | ||
1505 | /* marshal up the page array */ | 1529 | /* marshal up the page array */ |
1530 | cifs_kmap_lock(); | ||
1506 | len = rdata->marshal_iov(rdata, data_len); | 1531 | len = rdata->marshal_iov(rdata, data_len); |
1532 | cifs_kmap_unlock(); | ||
1507 | data_len -= len; | 1533 | data_len -= len; |
1508 | 1534 | ||
1509 | /* issue the read if we have any iovecs left to fill */ | 1535 | /* issue the read if we have any iovecs left to fill */ |
@@ -2069,7 +2095,9 @@ cifs_async_writev(struct cifs_writedata *wdata) | |||
2069 | * and set the iov_len properly for each one. It may also set | 2095 | * and set the iov_len properly for each one. It may also set |
2070 | * wdata->bytes too. | 2096 | * wdata->bytes too. |
2071 | */ | 2097 | */ |
2098 | cifs_kmap_lock(); | ||
2072 | wdata->marshal_iov(iov, wdata); | 2099 | wdata->marshal_iov(iov, wdata); |
2100 | cifs_kmap_unlock(); | ||
2073 | 2101 | ||
2074 | cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes); | 2102 | cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes); |
2075 | 2103 | ||