diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 6aa7457af763ef..c858a29325ff31 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -89,6 +89,32 @@ static struct { /* Forward declarations */ static void cifs_readv_complete(struct work_struct *work); +#ifdef CONFIG_HIGHMEM +/* + * On arches that have high memory, kmap address space is limited. By + * serializing the kmap operations on those arches, we ensure that we don't + * end up with a bunch of threads in writeback with partially mapped page + * arrays, stuck waiting for kmap to come back. That situation prevents + * progress and can deadlock. + */ +static DEFINE_MUTEX(cifs_kmap_mutex); + +static inline void +cifs_kmap_lock(void) +{ + mutex_lock(&cifs_kmap_mutex); +} + +static inline void +cifs_kmap_unlock(void) +{ + mutex_unlock(&cifs_kmap_mutex); +} +#else /* !CONFIG_HIGHMEM */ +#define cifs_kmap_lock() do { ; } while(0) +#define cifs_kmap_unlock() do { ; } while(0) +#endif /* CONFIG_HIGHMEM */ + /* Mark as invalid, all open files on tree connections since they were closed when session to server was lost */ static void mark_open_files_invalid(struct cifs_tcon *pTcon) @@ -1540,6 +1566,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0; cFYI(1, "eof=%llu eof_index=%lu", eof, eof_index); + cifs_kmap_lock(); list_for_each_entry_safe(page, tpage, &rdata->pages, lru) { if (remaining >= PAGE_CACHE_SIZE) { /* enough data to fill the page */ @@ -1589,6 +1616,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) page_cache_release(page); } } + cifs_kmap_unlock(); /* issue the read if we have any iovecs left to fill */ if (rdata->nr_iov > 1) { @@ -2171,6 +2199,7 @@ cifs_async_writev(struct cifs_writedata *wdata) iov[0].iov_base = smb; /* marshal up the pages into iov array */ + cifs_kmap_lock(); wdata->bytes = 0; for (i = 0; i < wdata->nr_pages; i++) { iov[i + 1].iov_len = min(inode->i_size - @@ -2179,6 +2208,7 @@ cifs_async_writev(struct cifs_writedata *wdata) iov[i + 1].iov_base = kmap(wdata->pages[i]); wdata->bytes += iov[i + 1].iov_len; } + cifs_kmap_unlock(); cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes);