From 0471ca3fe481cf5ff0ae24c7003f4d9086a02791 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 May 2012 07:13:16 -0400 Subject: cifs: make cifs_readdata_alloc take a work_func_t arg We'll need different completion routines for an uncached read. Allow the caller to set the one he needs at allocation time. Also, move most of these functions to file.c so we can make more of them static. Signed-off-by: Jeff Layton diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 96192c1..d57cdf4 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -476,8 +476,6 @@ struct cifs_readdata { struct kvec iov[1]; }; -struct cifs_readdata *cifs_readdata_alloc(unsigned int nr_pages); -void cifs_readdata_free(struct cifs_readdata *rdata); int cifs_async_readv(struct cifs_readdata *rdata); /* asynchronous write support */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index da2f544..d263f98 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -87,7 +87,6 @@ static struct { #endif /* CIFS_POSIX */ /* Forward declarations */ -static void cifs_readv_complete(struct work_struct *work); /* Mark as invalid, all open files on tree connections since they were closed when session to server was lost */ @@ -1385,28 +1384,6 @@ openRetry: return rc; } -struct cifs_readdata * -cifs_readdata_alloc(unsigned int nr_pages) -{ - struct cifs_readdata *rdata; - - /* readdata + 1 kvec for each page */ - rdata = kzalloc(sizeof(*rdata) + - sizeof(struct kvec) * nr_pages, GFP_KERNEL); - if (rdata != NULL) { - INIT_WORK(&rdata->work, cifs_readv_complete); - INIT_LIST_HEAD(&rdata->pages); - } - return rdata; -} - -void -cifs_readdata_free(struct cifs_readdata *rdata) -{ - cifsFileInfo_put(rdata->cfile); - kfree(rdata); -} - /* * Discard any remaining data in the current SMB. To do this, we borrow the * current bigbuf. @@ -1632,33 +1609,6 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) } static void -cifs_readv_complete(struct work_struct *work) -{ - struct cifs_readdata *rdata = container_of(work, - struct cifs_readdata, work); - struct page *page, *tpage; - - list_for_each_entry_safe(page, tpage, &rdata->pages, lru) { - list_del(&page->lru); - lru_cache_add_file(page); - - if (rdata->result == 0) { - kunmap(page); - flush_dcache_page(page); - SetPageUptodate(page); - } - - unlock_page(page); - - if (rdata->result == 0) - cifs_readpage_to_fscache(rdata->mapping->host, page); - - page_cache_release(page); - } - cifs_readdata_free(rdata); -} - -static void cifs_readv_callback(struct mid_q_entry *mid) { struct cifs_readdata *rdata = mid->callback_data; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 81725e9..d210288 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2339,6 +2339,27 @@ ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, return cifs_user_writev(iocb, iov, nr_segs, pos); } +static struct cifs_readdata * +cifs_readdata_alloc(unsigned int nr_vecs, work_func_t complete) +{ + struct cifs_readdata *rdata; + + rdata = kzalloc(sizeof(*rdata) + + sizeof(struct kvec) * nr_vecs, GFP_KERNEL); + if (rdata != NULL) { + INIT_WORK(&rdata->work, complete); + INIT_LIST_HEAD(&rdata->pages); + } + return rdata; +} + +static void +cifs_readdata_free(struct cifs_readdata *rdata) +{ + cifsFileInfo_put(rdata->cfile); + kfree(rdata); +} + static ssize_t cifs_iovec_read(struct file *file, const struct iovec *iov, unsigned long nr_segs, loff_t *poffset) @@ -2606,6 +2627,33 @@ int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) return rc; } +static void +cifs_readv_complete(struct work_struct *work) +{ + struct cifs_readdata *rdata = container_of(work, + struct cifs_readdata, work); + struct page *page, *tpage; + + list_for_each_entry_safe(page, tpage, &rdata->pages, lru) { + list_del(&page->lru); + lru_cache_add_file(page); + + if (rdata->result == 0) { + kunmap(page); + flush_dcache_page(page); + SetPageUptodate(page); + } + + unlock_page(page); + + if (rdata->result == 0) + cifs_readpage_to_fscache(rdata->mapping->host, page); + + page_cache_release(page); + } + cifs_readdata_free(rdata); +} + static int cifs_readpages(struct file *file, struct address_space *mapping, struct list_head *page_list, unsigned num_pages) { @@ -2708,7 +2756,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, nr_pages++; } - rdata = cifs_readdata_alloc(nr_pages); + rdata = cifs_readdata_alloc(nr_pages, cifs_readv_complete); if (!rdata) { /* best to give up if we're out of mem */ list_for_each_entry_safe(page, tpage, &tmplist, lru) { -- cgit v0.10.2 From 8d5ce4d23c79e0f9861b19fc534f5b2dc636f79c Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 May 2012 07:13:16 -0400 Subject: cifs: abstract out function to marshal the iovec for readv receives Cached and uncached reads will need to do different things here to handle the difference when the pages are in pagecache and not. Abstract out the function that marshals the page list into a kvec array. Signed-off-by: Jeff Layton diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index d57cdf4..f309b43 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -472,6 +472,8 @@ struct cifs_readdata { int result; struct list_head pages; struct work_struct work; + int (*marshal_iov) (struct cifs_readdata *rdata, + unsigned int remaining); unsigned int nr_iov; struct kvec iov[1]; }; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index d263f98..3aa1fcc 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1436,13 +1436,10 @@ static int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) { int length, len; - unsigned int data_offset, remaining, data_len; + unsigned int data_offset, data_len; struct cifs_readdata *rdata = mid->callback_data; char *buf = server->smallbuf; unsigned int buflen = get_rfc1002_length(buf) + 4; - u64 eof; - pgoff_t eof_index; - struct page *page, *tpage; cFYI(1, "%s: mid=%llu offset=%llu bytes=%u", __func__, mid->mid, rdata->offset, rdata->bytes); @@ -1525,64 +1522,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) } /* marshal up the page array */ - len = 0; - remaining = data_len; - rdata->nr_iov = 1; - - /* determine the eof that the server (probably) has */ - eof = CIFS_I(rdata->mapping->host)->server_eof; - eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0; - cFYI(1, "eof=%llu eof_index=%lu", eof, eof_index); - - list_for_each_entry_safe(page, tpage, &rdata->pages, lru) { - if (remaining >= PAGE_CACHE_SIZE) { - /* enough data to fill the page */ - rdata->iov[rdata->nr_iov].iov_base = kmap(page); - rdata->iov[rdata->nr_iov].iov_len = PAGE_CACHE_SIZE; - cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu", - rdata->nr_iov, page->index, - rdata->iov[rdata->nr_iov].iov_base, - rdata->iov[rdata->nr_iov].iov_len); - ++rdata->nr_iov; - len += PAGE_CACHE_SIZE; - remaining -= PAGE_CACHE_SIZE; - } else if (remaining > 0) { - /* enough for partial page, fill and zero the rest */ - rdata->iov[rdata->nr_iov].iov_base = kmap(page); - rdata->iov[rdata->nr_iov].iov_len = remaining; - cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu", - rdata->nr_iov, page->index, - rdata->iov[rdata->nr_iov].iov_base, - rdata->iov[rdata->nr_iov].iov_len); - memset(rdata->iov[rdata->nr_iov].iov_base + remaining, - '\0', PAGE_CACHE_SIZE - remaining); - ++rdata->nr_iov; - len += remaining; - remaining = 0; - } else if (page->index > eof_index) { - /* - * The VFS will not try to do readahead past the - * i_size, but it's possible that we have outstanding - * writes with gaps in the middle and the i_size hasn't - * caught up yet. Populate those with zeroed out pages - * to prevent the VFS from repeatedly attempting to - * fill them until the writes are flushed. - */ - zero_user(page, 0, PAGE_CACHE_SIZE); - list_del(&page->lru); - lru_cache_add_file(page); - flush_dcache_page(page); - SetPageUptodate(page); - unlock_page(page); - page_cache_release(page); - } else { - /* no need to hold page hostage */ - list_del(&page->lru); - lru_cache_add_file(page); - unlock_page(page); - page_cache_release(page); - } - } + len = rdata->marshal_iov(rdata, data_len); + data_len -= len; /* issue the read if we have any iovecs left to fill */ if (rdata->nr_iov > 1) { @@ -1598,7 +1539,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) rdata->bytes = length; cFYI(1, "total_read=%u buflen=%u remaining=%u", server->total_read, - buflen, remaining); + buflen, data_len); /* discard anything left over */ if (server->total_read < buflen) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index d210288..183381d 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2654,6 +2654,73 @@ cifs_readv_complete(struct work_struct *work) cifs_readdata_free(rdata); } +static int +cifs_readpages_marshal_iov(struct cifs_readdata *rdata, unsigned int remaining) +{ + int len = 0; + struct page *page, *tpage; + u64 eof; + pgoff_t eof_index; + + /* determine the eof that the server (probably) has */ + eof = CIFS_I(rdata->mapping->host)->server_eof; + eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0; + cFYI(1, "eof=%llu eof_index=%lu", eof, eof_index); + + rdata->nr_iov = 1; + list_for_each_entry_safe(page, tpage, &rdata->pages, lru) { + if (remaining >= PAGE_CACHE_SIZE) { + /* enough data to fill the page */ + rdata->iov[rdata->nr_iov].iov_base = kmap(page); + rdata->iov[rdata->nr_iov].iov_len = PAGE_CACHE_SIZE; + cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu", + rdata->nr_iov, page->index, + rdata->iov[rdata->nr_iov].iov_base, + rdata->iov[rdata->nr_iov].iov_len); + ++rdata->nr_iov; + len += PAGE_CACHE_SIZE; + remaining -= PAGE_CACHE_SIZE; + } else if (remaining > 0) { + /* enough for partial page, fill and zero the rest */ + rdata->iov[rdata->nr_iov].iov_base = kmap(page); + rdata->iov[rdata->nr_iov].iov_len = remaining; + cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu", + rdata->nr_iov, page->index, + rdata->iov[rdata->nr_iov].iov_base, + rdata->iov[rdata->nr_iov].iov_len); + memset(rdata->iov[rdata->nr_iov].iov_base + remaining, + '\0', PAGE_CACHE_SIZE - remaining); + ++rdata->nr_iov; + len += remaining; + remaining = 0; + } else if (page->index > eof_index) { + /* + * The VFS will not try to do readahead past the + * i_size, but it's possible that we have outstanding + * writes with gaps in the middle and the i_size hasn't + * caught up yet. Populate those with zeroed out pages + * to prevent the VFS from repeatedly attempting to + * fill them until the writes are flushed. + */ + zero_user(page, 0, PAGE_CACHE_SIZE); + list_del(&page->lru); + lru_cache_add_file(page); + flush_dcache_page(page); + SetPageUptodate(page); + unlock_page(page); + page_cache_release(page); + } else { + /* no need to hold page hostage */ + list_del(&page->lru); + lru_cache_add_file(page); + unlock_page(page); + page_cache_release(page); + } + } + + return len; +} + static int cifs_readpages(struct file *file, struct address_space *mapping, struct list_head *page_list, unsigned num_pages) { @@ -2777,6 +2844,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, rdata->offset = offset; rdata->bytes = bytes; rdata->pid = pid; + rdata->marshal_iov = cifs_readpages_marshal_iov; list_splice_init(&tmplist, &rdata->pages); do { -- cgit v0.10.2 From 6993f74a5bf836210e7f253d5ad3f76d73a95f51 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 May 2012 07:13:17 -0400 Subject: cifs: add refcounting to cifs_readdata structures This isn't strictly necessary for the async readpages code, but the uncached version will need to be able to collect the replies after issuing the calls. Add a kref to cifs_readdata and use change the code to take and put references appropriately. Signed-off-by: Jeff Layton diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index f309b43..63e91c7 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -464,6 +464,7 @@ extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8, /* asynchronous read support */ struct cifs_readdata { + struct kref refcount; struct cifsFileInfo *cfile; struct address_space *mapping; __u64 offset; @@ -478,6 +479,7 @@ struct cifs_readdata { struct kvec iov[1]; }; +void cifs_readdata_release(struct kref *refcount); int cifs_async_readv(struct cifs_readdata *rdata); /* asynchronous write support */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 3aa1fcc..45633da 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1635,12 +1635,15 @@ cifs_async_readv(struct cifs_readdata *rdata) rdata->iov[0].iov_base = smb; rdata->iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4; + kref_get(&rdata->refcount); rc = cifs_call_async(tcon->ses->server, rdata->iov, 1, cifs_readv_receive, cifs_readv_callback, rdata, false); if (rc == 0) cifs_stats_inc(&tcon->num_reads); + else + kref_put(&rdata->refcount, cifs_readdata_release); cifs_small_buf_release(smb); return rc; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 183381d..ae285e0 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2347,16 +2347,22 @@ cifs_readdata_alloc(unsigned int nr_vecs, work_func_t complete) rdata = kzalloc(sizeof(*rdata) + sizeof(struct kvec) * nr_vecs, GFP_KERNEL); if (rdata != NULL) { + kref_init(&rdata->refcount); INIT_WORK(&rdata->work, complete); INIT_LIST_HEAD(&rdata->pages); } return rdata; } -static void -cifs_readdata_free(struct cifs_readdata *rdata) +void +cifs_readdata_release(struct kref *refcount) { - cifsFileInfo_put(rdata->cfile); + struct cifs_readdata *rdata = container_of(refcount, + struct cifs_readdata, refcount); + + if (rdata->cfile) + cifsFileInfo_put(rdata->cfile); + kfree(rdata); } @@ -2651,7 +2657,7 @@ cifs_readv_complete(struct work_struct *work) page_cache_release(page); } - cifs_readdata_free(rdata); + kref_put(&rdata->refcount, cifs_readdata_release); } static int @@ -2837,9 +2843,8 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, } spin_lock(&cifs_file_list_lock); - cifsFileInfo_get(open_file); spin_unlock(&cifs_file_list_lock); - rdata->cfile = open_file; + rdata->cfile = cifsFileInfo_get(open_file); rdata->mapping = mapping; rdata->offset = offset; rdata->bytes = bytes; @@ -2864,9 +2869,11 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, unlock_page(page); page_cache_release(page); } - cifs_readdata_free(rdata); + kref_put(&rdata->refcount, cifs_readdata_release); break; } + + kref_put(&rdata->refcount, cifs_readdata_release); } return rc; -- cgit v0.10.2 From 2a1bb13853300bbb5a58eab006189d2c0dc215a0 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 May 2012 07:13:17 -0400 Subject: cifs: add wrapper for cifs_async_readv to retry opening file We'll need this same bit of code for the uncached case. Signed-off-by: Jeff Layton diff --git a/fs/cifs/file.c b/fs/cifs/file.c index ae285e0..d2a4259 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2366,6 +2366,23 @@ cifs_readdata_release(struct kref *refcount) kfree(rdata); } +static int +cifs_retry_async_readv(struct cifs_readdata *rdata) +{ + int rc; + + do { + if (rdata->cfile->invalidHandle) { + rc = cifs_reopen_file(rdata->cfile, true); + if (rc != 0) + continue; + } + rc = cifs_async_readv(rdata); + } while (rc == -EAGAIN); + + return rc; +} + static ssize_t cifs_iovec_read(struct file *file, const struct iovec *iov, unsigned long nr_segs, loff_t *poffset) @@ -2852,15 +2869,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, rdata->marshal_iov = cifs_readpages_marshal_iov; list_splice_init(&tmplist, &rdata->pages); - do { - if (open_file->invalidHandle) { - rc = cifs_reopen_file(open_file, true); - if (rc != 0) - continue; - } - rc = cifs_async_readv(rdata); - } while (rc == -EAGAIN); - + rc = cifs_retry_async_readv(rdata); if (rc != 0) { list_for_each_entry_safe(page, tpage, &rdata->pages, lru) { -- cgit v0.10.2 From 1c89254926c0643b99541d422c909762479aeef8 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 May 2012 07:13:17 -0400 Subject: cifs: convert cifs_iovec_read to use async reads Convert cifs_iovec_read to use async I/O. This also raises the limit on the rsize for uncached reads. We first allocate a set of pages to hold the replies, then issue the reads in parallel and then collect the replies and copy the results into the iovec. A possible future optimization would be to kmap and inline the iovec buffers and read the data directly from the socket into that. That would require some rather complex conversion of the iovec into a kvec however. Signed-off-by: Jeff Layton diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 63e91c7..eeb789d 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -465,6 +465,8 @@ extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8, /* asynchronous read support */ struct cifs_readdata { struct kref refcount; + struct list_head list; + struct completion done; struct cifsFileInfo *cfile; struct address_space *mapping; __u64 offset; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index d2a4259..4b5fe39 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2348,6 +2348,8 @@ cifs_readdata_alloc(unsigned int nr_vecs, work_func_t complete) sizeof(struct kvec) * nr_vecs, GFP_KERNEL); if (rdata != NULL) { kref_init(&rdata->refcount); + INIT_LIST_HEAD(&rdata->list); + init_completion(&rdata->done); INIT_WORK(&rdata->work, complete); INIT_LIST_HEAD(&rdata->pages); } @@ -2367,6 +2369,45 @@ cifs_readdata_release(struct kref *refcount) } static int +cifs_read_allocate_pages(struct list_head *list, unsigned int npages) +{ + int rc = 0; + struct page *page, *tpage; + unsigned int i; + + for (i = 0; i < npages; i++) { + page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); + if (!page) { + rc = -ENOMEM; + break; + } + list_add(&page->lru, list); + } + + if (rc) { + list_for_each_entry_safe(page, tpage, list, lru) { + list_del(&page->lru); + put_page(page); + } + } + return rc; +} + +static void +cifs_uncached_readdata_release(struct kref *refcount) +{ + struct page *page, *tpage; + struct cifs_readdata *rdata = container_of(refcount, + struct cifs_readdata, refcount); + + list_for_each_entry_safe(page, tpage, &rdata->pages, lru) { + list_del(&page->lru); + put_page(page); + } + cifs_readdata_release(refcount); +} + +static int cifs_retry_async_readv(struct cifs_readdata *rdata) { int rc; @@ -2383,24 +2424,139 @@ cifs_retry_async_readv(struct cifs_readdata *rdata) return rc; } +/** + * cifs_readdata_to_iov - copy data from pages in response to an iovec + * @rdata: the readdata response with list of pages holding data + * @iov: vector in which we should copy the data + * @nr_segs: number of segments in vector + * @offset: offset into file of the first iovec + * @copied: used to return the amount of data copied to the iov + * + * This function copies data from a list of pages in a readdata response into + * an array of iovecs. It will first calculate where the data should go + * based on the info in the readdata and then copy the data into that spot. + */ +static ssize_t +cifs_readdata_to_iov(struct cifs_readdata *rdata, const struct iovec *iov, + unsigned long nr_segs, loff_t offset, ssize_t *copied) +{ + int rc = 0; + struct iov_iter ii; + size_t pos = rdata->offset - offset; + struct page *page, *tpage; + ssize_t remaining = rdata->bytes; + unsigned char *pdata; + + /* set up iov_iter and advance to the correct offset */ + iov_iter_init(&ii, iov, nr_segs, iov_length(iov, nr_segs), 0); + iov_iter_advance(&ii, pos); + + *copied = 0; + list_for_each_entry_safe(page, tpage, &rdata->pages, lru) { + ssize_t copy; + + /* copy a whole page or whatever's left */ + copy = min_t(ssize_t, remaining, PAGE_SIZE); + + /* ...but limit it to whatever space is left in the iov */ + copy = min_t(ssize_t, copy, iov_iter_count(&ii)); + + /* go while there's data to be copied and no errors */ + if (copy && !rc) { + pdata = kmap(page); + rc = memcpy_toiovecend(ii.iov, pdata, ii.iov_offset, + (int)copy); + kunmap(page); + if (!rc) { + *copied += copy; + remaining -= copy; + iov_iter_advance(&ii, copy); + } + } + + list_del(&page->lru); + put_page(page); + } + + return rc; +} + +static void +cifs_uncached_readv_complete(struct work_struct *work) +{ + struct cifs_readdata *rdata = container_of(work, + struct cifs_readdata, work); + + /* if the result is non-zero then the pages weren't kmapped */ + if (rdata->result == 0) { + struct page *page; + + list_for_each_entry(page, &rdata->pages, lru) + kunmap(page); + } + + complete(&rdata->done); + kref_put(&rdata->refcount, cifs_uncached_readdata_release); +} + +static int +cifs_uncached_read_marshal_iov(struct cifs_readdata *rdata, + unsigned int remaining) +{ + int len = 0; + struct page *page, *tpage; + + rdata->nr_iov = 1; + list_for_each_entry_safe(page, tpage, &rdata->pages, lru) { + if (remaining >= PAGE_SIZE) { + /* enough data to fill the page */ + rdata->iov[rdata->nr_iov].iov_base = kmap(page); + rdata->iov[rdata->nr_iov].iov_len = PAGE_SIZE; + cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu", + rdata->nr_iov, page->index, + rdata->iov[rdata->nr_iov].iov_base, + rdata->iov[rdata->nr_iov].iov_len); + ++rdata->nr_iov; + len += PAGE_SIZE; + remaining -= PAGE_SIZE; + } else if (remaining > 0) { + /* enough for partial page, fill and zero the rest */ + rdata->iov[rdata->nr_iov].iov_base = kmap(page); + rdata->iov[rdata->nr_iov].iov_len = remaining; + cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu", + rdata->nr_iov, page->index, + rdata->iov[rdata->nr_iov].iov_base, + rdata->iov[rdata->nr_iov].iov_len); + memset(rdata->iov[rdata->nr_iov].iov_base + remaining, + '\0', PAGE_SIZE - remaining); + ++rdata->nr_iov; + len += remaining; + remaining = 0; + } else { + /* no need to hold page hostage */ + list_del(&page->lru); + put_page(page); + } + } + + return len; +} + static ssize_t cifs_iovec_read(struct file *file, const struct iovec *iov, unsigned long nr_segs, loff_t *poffset) { - int rc; - int xid; - ssize_t total_read; - unsigned int bytes_read = 0; + ssize_t rc; size_t len, cur_len; - int iov_offset = 0; + ssize_t total_read = 0; + loff_t offset = *poffset; + unsigned int npages; struct cifs_sb_info *cifs_sb; - struct cifs_tcon *pTcon; + struct cifs_tcon *tcon; struct cifsFileInfo *open_file; - struct smb_com_read_rsp *pSMBr; - struct cifs_io_parms io_parms; - char *read_data; - unsigned int rsize; - __u32 pid; + struct cifs_readdata *rdata, *tmp; + struct list_head rdata_list; + pid_t pid; if (!nr_segs) return 0; @@ -2409,14 +2565,10 @@ cifs_iovec_read(struct file *file, const struct iovec *iov, if (!len) return 0; - xid = GetXid(); + INIT_LIST_HEAD(&rdata_list); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - - /* FIXME: set up handlers for larger reads and/or convert to async */ - rsize = min_t(unsigned int, cifs_sb->rsize, CIFSMaxBufSize); - open_file = file->private_data; - pTcon = tlink_tcon(open_file->tlink); + tcon = tlink_tcon(open_file->tlink); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) pid = open_file->pid; @@ -2426,56 +2578,78 @@ cifs_iovec_read(struct file *file, const struct iovec *iov, if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, "attempting read on write only file instance"); - for (total_read = 0; total_read < len; total_read += bytes_read) { - cur_len = min_t(const size_t, len - total_read, rsize); - rc = -EAGAIN; - read_data = NULL; + do { + cur_len = min_t(const size_t, len - total_read, cifs_sb->rsize); + npages = DIV_ROUND_UP(cur_len, PAGE_SIZE); - while (rc == -EAGAIN) { - int buf_type = CIFS_NO_BUFFER; - if (open_file->invalidHandle) { - rc = cifs_reopen_file(open_file, true); - if (rc != 0) - break; - } - io_parms.netfid = open_file->netfid; - io_parms.pid = pid; - io_parms.tcon = pTcon; - io_parms.offset = *poffset; - io_parms.length = cur_len; - rc = CIFSSMBRead(xid, &io_parms, &bytes_read, - &read_data, &buf_type); - pSMBr = (struct smb_com_read_rsp *)read_data; - if (read_data) { - char *data_offset = read_data + 4 + - le16_to_cpu(pSMBr->DataOffset); - if (memcpy_toiovecend(iov, data_offset, - iov_offset, bytes_read)) - rc = -EFAULT; - if (buf_type == CIFS_SMALL_BUFFER) - cifs_small_buf_release(read_data); - else if (buf_type == CIFS_LARGE_BUFFER) - cifs_buf_release(read_data); - read_data = NULL; - iov_offset += bytes_read; - } + /* allocate a readdata struct */ + rdata = cifs_readdata_alloc(npages, + cifs_uncached_readv_complete); + if (!rdata) { + rc = -ENOMEM; + goto error; } - if (rc || (bytes_read == 0)) { - if (total_read) { - break; - } else { - FreeXid(xid); - return rc; + rc = cifs_read_allocate_pages(&rdata->pages, npages); + if (rc) + goto error; + + rdata->cfile = cifsFileInfo_get(open_file); + rdata->offset = offset; + rdata->bytes = cur_len; + rdata->pid = pid; + rdata->marshal_iov = cifs_uncached_read_marshal_iov; + + rc = cifs_retry_async_readv(rdata); +error: + if (rc) { + kref_put(&rdata->refcount, + cifs_uncached_readdata_release); + break; + } + + list_add_tail(&rdata->list, &rdata_list); + offset += cur_len; + len -= cur_len; + } while (len > 0); + + /* if at least one read request send succeeded, then reset rc */ + if (!list_empty(&rdata_list)) + rc = 0; + + /* the loop below should proceed in the order of increasing offsets */ +restart_loop: + list_for_each_entry_safe(rdata, tmp, &rdata_list, list) { + if (!rc) { + ssize_t copied; + + /* FIXME: freezable sleep too? */ + rc = wait_for_completion_killable(&rdata->done); + if (rc) + rc = -EINTR; + else if (rdata->result) + rc = rdata->result; + else { + rc = cifs_readdata_to_iov(rdata, iov, + nr_segs, *poffset, + &copied); + total_read += copied; + } + + /* resend call if it's a retryable error */ + if (rc == -EAGAIN) { + rc = cifs_retry_async_readv(rdata); + goto restart_loop; } - } else { - cifs_stats_bytes_read(pTcon, bytes_read); - *poffset += bytes_read; } + list_del_init(&rdata->list); + kref_put(&rdata->refcount, cifs_uncached_readdata_release); } - FreeXid(xid); - return total_read; + cifs_stats_bytes_read(tcon, total_read); + *poffset += total_read; + + return total_read ? total_read : rc; } ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov, -- cgit v0.10.2 From 5e500ed125f3b153aa3f228657723189e9f4c82e Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 May 2012 07:13:17 -0400 Subject: cifs: remove legacy MultiuserMount option We've now warned about this for two releases. Remove it for 3.5. Signed-off-by: Jeff Layton diff --git a/fs/cifs/README b/fs/cifs/README index b7d782b..22ab7b5 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -608,11 +608,6 @@ Stats Lists summary resource usage information as well as per in the kernel configuration. Configuration pseudo-files: -MultiuserMount If set to one, more than one CIFS session to - the same server ip address can be established - if more than one uid accesses the same mount - point and if the uids user/password mapping - information is available. (default is 0) PacketSigningEnabled If set to one, cifs packet signing is enabled and will be used if the server requires it. If set to two, cifs packet signing is diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 2704646..8aae8e2 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -420,7 +420,6 @@ static struct proc_dir_entry *proc_fs_cifs; static const struct file_operations cifsFYI_proc_fops; static const struct file_operations cifs_lookup_cache_proc_fops; static const struct file_operations traceSMB_proc_fops; -static const struct file_operations cifs_multiuser_mount_proc_fops; static const struct file_operations cifs_security_flags_proc_fops; static const struct file_operations cifs_linux_ext_proc_fops; @@ -440,8 +439,6 @@ cifs_proc_init(void) proc_create("traceSMB", 0, proc_fs_cifs, &traceSMB_proc_fops); proc_create("LinuxExtensionsEnabled", 0, proc_fs_cifs, &cifs_linux_ext_proc_fops); - proc_create("MultiuserMount", 0, proc_fs_cifs, - &cifs_multiuser_mount_proc_fops); proc_create("SecurityFlags", 0, proc_fs_cifs, &cifs_security_flags_proc_fops); proc_create("LookupCacheEnabled", 0, proc_fs_cifs, @@ -460,7 +457,6 @@ cifs_proc_clean(void) #ifdef CONFIG_CIFS_STATS remove_proc_entry("Stats", proc_fs_cifs); #endif - remove_proc_entry("MultiuserMount", proc_fs_cifs); remove_proc_entry("SecurityFlags", proc_fs_cifs); remove_proc_entry("LinuxExtensionsEnabled", proc_fs_cifs); remove_proc_entry("LookupCacheEnabled", proc_fs_cifs); @@ -617,52 +613,6 @@ static const struct file_operations traceSMB_proc_fops = { .write = traceSMB_proc_write, }; -static int cifs_multiuser_mount_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", multiuser_mount); - return 0; -} - -static int cifs_multiuser_mount_proc_open(struct inode *inode, struct file *fh) -{ - return single_open(fh, cifs_multiuser_mount_proc_show, NULL); -} - -static ssize_t cifs_multiuser_mount_proc_write(struct file *file, - const char __user *buffer, size_t count, loff_t *ppos) -{ - char c; - int rc; - static bool warned; - - rc = get_user(c, buffer); - if (rc) - return rc; - if (c == '0' || c == 'n' || c == 'N') - multiuser_mount = 0; - else if (c == '1' || c == 'y' || c == 'Y') { - multiuser_mount = 1; - if (!warned) { - warned = true; - printk(KERN_WARNING "CIFS VFS: The legacy multiuser " - "mount code is scheduled to be deprecated in " - "3.5. Please switch to using the multiuser " - "mount option."); - } - } - - return count; -} - -static const struct file_operations cifs_multiuser_mount_proc_fops = { - .owner = THIS_MODULE, - .open = cifs_multiuser_mount_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = cifs_multiuser_mount_proc_write, -}; - static int cifs_security_flags_proc_show(struct seq_file *m, void *v) { seq_printf(m, "0x%x\n", global_secflags); diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 541ef81..c45f170 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -56,7 +56,6 @@ int traceSMB = 0; bool enable_oplocks = true; unsigned int linuxExtEnabled = 1; unsigned int lookupCacheEnabled = 1; -unsigned int multiuser_mount = 0; unsigned int global_secflags = CIFSSEC_DEF; /* unsigned int ntlmv2_support = 0; */ unsigned int sign_CIFS_PDUs = 1; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 4ff6313..a867d99 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1042,12 +1042,7 @@ GLOBAL_EXTERN atomic_t smBufAllocCount; GLOBAL_EXTERN atomic_t midCount; /* Misc globals */ -GLOBAL_EXTERN unsigned int multiuser_mount; /* if enabled allows new sessions - to be established on existing mount if we - have the uid/password or Kerberos credential - or equivalent for current user */ -/* enable or disable oplocks */ -GLOBAL_EXTERN bool enable_oplocks; +GLOBAL_EXTERN bool enable_oplocks; /* enable or disable oplocks */ GLOBAL_EXTERN unsigned int lookupCacheEnabled; GLOBAL_EXTERN unsigned int global_secflags; /* if on, session setup sent with more secure ntlmssp2 challenge/resp */ diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index c29d1aa..d2bb1e7 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -306,8 +306,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , const struct cifs_tcon *treeCon, int word_count /* length of fixed section (word count) in two byte units */) { - struct list_head *temp_item; - struct cifs_ses *ses; char *temp = (char *) buffer; memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ @@ -337,51 +335,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , /* Uid is not converted */ buffer->Uid = treeCon->ses->Suid; buffer->Mid = GetNextMid(treeCon->ses->server); - if (multiuser_mount != 0) { - /* For the multiuser case, there are few obvious technically */ - /* possible mechanisms to match the local linux user (uid) */ - /* to a valid remote smb user (smb_uid): */ - /* 1) Query Winbind (or other local pam/nss daemon */ - /* for userid/password/logon_domain or credential */ - /* 2) Query Winbind for uid to sid to username mapping */ - /* and see if we have a matching password for existing*/ - /* session for that user perhas getting password by */ - /* adding a new pam_cifs module that stores passwords */ - /* so that the cifs vfs can get at that for all logged*/ - /* on users */ - /* 3) (Which is the mechanism we have chosen) */ - /* Search through sessions to the same server for a */ - /* a match on the uid that was passed in on mount */ - /* with the current processes uid (or euid?) and use */ - /* that smb uid. If no existing smb session for */ - /* that uid found, use the default smb session ie */ - /* the smb session for the volume mounted which is */ - /* the same as would be used if the multiuser mount */ - /* flag were disabled. */ - - /* BB Add support for establishing new tCon and SMB Session */ - /* with userid/password pairs found on the smb session */ - /* for other target tcp/ip addresses BB */ - if (current_fsuid() != treeCon->ses->linux_uid) { - cFYI(1, "Multiuser mode and UID " - "did not match tcon uid"); - spin_lock(&cifs_tcp_ses_lock); - list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) { - ses = list_entry(temp_item, struct cifs_ses, smb_ses_list); - if (ses->linux_uid == current_fsuid()) { - if (ses->server == treeCon->ses->server) { - cFYI(1, "found matching uid substitute right smb_uid"); - buffer->Uid = ses->Suid; - break; - } else { - /* BB eventually call cifs_setup_session here */ - cFYI(1, "local UID found but no smb sess with this server exists"); - } - } - } - spin_unlock(&cifs_tcp_ses_lock); - } - } } if (treeCon->Flags & SMB_SHARE_IS_IN_DFS) buffer->Flags2 |= SMBFLG2_DFS; -- cgit v0.10.2 From 4d61cd6ec764368689fab3bd19e78d76c1e6b176 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 May 2012 07:13:17 -0400 Subject: cifs: add a deprecation warning to CIFS_IOC_CHECKUMOUNT ioctl This was used by an ancient version of umount.cifs and in nowhere else that I'm aware of. Let's add a warning now and dump it for 3.7. Signed-off-by: Jeff Layton diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 4221b5e..6d2667f 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -51,7 +51,15 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) cifs_sb = CIFS_SB(inode->i_sb); switch (command) { + static bool warned = false; case CIFS_IOC_CHECKUMOUNT: + if (!warned) { + warned = true; + cERROR(1, "the CIFS_IOC_CHECKMOUNT ioctl will " + "be deprecated in 3.7. Please " + "migrate away from the use of " + "umount.cifs"); + } cFYI(1, "User unmount attempted"); if (cifs_sb->mnt_uid == current_uid()) rc = 0; -- cgit v0.10.2 From 15b6a47322940beb74a83ffc1632c1ee1d00f35b Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 May 2012 07:50:15 -0400 Subject: cifs: add a cache= option to better describe the different cache flavors Currently, we have several mount options that control cifs' cache behavior, but those options aren't considered to be mutually exclusive. The result is poorly-defined when someone specifies more than one of these options at mount time. Fix this by adding a new cache= mount option that will supercede "strictcache", and "forcedirectio". That will help make it clear that these options are mutually exclusive. Also, change the legacy options to be mutually exclusive too, to ensure that users don't get surprises. Reviewed-by: Pavel Shilovsky Signed-off-by: Jeff Layton diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e0b56d7..4898759 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -102,7 +102,7 @@ enum { Opt_srcaddr, Opt_prefixpath, Opt_iocharset, Opt_sockopt, Opt_netbiosname, Opt_servern, - Opt_ver, Opt_sec, + Opt_ver, Opt_sec, Opt_cache, /* Mount options to be ignored */ Opt_ignore, @@ -213,6 +213,7 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_ver, "vers=%s" }, { Opt_ver, "version=%s" }, { Opt_sec, "sec=%s" }, + { Opt_cache, "cache=%s" }, { Opt_ignore, "cred" }, { Opt_ignore, "credentials" }, @@ -261,6 +262,21 @@ static const match_table_t cifs_secflavor_tokens = { { Opt_sec_err, NULL } }; +/* cache flavors */ +enum { + Opt_cache_loose, + Opt_cache_strict, + Opt_cache_none, + Opt_cache_err +}; + +static const match_table_t cifs_cacheflavor_tokens = { + { Opt_cache_loose, "loose" }, + { Opt_cache_strict, "strict" }, + { Opt_cache_none, "none" }, + { Opt_cache_err, NULL } +}; + static int ip_connect(struct TCP_Server_Info *server); static int generic_ip_connect(struct TCP_Server_Info *server); static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); @@ -1186,6 +1202,31 @@ static int cifs_parse_security_flavors(char *value, } static int +cifs_parse_cache_flavor(char *value, struct smb_vol *vol) +{ + substring_t args[MAX_OPT_ARGS]; + + switch (match_token(value, cifs_cacheflavor_tokens, args)) { + case Opt_cache_loose: + vol->direct_io = false; + vol->strict_io = false; + break; + case Opt_cache_strict: + vol->direct_io = false; + vol->strict_io = true; + break; + case Opt_cache_none: + vol->direct_io = true; + vol->strict_io = false; + break; + default: + cERROR(1, "bad cache= option: %s", value); + return 1; + } + return 0; +} + +static int cifs_parse_mount_options(const char *mountdata, const char *devname, struct smb_vol *vol) { @@ -1414,10 +1455,12 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, vol->seal = 1; break; case Opt_direct: - vol->direct_io = 1; + vol->direct_io = true; + vol->strict_io = false; break; case Opt_strictcache: - vol->strict_io = 1; + vol->direct_io = false; + vol->strict_io = true; break; case Opt_noac: printk(KERN_WARNING "CIFS: Mount option noac not " @@ -1838,6 +1881,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, if (cifs_parse_security_flavors(string, vol) != 0) goto cifs_parse_mount_err; break; + case Opt_cache: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (cifs_parse_cache_flavor(string, vol) != 0) + goto cifs_parse_mount_err; + break; default: /* * An option we don't recognize. Save it off for later -- cgit v0.10.2 From 09983b2fab80fa037b1dcf9a11de5a70df59ef7f Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 May 2012 07:53:00 -0400 Subject: cifs: add deprecation warnings to strictcache and forcedirectio Leave them in for 2 releases and remove for 3.7. Reviewed-by: Pavel Shilovsky Signed-off-by: Jeff Layton diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 4898759..3418680 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1457,10 +1457,16 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, case Opt_direct: vol->direct_io = true; vol->strict_io = false; + cERROR(1, "The \"directio\" option will be removed in " + "3.7. Please switch to the \"cache=none\" " + "option."); break; case Opt_strictcache: vol->direct_io = false; vol->strict_io = true; + cERROR(1, "The \"strictcache\" option will be removed " + "in 3.7. Please switch to the \"cache=strict\" " + "option."); break; case Opt_noac: printk(KERN_WARNING "CIFS: Mount option noac not " -- cgit v0.10.2 From d06b5056ae160453c4be17e24e8cf08d65f4569f Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 May 2012 07:53:01 -0400 Subject: cifs: display cache= option in /proc/mounts ...and deprecate the display of strictcache, forcedirectio, and fsc as separate options. Reviewed-by: Pavel Shilovsky Signed-off-by: Jeff Layton diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index c45f170..f718d57 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -328,6 +328,19 @@ cifs_show_security(struct seq_file *s, struct TCP_Server_Info *server) seq_printf(s, "i"); } +static void +cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb) +{ + seq_printf(s, ",cache="); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) + seq_printf(s, "strict"); + else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) + seq_printf(s, "none"); + else + seq_printf(s, "loose"); +} + /* * cifs_show_options() is for displaying mount options in /proc/mounts. * Not all settable options are displayed but most of the important @@ -342,6 +355,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root) srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; cifs_show_security(s, tcon->ses->server); + cifs_show_cache_flavor(s, cifs_sb); seq_printf(s, ",unc=%s", tcon->treeName); @@ -407,8 +421,6 @@ cifs_show_options(struct seq_file *s, struct dentry *root) seq_printf(s, ",rwpidforward"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) seq_printf(s, ",forcemand"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) - seq_printf(s, ",directio"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) seq_printf(s, ",nouser_xattr"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) @@ -431,8 +443,6 @@ cifs_show_options(struct seq_file *s, struct dentry *root) seq_printf(s, ",nostrictsync"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) seq_printf(s, ",noperm"); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) - seq_printf(s, ",strictcache"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) seq_printf(s, ",backupuid=%u", cifs_sb->mnt_backupuid); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) -- cgit v0.10.2 From 296838b182ebad919074bf324e1667d28a04b936 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 May 2012 07:53:01 -0400 Subject: cifs: add warning about change in default cache semantics in 3.7 Add a warning that will be displayed when there is no cache= option specified. We want to ensure that users are aware of the change in defaults coming in 3.7. Reviewed-by: Pavel Shilovsky Signed-off-by: Jeff Layton diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 3418680..c49d494 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1244,6 +1244,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, char *string = NULL; char *tmp_end, *value; char delim; + bool cache_specified = false; + static bool cache_warned = false; separator[0] = ','; separator[1] = 0; @@ -1455,6 +1457,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, vol->seal = 1; break; case Opt_direct: + cache_specified = true; vol->direct_io = true; vol->strict_io = false; cERROR(1, "The \"directio\" option will be removed in " @@ -1462,6 +1465,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, "option."); break; case Opt_strictcache: + cache_specified = true; vol->direct_io = false; vol->strict_io = true; cERROR(1, "The \"strictcache\" option will be removed " @@ -1888,6 +1892,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, goto cifs_parse_mount_err; break; case Opt_cache: + cache_specified = true; string = match_strdup(args); if (string == NULL) goto out_nomem; @@ -1938,6 +1943,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, printk(KERN_NOTICE "CIFS: ignoring forcegid mount option " "specified with no gid= option.\n"); + /* FIXME: remove this block in 3.7 */ + if (!cache_specified && !cache_warned) { + cache_warned = true; + printk(KERN_NOTICE "CIFS: no cache= option specified, using " + "\"cache=loose\". This default will change " + "to \"cache=strict\" in 3.7.\n"); + } + kfree(mountdata_copy); return 0; -- cgit v0.10.2 From 5249af32da5330c0bcaf0412a32aa30c5e93e908 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 15 May 2012 12:04:03 -0400 Subject: cifs: remove the vers= and version= synonyms for ver= We want these to mean something different entirely, and the mount.cifs helper only ever passed in ver= automatically. Also, don't allow ver=cifs anymore since that was never passed in by the mount helper. Signed-off-by: Jeff Layton diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index c49d494..4a6baa8 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -210,8 +210,6 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_netbiosname, "netbiosname=%s" }, { Opt_servern, "servern=%s" }, { Opt_ver, "ver=%s" }, - { Opt_ver, "vers=%s" }, - { Opt_ver, "version=%s" }, { Opt_sec, "sec=%s" }, { Opt_cache, "cache=%s" }, @@ -1874,8 +1872,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, if (string == NULL) goto out_nomem; - if (strnicmp(string, "cifs", 4) == 0 || - strnicmp(string, "1", 1) == 0) { + if (strnicmp(string, "1", 1) == 0) { /* This is the default */ break; } -- cgit v0.10.2 From 23db65f511e6ee98ad767833f2ec58b0568ba32b Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 15 May 2012 12:20:51 -0400 Subject: cifs: add a smb_version_operations/values structures and a smb_version enum We need a way to dispatch different operations for different versions. Behold the smb_version_operations/values structures. For now, those structures just hold the version enum value and nothing uses them. Eventually, we'll expand them to cover other operations/values as we change the callers to dispatch from here. Signed-off-by: Jeff Layton Signed-off-by: Pavel Shilovsky diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index 005d524..0fe7025 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_CIFS) += cifs.o cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \ link.o misc.o netmisc.o smbencrypt.o transport.o asn1.o \ cifs_unicode.o nterr.o xattr.o cifsencrypt.o \ - readdir.o ioctl.o sess.o export.o + readdir.o ioctl.o sess.o export.o smb1ops.o cifs-$(CONFIG_CIFS_ACL) += cifsacl.o diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index f718d57..8a6928c 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -354,6 +354,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root) struct sockaddr *srcaddr; srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; + seq_printf(s, ",vers=%s", tcon->ses->server->vals->version_string); cifs_show_security(s, tcon->ses->server); cifs_show_cache_flavor(s, cifs_sb); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index a867d99..812e22a 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -150,6 +150,17 @@ struct cifs_cred { ***************************************************************** */ +enum smb_version { + Smb_1 = 1, +}; + +struct smb_version_operations { +}; + +struct smb_version_values { + char *version_string; +}; + struct smb_vol { char *username; char *password; @@ -205,6 +216,8 @@ struct smb_vol { bool sockopt_tcp_nodelay:1; unsigned short int port; unsigned long actimeo; /* attribute cache timeout (jiffies) */ + struct smb_version_operations *ops; + struct smb_version_values *vals; char *prepath; struct sockaddr_storage srcaddr; /* allow binding to a local IP */ struct nls_table *local_nls; @@ -242,6 +255,8 @@ struct TCP_Server_Info { int srv_count; /* reference counter */ /* 15 character server name + 0x20 16th byte indicating type = srv */ char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; + struct smb_version_operations *ops; + struct smb_version_values *vals; enum statusEnum tcpStatus; /* what we think the status is */ char *hostname; /* hostname portion of UNC string */ struct socket *ssocket; @@ -1069,4 +1084,8 @@ void cifs_oplock_break(struct work_struct *work); extern const struct slow_work_ops cifs_oplock_break_ops; extern struct workqueue_struct *cifsiod_wq; +/* Operations for different SMB versions */ +#define SMB1_VERSION_STRING "1.0" +extern struct smb_version_operations smb1_operations; +extern struct smb_version_values smb1_values; #endif /* _CIFS_GLOB_H */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 4a6baa8..5ac20fc 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -102,7 +102,7 @@ enum { Opt_srcaddr, Opt_prefixpath, Opt_iocharset, Opt_sockopt, Opt_netbiosname, Opt_servern, - Opt_ver, Opt_sec, Opt_cache, + Opt_ver, Opt_vers, Opt_sec, Opt_cache, /* Mount options to be ignored */ Opt_ignore, @@ -210,6 +210,7 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_netbiosname, "netbiosname=%s" }, { Opt_servern, "servern=%s" }, { Opt_ver, "ver=%s" }, + { Opt_vers, "vers=%s" }, { Opt_sec, "sec=%s" }, { Opt_cache, "cache=%s" }, @@ -275,6 +276,10 @@ static const match_table_t cifs_cacheflavor_tokens = { { Opt_cache_err, NULL } }; +static const match_table_t cifs_smb_version_tokens = { + { Smb_1, SMB1_VERSION_STRING }, +}; + static int ip_connect(struct TCP_Server_Info *server); static int generic_ip_connect(struct TCP_Server_Info *server); static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); @@ -1225,6 +1230,23 @@ cifs_parse_cache_flavor(char *value, struct smb_vol *vol) } static int +cifs_parse_smb_version(char *value, struct smb_vol *vol) +{ + substring_t args[MAX_OPT_ARGS]; + + switch (match_token(value, cifs_smb_version_tokens, args)) { + case Smb_1: + vol->ops = &smb1_operations; + vol->vals = &smb1_values; + break; + default: + cERROR(1, "Unknown vers= option specified: %s", value); + return 1; + } + return 0; +} + +static int cifs_parse_mount_options(const char *mountdata, const char *devname, struct smb_vol *vol) { @@ -1277,6 +1299,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, vol->actimeo = CIFS_DEF_ACTIMEO; + /* FIXME: add autonegotiation -- for now, SMB1 is default */ + vol->ops = &smb1_operations; + vol->vals = &smb1_values; + if (!mountdata) goto cifs_parse_mount_err; @@ -1880,6 +1906,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, printk(KERN_WARNING "CIFS: Invalid version" " specified\n"); goto cifs_parse_mount_err; + case Opt_vers: + string = match_strdup(args); + if (string == NULL) + goto out_nomem; + + if (cifs_parse_smb_version(string, vol) != 0) + goto cifs_parse_mount_err; + break; case Opt_sec: string = match_strdup(args); if (string == NULL) @@ -2108,6 +2142,9 @@ match_security(struct TCP_Server_Info *server, struct smb_vol *vol) static int match_server(struct TCP_Server_Info *server, struct sockaddr *addr, struct smb_vol *vol) { + if ((server->vals != vol->vals) || (server->ops != vol->ops)) + return 0; + if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns)) return 0; @@ -2230,6 +2267,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info) goto out_err; } + tcp_ses->ops = volume_info->ops; + tcp_ses->vals = volume_info->vals; cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns)); tcp_ses->hostname = extract_hostname(volume_info->UNC); if (IS_ERR(tcp_ses->hostname)) { @@ -3636,6 +3675,7 @@ cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, if (cifs_parse_mount_options(mount_data, devname, volume_info)) return -EINVAL; + if (volume_info->nullauth) { cFYI(1, "Anonymous login"); kfree(volume_info->username); diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c new file mode 100644 index 0000000..d2850d1 --- /dev/null +++ b/fs/cifs/smb1ops.c @@ -0,0 +1,27 @@ +/* + * SMB1 (CIFS) version specific operations + * + * Copyright (c) 2012, Jeff Layton + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "cifsglob.h" + +struct smb_version_operations smb1_operations = { +}; + +struct smb_version_values smb1_values = { + .version_string = SMB1_VERSION_STRING, +}; -- cgit v0.10.2 From 121b046af54437b084aa0e4be967ae5aed7528b5 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 15 May 2012 12:21:10 -0400 Subject: cifs: convert send_nt_cancel into a version specific op For SMB2, this should be a no-op. Obviously if we wanted to do something for the SMB2 case, we could also define an operation here for it. Signed-off-by: Jeff Layton Signed-off-by: Pavel Shilovsky diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 812e22a..c41bf6d 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -154,7 +154,12 @@ enum smb_version { Smb_1 = 1, }; +struct mid_q_entry; +struct TCP_Server_Info; + struct smb_version_operations { + int (*send_cancel)(struct TCP_Server_Info *, void *, + struct mid_q_entry *); }; struct smb_version_values { @@ -718,7 +723,6 @@ static inline void cifs_stats_bytes_read(struct cifs_tcon *tcon, #endif -struct mid_q_entry; /* * This is the prototype for the mid receive function. This function is for diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index d2850d1..fa486f0 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -18,8 +18,49 @@ */ #include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" + +/* + * An NT cancel request header looks just like the original request except: + * + * The Command is SMB_COM_NT_CANCEL + * The WordCount is zeroed out + * The ByteCount is zeroed out + * + * This function mangles an existing request buffer into a + * SMB_COM_NT_CANCEL request and then sends it. + */ +static int +send_nt_cancel(struct TCP_Server_Info *server, void *buf, + struct mid_q_entry *mid) +{ + int rc = 0; + struct smb_hdr *in_buf = (struct smb_hdr *)buf; + + /* -4 for RFC1001 length and +2 for BCC field */ + in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2); + in_buf->Command = SMB_COM_NT_CANCEL; + in_buf->WordCount = 0; + put_bcc(0, in_buf); + + mutex_lock(&server->srv_mutex); + rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); + if (rc) { + mutex_unlock(&server->srv_mutex); + return rc; + } + rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); + mutex_unlock(&server->srv_mutex); + + cFYI(1, "issued NT_CANCEL for mid %u, rc = %d", + in_buf->Mid, rc); + + return rc; +} struct smb_version_operations smb1_operations = { + .send_cancel = send_nt_cancel, }; struct smb_version_values smb1_values = { diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 0961336..269a5a7 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -483,41 +483,11 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) return rc; } -/* - * An NT cancel request header looks just like the original request except: - * - * The Command is SMB_COM_NT_CANCEL - * The WordCount is zeroed out - * The ByteCount is zeroed out - * - * This function mangles an existing request buffer into a - * SMB_COM_NT_CANCEL request and then sends it. - */ -static int -send_nt_cancel(struct TCP_Server_Info *server, struct smb_hdr *in_buf, - struct mid_q_entry *mid) +static inline int +send_cancel(struct TCP_Server_Info *server, void *buf, struct mid_q_entry *mid) { - int rc = 0; - - /* -4 for RFC1001 length and +2 for BCC field */ - in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2); - in_buf->Command = SMB_COM_NT_CANCEL; - in_buf->WordCount = 0; - put_bcc(0, in_buf); - - mutex_lock(&server->srv_mutex); - rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); - if (rc) { - mutex_unlock(&server->srv_mutex); - return rc; - } - rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); - mutex_unlock(&server->srv_mutex); - - cFYI(1, "issued NT_CANCEL for mid %u, rc = %d", - in_buf->Mid, rc); - - return rc; + return server->ops->send_cancel ? + server->ops->send_cancel(server, buf, mid) : 0; } int @@ -636,7 +606,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, rc = wait_for_response(ses->server, midQ); if (rc != 0) { - send_nt_cancel(ses->server, (struct smb_hdr *)buf, midQ); + send_cancel(ses->server, buf, midQ); spin_lock(&GlobalMid_Lock); if (midQ->mid_state == MID_REQUEST_SUBMITTED) { midQ->callback = DeleteMidQEntry; @@ -753,7 +723,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, rc = wait_for_response(ses->server, midQ); if (rc != 0) { - send_nt_cancel(ses->server, in_buf, midQ); + send_cancel(ses->server, in_buf, midQ); spin_lock(&GlobalMid_Lock); if (midQ->mid_state == MID_REQUEST_SUBMITTED) { /* no longer considered to be "in-flight" */ @@ -898,7 +868,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, if (in_buf->Command == SMB_COM_TRANSACTION2) { /* POSIX lock. We send a NT_CANCEL SMB to cause the blocking lock to return. */ - rc = send_nt_cancel(ses->server, in_buf, midQ); + rc = send_cancel(ses->server, in_buf, midQ); if (rc) { delete_mid(midQ); return rc; @@ -919,7 +889,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, rc = wait_for_response(ses->server, midQ); if (rc) { - send_nt_cancel(ses->server, in_buf, midQ); + send_cancel(ses->server, in_buf, midQ); spin_lock(&GlobalMid_Lock); if (midQ->mid_state == MID_REQUEST_SUBMITTED) { /* no longer considered to be "in-flight" */ -- cgit v0.10.2 From fbd35acadd728eac5d94f360c7cd8cbe12a0379f Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Fri, 24 Feb 2012 15:41:06 +0300 Subject: CIFS: Move locks to cifsFileInfo structure CIFS brlock cache can be used by several file handles if we have a write-caching lease on the file that is supported by SMB2 protocol. Prepate the code to handle this situation correctly by sorting brlocks by a fid to easily push them in portions when lease break comes. Signed-off-by: Pavel Shilovsky diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 8a6928c..d6660f7 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -955,7 +955,6 @@ cifs_init_once(void *inode) struct cifsInodeInfo *cifsi = inode; inode_init_once(&cifsi->vfs_inode); - INIT_LIST_HEAD(&cifsi->llist); mutex_init(&cifsi->lock_mutex); } diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index c41bf6d..03d24a5 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -568,7 +568,6 @@ struct cifsLockInfo { __u64 length; __u32 pid; __u8 type; - __u16 netfid; }; /* @@ -593,6 +592,10 @@ struct cifs_search_info { struct cifsFileInfo { struct list_head tlist; /* pointer to next fid owned by tcon */ struct list_head flist; /* next fid (file instance) for this inode */ + struct list_head llist; /* + * brlocks held by this fid, protected by + * lock_mutex from cifsInodeInfo structure + */ unsigned int uid; /* allows finding which FileInfo structure */ __u32 pid; /* process id who opened file */ __u16 netfid; /* file id from remote */ @@ -635,9 +638,12 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file); */ struct cifsInodeInfo { - struct list_head llist; /* brlocks for this inode */ bool can_cache_brlcks; - struct mutex lock_mutex; /* protect two fields above */ + struct mutex lock_mutex; /* + * protect the field above and llist + * from every cifsFileInfo structure + * from openFileList + */ /* BB add in lists for dirty pages i.e. write caching info for oplock */ struct list_head openFileList; __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 4b5fe39..fc45cd9 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -264,6 +264,7 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file, pCifsFile->tlink = cifs_get_tlink(tlink); mutex_init(&pCifsFile->fh_mutex); INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break); + INIT_LIST_HEAD(&pCifsFile->llist); spin_lock(&cifs_file_list_lock); list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList)); @@ -334,9 +335,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) * is closed anyway. */ mutex_lock(&cifsi->lock_mutex); - list_for_each_entry_safe(li, tmp, &cifsi->llist, llist) { - if (li->netfid != cifs_file->netfid) - continue; + list_for_each_entry_safe(li, tmp, &cifs_file->llist, llist) { list_del(&li->llist); cifs_del_lock_waiters(li); kfree(li); @@ -645,7 +644,7 @@ int cifs_closedir(struct inode *inode, struct file *file) } static struct cifsLockInfo * -cifs_lock_init(__u64 offset, __u64 length, __u8 type, __u16 netfid) +cifs_lock_init(__u64 offset, __u64 length, __u8 type) { struct cifsLockInfo *lock = kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL); @@ -654,7 +653,6 @@ cifs_lock_init(__u64 offset, __u64 length, __u8 type, __u16 netfid) lock->offset = offset; lock->length = length; lock->type = type; - lock->netfid = netfid; lock->pid = current->tgid; INIT_LIST_HEAD(&lock->blist); init_waitqueue_head(&lock->block_q); @@ -672,19 +670,19 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock) } static bool -__cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset, - __u64 length, __u8 type, __u16 netfid, - struct cifsLockInfo **conf_lock) +cifs_find_fid_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, + __u64 length, __u8 type, __u16 netfid, + struct cifsLockInfo **conf_lock) { - struct cifsLockInfo *li, *tmp; + struct cifsLockInfo *li; - list_for_each_entry_safe(li, tmp, &cinode->llist, llist) { + list_for_each_entry(li, &cfile->llist, llist) { if (offset + length <= li->offset || offset >= li->offset + li->length) continue; else if ((type & LOCKING_ANDX_SHARED_LOCK) && - ((netfid == li->netfid && current->tgid == li->pid) || - type == li->type)) + ((netfid == cfile->netfid && current->tgid == li->pid) + || type == li->type)) continue; else { *conf_lock = li; @@ -695,11 +693,23 @@ __cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset, } static bool -cifs_find_lock_conflict(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock, +cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset, + __u64 length, __u8 type, __u16 netfid, struct cifsLockInfo **conf_lock) { - return __cifs_find_lock_conflict(cinode, lock->offset, lock->length, - lock->type, lock->netfid, conf_lock); + bool rc = false; + struct cifsFileInfo *fid, *tmp; + + spin_lock(&cifs_file_list_lock); + list_for_each_entry_safe(fid, tmp, &cinode->openFileList, flist) { + rc = cifs_find_fid_lock_conflict(fid, offset, length, type, + netfid, conf_lock); + if (rc) + break; + } + spin_unlock(&cifs_file_list_lock); + + return rc; } /* @@ -710,17 +720,18 @@ cifs_find_lock_conflict(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock, * the server or 1 otherwise. */ static int -cifs_lock_test(struct cifsInodeInfo *cinode, __u64 offset, __u64 length, - __u8 type, __u16 netfid, struct file_lock *flock) +cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length, + __u8 type, struct file_lock *flock) { int rc = 0; struct cifsLockInfo *conf_lock; + struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); bool exist; mutex_lock(&cinode->lock_mutex); - exist = __cifs_find_lock_conflict(cinode, offset, length, type, netfid, - &conf_lock); + exist = cifs_find_lock_conflict(cinode, offset, length, type, + cfile->netfid, &conf_lock); if (exist) { flock->fl_start = conf_lock->offset; flock->fl_end = conf_lock->offset + conf_lock->length - 1; @@ -739,10 +750,11 @@ cifs_lock_test(struct cifsInodeInfo *cinode, __u64 offset, __u64 length, } static void -cifs_lock_add(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock) +cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock) { + struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); mutex_lock(&cinode->lock_mutex); - list_add_tail(&lock->llist, &cinode->llist); + list_add_tail(&lock->llist, &cfile->llist); mutex_unlock(&cinode->lock_mutex); } @@ -753,10 +765,11 @@ cifs_lock_add(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock) * 3) -EACCESS, if there is a lock that prevents us and wait is false. */ static int -cifs_lock_add_if(struct cifsInodeInfo *cinode, struct cifsLockInfo *lock, +cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock, bool wait) { struct cifsLockInfo *conf_lock; + struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); bool exist; int rc = 0; @@ -764,9 +777,10 @@ try_again: exist = false; mutex_lock(&cinode->lock_mutex); - exist = cifs_find_lock_conflict(cinode, lock, &conf_lock); + exist = cifs_find_lock_conflict(cinode, lock->offset, lock->length, + lock->type, cfile->netfid, &conf_lock); if (!exist && cinode->can_cache_brlcks) { - list_add_tail(&lock->llist, &cinode->llist); + list_add_tail(&lock->llist, &cfile->llist); mutex_unlock(&cinode->lock_mutex); return rc; } @@ -888,7 +902,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile) for (i = 0; i < 2; i++) { cur = buf; num = 0; - list_for_each_entry_safe(li, tmp, &cinode->llist, llist) { + list_for_each_entry_safe(li, tmp, &cfile->llist, llist) { if (li->type != types[i]) continue; cur->Pid = cpu_to_le16(li->pid); @@ -1104,7 +1118,6 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u8 type, __u64 length = 1 + flock->fl_end - flock->fl_start; struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); __u16 netfid = cfile->netfid; if (posix_lck) { @@ -1124,8 +1137,7 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u8 type, return rc; } - rc = cifs_lock_test(cinode, flock->fl_start, length, type, netfid, - flock); + rc = cifs_lock_test(cfile, flock->fl_start, length, type, flock); if (!rc) return rc; @@ -1212,15 +1224,13 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid) for (i = 0; i < 2; i++) { cur = buf; num = 0; - list_for_each_entry_safe(li, tmp, &cinode->llist, llist) { + list_for_each_entry_safe(li, tmp, &cfile->llist, llist) { if (flock->fl_start > li->offset || (flock->fl_start + length) < (li->offset + li->length)) continue; if (current->tgid != li->pid) continue; - if (cfile->netfid != li->netfid) - continue; if (types[i] != li->type) continue; if (!cinode->can_cache_brlcks) { @@ -1233,7 +1243,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid) cpu_to_le32((u32)(li->offset>>32)); /* * We need to save a lock here to let us add - * it again to the inode list if the unlock + * it again to the file's list if the unlock * range request fails on the server. */ list_move(&li->llist, &tmp_llist); @@ -1247,10 +1257,10 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid) * We failed on the unlock range * request - add all locks from * the tmp list to the head of - * the inode list. + * the file's list. */ cifs_move_llist(&tmp_llist, - &cinode->llist); + &cfile->llist); rc = stored_rc; } else /* @@ -1265,7 +1275,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid) } else { /* * We can cache brlock requests - simply remove - * a lock from the inode list. + * a lock from the file's list. */ list_del(&li->llist); cifs_del_lock_waiters(li); @@ -1276,7 +1286,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid) stored_rc = cifs_lockv(xid, tcon, cfile->netfid, types[i], num, 0, buf); if (stored_rc) { - cifs_move_llist(&tmp_llist, &cinode->llist); + cifs_move_llist(&tmp_llist, &cfile->llist); rc = stored_rc; } else cifs_free_llist(&tmp_llist); @@ -1296,7 +1306,6 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u8 type, __u64 length = 1 + flock->fl_end - flock->fl_start; struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); - struct cifsInodeInfo *cinode = CIFS_I(file->f_path.dentry->d_inode); __u16 netfid = cfile->netfid; if (posix_lck) { @@ -1323,11 +1332,11 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u8 type, if (lock) { struct cifsLockInfo *lock; - lock = cifs_lock_init(flock->fl_start, length, type, netfid); + lock = cifs_lock_init(flock->fl_start, length, type); if (!lock) return -ENOMEM; - rc = cifs_lock_add_if(cinode, lock, wait_flag); + rc = cifs_lock_add_if(cfile, lock, wait_flag); if (rc < 0) kfree(lock); if (rc <= 0) @@ -1340,7 +1349,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u8 type, goto out; } - cifs_lock_add(cinode, lock); + cifs_lock_add(cfile, lock); } else if (unlock) rc = cifs_unlock_range(cfile, flock, xid); -- cgit v0.10.2 From 04a6aa8acfac51385ec3e72fac1227e15db78ed9 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Tue, 28 Feb 2012 14:16:55 +0300 Subject: CIFS: Convert lock type to 32 bit variable to handle SMB2 lock type field further. Acked-by: Jeff Layton Signed-off-by: Pavel Shilovsky diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 03d24a5..0e06de1 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -567,7 +567,7 @@ struct cifsLockInfo { __u64 offset; __u64 length; __u32 pid; - __u8 type; + __u32 type; }; /* diff --git a/fs/cifs/file.c b/fs/cifs/file.c index fc45cd9..8d5d9c0 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -912,7 +912,8 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile) cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32)); if (++num == max_num) { stored_rc = cifs_lockv(xid, tcon, cfile->netfid, - li->type, 0, num, buf); + (__u8)li->type, 0, num, + buf); if (stored_rc) rc = stored_rc; cur = buf; @@ -923,7 +924,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile) if (num) { stored_rc = cifs_lockv(xid, tcon, cfile->netfid, - types[i], 0, num, buf); + (__u8)types[i], 0, num, buf); if (stored_rc) rc = stored_rc; } @@ -1067,7 +1068,7 @@ cifs_push_locks(struct cifsFileInfo *cfile) } static void -cifs_read_flock(struct file_lock *flock, __u8 *type, int *lock, int *unlock, +cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock, bool *wait_flag) { if (flock->fl_flags & FL_POSIX) @@ -1111,7 +1112,7 @@ cifs_read_flock(struct file_lock *flock, __u8 *type, int *lock, int *unlock, } static int -cifs_getlk(struct file *file, struct file_lock *flock, __u8 type, +cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, bool wait_flag, bool posix_lck, int xid) { int rc = 0; @@ -1299,7 +1300,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, int xid) } static int -cifs_setlk(struct file *file, struct file_lock *flock, __u8 type, +cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, bool wait_flag, bool posix_lck, int lock, int unlock, int xid) { int rc = 0; @@ -1370,7 +1371,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock) struct cifsInodeInfo *cinode; struct cifsFileInfo *cfile; __u16 netfid; - __u8 type; + __u32 type; rc = -EACCES; xid = GetXid(); -- cgit v0.10.2 From 106dc538abac88e804c63b7fe21ffb09cffaefc7 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Tue, 28 Feb 2012 14:23:34 +0300 Subject: CIFS: Separate protocol specific lock type handling Signed-off-by: Pavel Shilovsky diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 0e06de1..6943448 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -164,6 +164,10 @@ struct smb_version_operations { struct smb_version_values { char *version_string; + __u32 large_lock_type; + __u32 exclusive_lock_type; + __u32 shared_lock_type; + __u32 unlock_lock_type; }; struct smb_vol { diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 8d5d9c0..ac71f85 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -675,12 +675,13 @@ cifs_find_fid_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, struct cifsLockInfo **conf_lock) { struct cifsLockInfo *li; + struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; list_for_each_entry(li, &cfile->llist, llist) { if (offset + length <= li->offset || offset >= li->offset + li->length) continue; - else if ((type & LOCKING_ANDX_SHARED_LOCK) && + else if ((type & server->vals->shared_lock_type) && ((netfid == cfile->netfid && current->tgid == li->pid) || type == li->type)) continue; @@ -726,6 +727,7 @@ cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length, int rc = 0; struct cifsLockInfo *conf_lock; struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); + struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server; bool exist; mutex_lock(&cinode->lock_mutex); @@ -736,7 +738,7 @@ cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length, flock->fl_start = conf_lock->offset; flock->fl_end = conf_lock->offset + conf_lock->length - 1; flock->fl_pid = conf_lock->pid; - if (conf_lock->type & LOCKING_ANDX_SHARED_LOCK) + if (conf_lock->type & server->vals->shared_lock_type) flock->fl_type = F_RDLCK; else flock->fl_type = F_WRLCK; @@ -1069,7 +1071,7 @@ cifs_push_locks(struct cifsFileInfo *cfile) static void cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock, - bool *wait_flag) + bool *wait_flag, struct TCP_Server_Info *server) { if (flock->fl_flags & FL_POSIX) cFYI(1, "Posix"); @@ -1088,24 +1090,27 @@ cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock, (~(FL_POSIX | FL_FLOCK | FL_SLEEP | FL_ACCESS | FL_LEASE))) cFYI(1, "Unknown lock flags 0x%x", flock->fl_flags); - *type = LOCKING_ANDX_LARGE_FILES; + *type = server->vals->large_lock_type; if (flock->fl_type == F_WRLCK) { cFYI(1, "F_WRLCK "); + *type |= server->vals->exclusive_lock_type; *lock = 1; } else if (flock->fl_type == F_UNLCK) { cFYI(1, "F_UNLCK"); + *type |= server->vals->unlock_lock_type; *unlock = 1; /* Check if unlock includes more than one lock range */ } else if (flock->fl_type == F_RDLCK) { cFYI(1, "F_RDLCK"); - *type |= LOCKING_ANDX_SHARED_LOCK; + *type |= server->vals->shared_lock_type; *lock = 1; } else if (flock->fl_type == F_EXLCK) { cFYI(1, "F_EXLCK"); + *type |= server->vals->exclusive_lock_type; *lock = 1; } else if (flock->fl_type == F_SHLCK) { cFYI(1, "F_SHLCK"); - *type |= LOCKING_ANDX_SHARED_LOCK; + *type |= server->vals->shared_lock_type; *lock = 1; } else cFYI(1, "Unknown type of lock"); @@ -1119,6 +1124,7 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, __u64 length = 1 + flock->fl_end - flock->fl_start; struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; __u16 netfid = cfile->netfid; if (posix_lck) { @@ -1128,7 +1134,7 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, if (!rc) return rc; - if (type & LOCKING_ANDX_SHARED_LOCK) + if (type & server->vals->shared_lock_type) posix_lock_type = CIFS_RDLCK; else posix_lock_type = CIFS_WRLCK; @@ -1152,23 +1158,22 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, flock->fl_type = F_UNLCK; if (rc != 0) cERROR(1, "Error unlocking previously locked " - "range %d during test of lock", rc); + "range %d during test of lock", rc); return 0; } - if (type & LOCKING_ANDX_SHARED_LOCK) { + if (type & server->vals->shared_lock_type) { flock->fl_type = F_WRLCK; return 0; } rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length, flock->fl_start, 0, 1, - type | LOCKING_ANDX_SHARED_LOCK, 0, 0); + type | server->vals->shared_lock_type, 0, 0); if (rc == 0) { rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length, flock->fl_start, 1, 0, - type | LOCKING_ANDX_SHARED_LOCK, - 0, 0); + type | server->vals->shared_lock_type, 0, 0); flock->fl_type = F_RDLCK; if (rc != 0) cERROR(1, "Error unlocking previously locked " @@ -1307,6 +1312,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, __u64 length = 1 + flock->fl_end - flock->fl_start; struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct TCP_Server_Info *server = tcon->ses->server; __u16 netfid = cfile->netfid; if (posix_lck) { @@ -1316,7 +1322,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, if (!rc || rc < 0) return rc; - if (type & LOCKING_ANDX_SHARED_LOCK) + if (type & server->vals->shared_lock_type) posix_lock_type = CIFS_RDLCK; else posix_lock_type = CIFS_WRLCK; @@ -1380,11 +1386,13 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock) "end: %lld", cmd, flock->fl_flags, flock->fl_type, flock->fl_start, flock->fl_end); - cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag); - - cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); cfile = (struct cifsFileInfo *)file->private_data; tcon = tlink_tcon(cfile->tlink); + + cifs_read_flock(flock, &type, &lock, &unlock, &wait_flag, + tcon->ses->server); + + cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); netfid = cfile->netfid; cinode = CIFS_I(file->f_path.dentry->d_inode); diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index fa486f0..af48a8b 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -20,6 +20,7 @@ #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" +#include "cifspdu.h" /* * An NT cancel request header looks just like the original request except: @@ -65,4 +66,8 @@ struct smb_version_operations smb1_operations = { struct smb_version_values smb1_values = { .version_string = SMB1_VERSION_STRING, + .large_lock_type = LOCKING_ANDX_LARGE_FILES, + .exclusive_lock_type = 0, + .shared_lock_type = LOCKING_ANDX_SHARED_LOCK, + .unlock_lock_type = 0, }; -- cgit v0.10.2 From 55157dfbb566e23e3c76489cb028fc82bd985ea1 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Tue, 28 Feb 2012 14:04:17 +0300 Subject: CIFS: Separate protocol specific part from getlk Signed-off-by: Pavel Shilovsky diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 6943448..b6e97f8 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -156,10 +156,12 @@ enum smb_version { struct mid_q_entry; struct TCP_Server_Info; +struct cifsFileInfo; struct smb_version_operations { int (*send_cancel)(struct TCP_Server_Info *, void *, struct mid_q_entry *); + bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *); }; struct smb_version_values { diff --git a/fs/cifs/file.c b/fs/cifs/file.c index ac71f85..9c2d85d 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -671,7 +671,7 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock) static bool cifs_find_fid_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, - __u64 length, __u8 type, __u16 netfid, + __u64 length, __u8 type, struct cifsFileInfo *cur, struct cifsLockInfo **conf_lock) { struct cifsLockInfo *li; @@ -682,8 +682,8 @@ cifs_find_fid_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, offset >= li->offset + li->length) continue; else if ((type & server->vals->shared_lock_type) && - ((netfid == cfile->netfid && current->tgid == li->pid) - || type == li->type)) + ((server->ops->compare_fids(cur, cfile) && + current->tgid == li->pid) || type == li->type)) continue; else { *conf_lock = li; @@ -694,17 +694,17 @@ cifs_find_fid_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, } static bool -cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset, - __u64 length, __u8 type, __u16 netfid, - struct cifsLockInfo **conf_lock) +cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length, + __u8 type, struct cifsLockInfo **conf_lock) { bool rc = false; struct cifsFileInfo *fid, *tmp; + struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); spin_lock(&cifs_file_list_lock); list_for_each_entry_safe(fid, tmp, &cinode->openFileList, flist) { rc = cifs_find_fid_lock_conflict(fid, offset, length, type, - netfid, conf_lock); + cfile, conf_lock); if (rc) break; } @@ -732,8 +732,8 @@ cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length, mutex_lock(&cinode->lock_mutex); - exist = cifs_find_lock_conflict(cinode, offset, length, type, - cfile->netfid, &conf_lock); + exist = cifs_find_lock_conflict(cfile, offset, length, type, + &conf_lock); if (exist) { flock->fl_start = conf_lock->offset; flock->fl_end = conf_lock->offset + conf_lock->length - 1; @@ -779,8 +779,8 @@ try_again: exist = false; mutex_lock(&cinode->lock_mutex); - exist = cifs_find_lock_conflict(cinode, lock->offset, lock->length, - lock->type, cfile->netfid, &conf_lock); + exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length, + lock->type, &conf_lock); if (!exist && cinode->can_cache_brlcks) { list_add_tail(&lock->llist, &cfile->llist); mutex_unlock(&cinode->lock_mutex); @@ -1117,6 +1117,15 @@ cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock, } static int +cifs_mandatory_lock(int xid, struct cifsFileInfo *cfile, __u64 offset, + __u64 length, __u32 type, int lock, int unlock, bool wait) +{ + return CIFSSMBLock(xid, tlink_tcon(cfile->tlink), cfile->netfid, + current->tgid, length, offset, unlock, lock, + (__u8)type, wait, 0); +} + +static int cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, bool wait_flag, bool posix_lck, int xid) { @@ -1149,12 +1158,11 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, return rc; /* BB we could chain these into one lock request BB */ - rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length, - flock->fl_start, 0, 1, type, 0, 0); + rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length, type, + 1, 0, false); if (rc == 0) { - rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, - length, flock->fl_start, 1, 0, - type, 0, 0); + rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length, + type, 0, 1, false); flock->fl_type = F_UNLCK; if (rc != 0) cERROR(1, "Error unlocking previously locked " @@ -1167,13 +1175,13 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, return 0; } - rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length, - flock->fl_start, 0, 1, - type | server->vals->shared_lock_type, 0, 0); + rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length, + type | server->vals->shared_lock_type, 1, 0, + false); if (rc == 0) { - rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, - length, flock->fl_start, 1, 0, - type | server->vals->shared_lock_type, 0, 0); + rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length, + type | server->vals->shared_lock_type, + 0, 1, false); flock->fl_type = F_RDLCK; if (rc != 0) cERROR(1, "Error unlocking previously locked " diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index af48a8b..ce27f86 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -60,8 +60,15 @@ send_nt_cancel(struct TCP_Server_Info *server, void *buf, return rc; } +static bool +cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) +{ + return ob1->netfid == ob2->netfid; +} + struct smb_version_operations smb1_operations = { .send_cancel = send_nt_cancel, + .compare_fids = cifs_compare_fids, }; struct smb_version_values smb1_values = { -- cgit v0.10.2 From 7f92447aa7be605fd80d9f248efe0b8ac9379f11 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Wed, 28 Mar 2012 17:10:25 +0400 Subject: CIFS: Separate protocol specific part from setlk Signed-off-by: Pavel Shilovsky diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 9c2d85d..ed9b5a8 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1357,8 +1357,8 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, if (rc <= 0) goto out; - rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length, - flock->fl_start, 0, 1, type, wait_flag, 0); + rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length, + type, 1, 0, wait_flag); if (rc) { kfree(lock); goto out; -- cgit v0.10.2 From 2608bee744a92d60d15ff4e6e0b913d8b406aedd Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Tue, 15 May 2012 10:19:16 -0500 Subject: cifs: Include backup intent search flags during searches {try #2) As observed and suggested by Tushar Gosavi... --------- readdir calls these function to send TRANS2_FIND_FIRST and TRANS2_FIND_NEXT command to the server. The current cifs module is not specifying CIFS_SEARCH_BACKUP_SEARCH flag while sending these command when backupuid/backupgid is specified. This can be resolved by specifying CIFS_SEARCH_BACKUP_SEARCH flag. --------- Cc: Reported-and-Tested-by: Tushar Gosavi Signed-off-by: Shirish Pargaonkar Acked-by: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index eeb789d..0a3fa96 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -192,11 +192,13 @@ extern int CIFSTCon(unsigned int xid, struct cifs_ses *ses, extern int CIFSFindFirst(const int xid, struct cifs_tcon *tcon, const char *searchName, const struct nls_table *nls_codepage, - __u16 *searchHandle, struct cifs_search_info *psrch_inf, + __u16 *searchHandle, __u16 search_flags, + struct cifs_search_info *psrch_inf, int map, const char dirsep); extern int CIFSFindNext(const int xid, struct cifs_tcon *tcon, - __u16 searchHandle, struct cifs_search_info *psrch_inf); + __u16 searchHandle, __u16 search_flags, + struct cifs_search_info *psrch_inf); extern int CIFSFindClose(const int, struct cifs_tcon *tcon, const __u16 search_handle); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 45633da..3563c93 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -4238,7 +4238,7 @@ int CIFSFindFirst(const int xid, struct cifs_tcon *tcon, const char *searchName, const struct nls_table *nls_codepage, - __u16 *pnetfid, + __u16 *pnetfid, __u16 search_flags, struct cifs_search_info *psrch_inf, int remap, const char dirsep) { /* level 257 SMB_ */ @@ -4310,8 +4310,7 @@ findFirstRetry: cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY); pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO)); - pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | - CIFS_SEARCH_RETURN_RESUME); + pSMB->SearchFlags = cpu_to_le16(search_flags); pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); /* BB what should we set StorageType to? Does it matter? BB */ @@ -4381,8 +4380,8 @@ findFirstRetry: return rc; } -int CIFSFindNext(const int xid, struct cifs_tcon *tcon, - __u16 searchHandle, struct cifs_search_info *psrch_inf) +int CIFSFindNext(const int xid, struct cifs_tcon *tcon, __u16 searchHandle, + __u16 search_flags, struct cifs_search_info *psrch_inf) { TRANSACTION2_FNEXT_REQ *pSMB = NULL; TRANSACTION2_FNEXT_RSP *pSMBr = NULL; @@ -4425,8 +4424,7 @@ int CIFSFindNext(const int xid, struct cifs_tcon *tcon, cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO)); pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); pSMB->ResumeKey = psrch_inf->resume_key; - pSMB->SearchFlags = - cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME); + pSMB->SearchFlags = cpu_to_le16(search_flags); name_len = psrch_inf->resume_name_len; params += name_len; diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index e2bbc68..0a8224d 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -219,6 +219,7 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb, static int initiate_cifs_search(const int xid, struct file *file) { + __u16 search_flags; int rc = 0; char *full_path = NULL; struct cifsFileInfo *cifsFile; @@ -270,8 +271,12 @@ ffirst_retry: cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO; } + search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME; + if (backup_cred(cifs_sb)) + search_flags |= CIFS_SEARCH_BACKUP_SEARCH; + rc = CIFSFindFirst(xid, pTcon, full_path, cifs_sb->local_nls, - &cifsFile->netfid, &cifsFile->srch_inf, + &cifsFile->netfid, search_flags, &cifsFile->srch_inf, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb)); if (rc == 0) @@ -502,11 +507,13 @@ static int cifs_save_resume_key(const char *current_entry, static int find_cifs_entry(const int xid, struct cifs_tcon *pTcon, struct file *file, char **ppCurrentEntry, int *num_to_ret) { + __u16 search_flags; int rc = 0; int pos_in_buf = 0; loff_t first_entry_in_buffer; loff_t index_to_find = file->f_pos; struct cifsFileInfo *cifsFile = file->private_data; + struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); /* check if index in the buffer */ if ((cifsFile == NULL) || (ppCurrentEntry == NULL) || @@ -560,10 +567,14 @@ static int find_cifs_entry(const int xid, struct cifs_tcon *pTcon, cifsFile); } + search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME; + if (backup_cred(cifs_sb)) + search_flags |= CIFS_SEARCH_BACKUP_SEARCH; + while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) && (rc == 0) && !cifsFile->srch_inf.endOfSearch) { cFYI(1, "calling findnext2"); - rc = CIFSFindNext(xid, pTcon, cifsFile->netfid, + rc = CIFSFindNext(xid, pTcon, cifsFile->netfid, search_flags, &cifsFile->srch_inf); /* FindFirst/Next set last_entry to NULL on malformed reply */ if (cifsFile->srch_inf.last_entry) -- cgit v0.10.2 From 082d0642c61fc309ca7f6133968ba1264193dcc1 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Thu, 17 May 2012 12:18:21 +0400 Subject: CIFS: Move protocol specific part from SendReceive2 to ops struct Acked-by: Shirish Pargaonkar Reviewed-by: Jeff Layton Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index b6e97f8..5b10838 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -157,11 +157,18 @@ enum smb_version { struct mid_q_entry; struct TCP_Server_Info; struct cifsFileInfo; +struct cifs_ses; struct smb_version_operations { int (*send_cancel)(struct TCP_Server_Info *, void *, struct mid_q_entry *); bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *); + /* setup request: allocate mid, sign message */ + int (*setup_request)(struct cifs_ses *, struct kvec *, unsigned int, + struct mid_q_entry **); + /* check response: verify signature, map error */ + int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *, + bool); }; struct smb_version_values { diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 0a3fa96..57af642 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -78,6 +78,8 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *, int * /* bytes returned */ , const int long_op); extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses, char *in_buf, int flags); +extern int cifs_setup_request(struct cifs_ses *, struct kvec *, unsigned int, + struct mid_q_entry **); extern int cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, bool log_error); extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *, diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index ce27f86..9cf76e8 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -69,6 +69,8 @@ cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) struct smb_version_operations smb1_operations = { .send_cancel = send_nt_cancel, .compare_fids = cifs_compare_fids, + .setup_request = cifs_setup_request, + .check_receive = cifs_check_receive, }; struct smb_version_values smb1_values = { diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 269a5a7..87bd998 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -514,7 +514,7 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, return map_smb_to_linux_error(mid->resp_buf, log_error); } -static int +int cifs_setup_request(struct cifs_ses *ses, struct kvec *iov, unsigned int nvec, struct mid_q_entry **ret_mid) { @@ -577,7 +577,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, mutex_lock(&ses->server->srv_mutex); - rc = cifs_setup_request(ses, iov, n_vec, &midQ); + rc = ses->server->ops->setup_request(ses, iov, n_vec, &midQ); if (rc) { mutex_unlock(&ses->server->srv_mutex); cifs_small_buf_release(buf); @@ -640,7 +640,8 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, else *pRespBufType = CIFS_SMALL_BUFFER; - rc = cifs_check_receive(midQ, ses->server, flags & CIFS_LOG_ERROR); + rc = ses->server->ops->check_receive(midQ, ses->server, + flags & CIFS_LOG_ERROR); /* mark it so buf will not be freed by delete_mid */ if ((flags & CIFS_NO_RESP) == 0) -- cgit v0.10.2 From 1887f60103aaeeeb7a3fbf3b366d626043873164 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Thu, 17 May 2012 12:45:31 +0400 Subject: CIFS: Move header_size/max_header_size to ops structure Acked-by: Shirish Pargaonkar Reviewed-by: Jeff Layton Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 5b10838..9ee832d 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -177,8 +177,13 @@ struct smb_version_values { __u32 exclusive_lock_type; __u32 shared_lock_type; __u32 unlock_lock_type; + size_t header_size; + size_t max_header_size; }; +#define HEADER_SIZE(server) (server->vals->header_size) +#define MAX_HEADER_SIZE(server) (server->vals->max_header_size) + struct smb_vol { char *username; char *password; @@ -374,18 +379,6 @@ has_credits(struct TCP_Server_Info *server, int *credits) return num > 0; } -static inline size_t -header_size(void) -{ - return sizeof(struct smb_hdr); -} - -static inline size_t -max_header_size(void) -{ - return MAX_CIFS_HDR_SIZE; -} - /* * Macros to allow the TCP_Server_Info->net field and related code to drop out * when CONFIG_NET_NS isn't set. diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 3563c93..77463f7 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1400,7 +1400,7 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) length = cifs_read_from_socket(server, server->bigbuf, min_t(unsigned int, remaining, - CIFSMaxBufSize + max_header_size())); + CIFSMaxBufSize + MAX_HEADER_SIZE(server))); if (length < 0) return length; server->total_read += length; @@ -1449,9 +1449,10 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) * can if there's not enough data. At this point, we've read down to * the Mid. */ - len = min_t(unsigned int, buflen, read_rsp_size()) - header_size() + 1; + len = min_t(unsigned int, buflen, read_rsp_size()) - + HEADER_SIZE(server) + 1; - rdata->iov[0].iov_base = buf + header_size() - 1; + rdata->iov[0].iov_base = buf + HEADER_SIZE(server) - 1; rdata->iov[0].iov_len = len; length = cifs_readv_from_socket(server, rdata->iov, 1, len); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 5ac20fc..65ec6ef 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -568,7 +568,7 @@ allocate_buffers(struct TCP_Server_Info *server) } } else if (server->large_buf) { /* we are reusing a dirty large buf, clear its start */ - memset(server->bigbuf, 0, header_size()); + memset(server->bigbuf, 0, HEADER_SIZE(server)); } if (!server->smallbuf) { @@ -582,7 +582,7 @@ allocate_buffers(struct TCP_Server_Info *server) /* beginning of smb buffer is cleared in our buf_get */ } else { /* if existing small buf clear beginning */ - memset(server->smallbuf, 0, header_size()); + memset(server->smallbuf, 0, HEADER_SIZE(server)); } return true; @@ -953,7 +953,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) unsigned int pdu_length = get_rfc1002_length(buf); /* make sure this will fit in a large buffer */ - if (pdu_length > CIFSMaxBufSize + max_header_size() - 4) { + if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - 4) { cERROR(1, "SMB response too long (%u bytes)", pdu_length); cifs_reconnect(server); @@ -969,8 +969,8 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) } /* now read the rest */ - length = cifs_read_from_socket(server, buf + header_size() - 1, - pdu_length - header_size() + 1 + 4); + length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, + pdu_length - HEADER_SIZE(server) + 1 + 4); if (length < 0) return length; server->total_read += length; @@ -1044,7 +1044,7 @@ cifs_demultiplex_thread(void *p) continue; /* make sure we have enough to get to the MID */ - if (pdu_length < header_size() - 1 - 4) { + if (pdu_length < HEADER_SIZE(server) - 1 - 4) { cERROR(1, "SMB response too short (%u bytes)", pdu_length); cifs_reconnect(server); @@ -1054,7 +1054,7 @@ cifs_demultiplex_thread(void *p) /* read down to the MID */ length = cifs_read_from_socket(server, buf + 4, - header_size() - 1 - 4); + HEADER_SIZE(server) - 1 - 4); if (length < 0) continue; server->total_read += length; @@ -1079,7 +1079,8 @@ cifs_demultiplex_thread(void *p) } else if (!is_valid_oplock_break(buf, server)) { cERROR(1, "No task to wake, unknown frame received! " "NumMids %d", atomic_read(&midCount)); - cifs_dump_mem("Received Data is: ", buf, header_size()); + cifs_dump_mem("Received Data is: ", buf, + HEADER_SIZE(server)); #ifdef CONFIG_CIFS_DEBUG2 cifs_dump_detail(buf); cifs_dump_mids(server); diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 9cf76e8..3668e81a 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -79,4 +79,6 @@ struct smb_version_values smb1_values = { .exclusive_lock_type = 0, .shared_lock_type = LOCKING_ANDX_SHARED_LOCK, .unlock_lock_type = 0, + .header_size = sizeof(struct smb_hdr), + .max_header_size = MAX_CIFS_HDR_SIZE, }; -- cgit v0.10.2 From eb37871118abfc30629ee2bef11013eddb42c756 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Thu, 17 May 2012 13:02:51 +0400 Subject: CIFS: Move protocol specific part from cifs_readv_receive to ops struct Acked-by: Shirish Pargaonkar Reviewed-by: Jeff Layton Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9ee832d..8251401 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -169,6 +169,12 @@ struct smb_version_operations { /* check response: verify signature, map error */ int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *, bool); + /* data offset from read response message */ + unsigned int (*read_data_offset)(char *); + /* data length from read response message */ + unsigned int (*read_data_length)(char *); + /* map smb to linux error */ + int (*map_error)(char *, bool); }; struct smb_version_values { @@ -179,6 +185,7 @@ struct smb_version_values { __u32 unlock_lock_type; size_t header_size; size_t max_header_size; + size_t read_rsp_size; }; #define HEADER_SIZE(server) (server->vals->header_size) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 77463f7..b1f3751 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1411,27 +1411,6 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid) return 0; } -static inline size_t -read_rsp_size(void) -{ - return sizeof(READ_RSP); -} - -static inline unsigned int -read_data_offset(char *buf) -{ - READ_RSP *rsp = (READ_RSP *)buf; - return le16_to_cpu(rsp->DataOffset); -} - -static inline unsigned int -read_data_length(char *buf) -{ - READ_RSP *rsp = (READ_RSP *)buf; - return (le16_to_cpu(rsp->DataLengthHigh) << 16) + - le16_to_cpu(rsp->DataLength); -} - static int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) { @@ -1449,7 +1428,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) * can if there's not enough data. At this point, we've read down to * the Mid. */ - len = min_t(unsigned int, buflen, read_rsp_size()) - + len = min_t(unsigned int, buflen, server->vals->read_rsp_size) - HEADER_SIZE(server) + 1; rdata->iov[0].iov_base = buf + HEADER_SIZE(server) - 1; @@ -1461,7 +1440,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) server->total_read += length; /* Was the SMB read successful? */ - rdata->result = map_smb_to_linux_error(buf, false); + rdata->result = server->ops->map_error(buf, false); if (rdata->result != 0) { cFYI(1, "%s: server returned error %d", __func__, rdata->result); @@ -1469,14 +1448,15 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) } /* Is there enough to get to the rest of the READ_RSP header? */ - if (server->total_read < read_rsp_size()) { + if (server->total_read < server->vals->read_rsp_size) { cFYI(1, "%s: server returned short header. got=%u expected=%zu", - __func__, server->total_read, read_rsp_size()); + __func__, server->total_read, + server->vals->read_rsp_size); rdata->result = -EIO; return cifs_readv_discard(server, mid); } - data_offset = read_data_offset(buf) + 4; + data_offset = server->ops->read_data_offset(buf) + 4; if (data_offset < server->total_read) { /* * win2k8 sometimes sends an offset of 0 when the read @@ -1515,7 +1495,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) rdata->iov[0].iov_base, rdata->iov[0].iov_len); /* how much data is in the response? */ - data_len = read_data_length(buf); + data_len = server->ops->read_data_length(buf); if (data_offset + data_len > buflen) { /* data_len is corrupt -- discard frame */ rdata->result = -EIO; diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 3668e81a..daa2ede 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -66,11 +66,29 @@ cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2) return ob1->netfid == ob2->netfid; } +static unsigned int +cifs_read_data_offset(char *buf) +{ + READ_RSP *rsp = (READ_RSP *)buf; + return le16_to_cpu(rsp->DataOffset); +} + +static unsigned int +cifs_read_data_length(char *buf) +{ + READ_RSP *rsp = (READ_RSP *)buf; + return (le16_to_cpu(rsp->DataLengthHigh) << 16) + + le16_to_cpu(rsp->DataLength); +} + struct smb_version_operations smb1_operations = { .send_cancel = send_nt_cancel, .compare_fids = cifs_compare_fids, .setup_request = cifs_setup_request, .check_receive = cifs_check_receive, + .read_data_offset = cifs_read_data_offset, + .read_data_length = cifs_read_data_length, + .map_error = map_smb_to_linux_error, }; struct smb_version_values smb1_values = { @@ -81,4 +99,5 @@ struct smb_version_values smb1_values = { .unlock_lock_type = 0, .header_size = sizeof(struct smb_hdr), .max_header_size = MAX_CIFS_HDR_SIZE, + .read_rsp_size = sizeof(READ_RSP), }; -- cgit v0.10.2 From 8aa26f3ed80ddbaf78804b6481fcfdbd447caa1c Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Thu, 17 May 2012 13:25:35 +0400 Subject: CIFS: Move protocol specific demultiplex thread calls to ops struct Acked-by: Shirish Pargaonkar Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 8aae8e2..e814052 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -57,19 +57,21 @@ cifs_dump_mem(char *label, void *data, int length) } } -#ifdef CONFIG_CIFS_DEBUG2 void cifs_dump_detail(void *buf) { +#ifdef CONFIG_CIFS_DEBUG2 struct smb_hdr *smb = (struct smb_hdr *)buf; cERROR(1, "Cmd: %d Err: 0x%x Flags: 0x%x Flgs2: 0x%x Mid: %d Pid: %d", smb->Command, smb->Status.CifsError, smb->Flags, smb->Flags2, smb->Mid, smb->Pid); cERROR(1, "smb buf %p len %d", smb, smbCalcSize(smb)); +#endif /* CONFIG_CIFS_DEBUG2 */ } void cifs_dump_mids(struct TCP_Server_Info *server) { +#ifdef CONFIG_CIFS_DEBUG2 struct list_head *tmp; struct mid_q_entry *mid_entry; @@ -102,8 +104,8 @@ void cifs_dump_mids(struct TCP_Server_Info *server) } } spin_unlock(&GlobalMid_Lock); -} #endif /* CONFIG_CIFS_DEBUG2 */ +} #ifdef CONFIG_PROC_FS static int cifs_debug_data_proc_show(struct seq_file *m, void *v) diff --git a/fs/cifs/cifs_debug.h b/fs/cifs/cifs_debug.h index 566e0ae..c0c68bb 100644 --- a/fs/cifs/cifs_debug.h +++ b/fs/cifs/cifs_debug.h @@ -24,10 +24,10 @@ #define _H_CIFS_DEBUG void cifs_dump_mem(char *label, void *data, int length); -#ifdef CONFIG_CIFS_DEBUG2 -#define DBG2 2 void cifs_dump_detail(void *); void cifs_dump_mids(struct TCP_Server_Info *); +#ifdef CONFIG_CIFS_DEBUG2 +#define DBG2 2 #else #define DBG2 0 #endif diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 8251401..1001924 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -175,6 +175,12 @@ struct smb_version_operations { unsigned int (*read_data_length)(char *); /* map smb to linux error */ int (*map_error)(char *, bool); + /* find mid corresponding to the response message */ + struct mid_q_entry * (*find_mid)(struct TCP_Server_Info *, char *); + void (*dump_detail)(void *); + /* verify the message */ + int (*check_message)(char *, unsigned int); + bool (*is_oplock_break)(char *, struct TCP_Server_Info *); }; struct smb_version_values { diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 65ec6ef..ce033d7 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -783,25 +783,6 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type) return false; } -static struct mid_q_entry * -find_mid(struct TCP_Server_Info *server, char *buffer) -{ - struct smb_hdr *buf = (struct smb_hdr *)buffer; - struct mid_q_entry *mid; - - spin_lock(&GlobalMid_Lock); - list_for_each_entry(mid, &server->pending_mid_q, qhead) { - if (mid->mid == buf->Mid && - mid->mid_state == MID_REQUEST_SUBMITTED && - le16_to_cpu(mid->command) == buf->Command) { - spin_unlock(&GlobalMid_Lock); - return mid; - } - } - spin_unlock(&GlobalMid_Lock); - return NULL; -} - void dequeue_mid(struct mid_q_entry *mid, bool malformed) { @@ -986,7 +967,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) * 48 bytes is enough to display the header and a little bit * into the payload for debugging purposes. */ - length = checkSMB(buf, server->total_read); + length = server->ops->check_message(buf, server->total_read); if (length != 0) cifs_dump_mem("Bad SMB: ", buf, min_t(unsigned int, server->total_read, 48)); @@ -1059,7 +1040,7 @@ cifs_demultiplex_thread(void *p) continue; server->total_read += length; - mid_entry = find_mid(server, buf); + mid_entry = server->ops->find_mid(server, buf); if (!mid_entry || !mid_entry->receive) length = standard_receive3(server, mid_entry); @@ -1076,13 +1057,13 @@ cifs_demultiplex_thread(void *p) if (mid_entry != NULL) { if (!mid_entry->multiRsp || mid_entry->multiEnd) mid_entry->callback(mid_entry); - } else if (!is_valid_oplock_break(buf, server)) { + } else if (!server->ops->is_oplock_break(buf, server)) { cERROR(1, "No task to wake, unknown frame received! " "NumMids %d", atomic_read(&midCount)); cifs_dump_mem("Received Data is: ", buf, HEADER_SIZE(server)); #ifdef CONFIG_CIFS_DEBUG2 - cifs_dump_detail(buf); + server->ops->dump_detail(buf); cifs_dump_mids(server); #endif /* CIFS_DEBUG2 */ diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index daa2ede..ddc8ca6 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -81,6 +81,25 @@ cifs_read_data_length(char *buf) le16_to_cpu(rsp->DataLength); } +static struct mid_q_entry * +cifs_find_mid(struct TCP_Server_Info *server, char *buffer) +{ + struct smb_hdr *buf = (struct smb_hdr *)buffer; + struct mid_q_entry *mid; + + spin_lock(&GlobalMid_Lock); + list_for_each_entry(mid, &server->pending_mid_q, qhead) { + if (mid->mid == buf->Mid && + mid->mid_state == MID_REQUEST_SUBMITTED && + le16_to_cpu(mid->command) == buf->Command) { + spin_unlock(&GlobalMid_Lock); + return mid; + } + } + spin_unlock(&GlobalMid_Lock); + return NULL; +} + struct smb_version_operations smb1_operations = { .send_cancel = send_nt_cancel, .compare_fids = cifs_compare_fids, @@ -89,6 +108,10 @@ struct smb_version_operations smb1_operations = { .read_data_offset = cifs_read_data_offset, .read_data_length = cifs_read_data_length, .map_error = map_smb_to_linux_error, + .find_mid = cifs_find_mid, + .check_message = checkSMB, + .dump_detail = cifs_dump_detail, + .is_oplock_break = is_valid_oplock_break, }; struct smb_version_values smb1_values = { -- cgit v0.10.2 From 452757897a311b939ae202744754ffd9ce6704d8 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Thu, 17 May 2012 17:53:29 +0400 Subject: CIFS: Move add/set_credits and get_credits_field to ops structure Acked-by: Shirish Pargaonkar Reviewed-by: Jeff Layton Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 1001924..2396456 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -169,6 +169,9 @@ struct smb_version_operations { /* check response: verify signature, map error */ int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *, bool); + void (*add_credits)(struct TCP_Server_Info *, const unsigned int); + void (*set_credits)(struct TCP_Server_Info *, const int); + int * (*get_credits_field)(struct TCP_Server_Info *); /* data offset from read response message */ unsigned int (*read_data_offset)(char *); /* data length from read response message */ @@ -372,16 +375,6 @@ in_flight(struct TCP_Server_Info *server) return num; } -static inline int* -get_credits_field(struct TCP_Server_Info *server) -{ - /* - * This will change to switch statement when we reserve slots for echos - * and oplock breaks. - */ - return &server->credits; -} - static inline bool has_credits(struct TCP_Server_Info *server, int *credits) { @@ -392,6 +385,18 @@ has_credits(struct TCP_Server_Info *server, int *credits) return num > 0; } +static inline void +add_credits(struct TCP_Server_Info *server, const unsigned int add) +{ + server->ops->add_credits(server, add); +} + +static inline void +set_credits(struct TCP_Server_Info *server, const int val) +{ + server->ops->set_credits(server, val); +} + /* * Macros to allow the TCP_Server_Info->net field and related code to drop out * when CONFIG_NET_NS isn't set. diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 57af642..5ec21ec 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -90,9 +90,6 @@ extern int SendReceiveBlockingLock(const unsigned int xid, struct smb_hdr *in_buf , struct smb_hdr *out_buf, int *bytes_returned); -extern void cifs_add_credits(struct TCP_Server_Info *server, - const unsigned int add); -extern void cifs_set_credits(struct TCP_Server_Info *server, const int val); extern int checkSMB(char *buf, unsigned int length); extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *); extern bool backup_cred(struct cifs_sb_info *); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index b1f3751..b5ad716 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -460,7 +460,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifs_ses *ses) server->maxReq = min_t(unsigned int, le16_to_cpu(rsp->MaxMpxCount), cifs_max_pending); - cifs_set_credits(server, server->maxReq); + set_credits(server, server->maxReq); server->maxBuf = le16_to_cpu(rsp->MaxBufSize); server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs); /* even though we do not use raw we might as well set this @@ -568,7 +568,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifs_ses *ses) little endian */ server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount), cifs_max_pending); - cifs_set_credits(server, server->maxReq); + set_credits(server, server->maxReq); /* probably no need to store and check maxvcs */ server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize); server->max_rw = le32_to_cpu(pSMBr->MaxRawSize); @@ -720,7 +720,7 @@ cifs_echo_callback(struct mid_q_entry *mid) struct TCP_Server_Info *server = mid->callback_data; DeleteMidQEntry(mid); - cifs_add_credits(server, 1); + add_credits(server, 1); } int @@ -1563,7 +1563,7 @@ cifs_readv_callback(struct mid_q_entry *mid) queue_work(cifsiod_wq, &rdata->work); DeleteMidQEntry(mid); - cifs_add_credits(server, 1); + add_credits(server, 1); } /* cifs_async_readv - send an async write, and set up mid to handle result */ @@ -2010,7 +2010,7 @@ cifs_writev_callback(struct mid_q_entry *mid) queue_work(cifsiod_wq, &wdata->work); DeleteMidQEntry(mid); - cifs_add_credits(tcon->ses->server, 1); + add_credits(tcon->ses->server, 1); } /* cifs_async_writev - send an async write, and set up mid to handle result */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index ce033d7..c71c11c 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -4099,11 +4099,11 @@ int cifs_negotiate_protocol(unsigned int xid, struct cifs_ses *ses) if (server->maxBuf != 0) return 0; - cifs_set_credits(server, 1); + set_credits(server, 1); rc = CIFSSMBNegotiate(xid, ses); if (rc == -EAGAIN) { /* retry only once on 1st time connection */ - cifs_set_credits(server, 1); + set_credits(server, 1); rc = CIFSSMBNegotiate(xid, ses); if (rc == -EAGAIN) rc = -EHOSTDOWN; diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index d2bb1e7..e2552d2 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -653,22 +653,3 @@ backup_cred(struct cifs_sb_info *cifs_sb) return false; } - -void -cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add) -{ - spin_lock(&server->req_lock); - server->credits += add; - server->in_flight--; - spin_unlock(&server->req_lock); - wake_up(&server->request_q); -} - -void -cifs_set_credits(struct TCP_Server_Info *server, const int val) -{ - spin_lock(&server->req_lock); - server->credits = val; - server->oplocks = val > 1 ? enable_oplocks : false; - spin_unlock(&server->req_lock); -} diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index ddc8ca6..d9d615f 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -100,11 +100,39 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer) return NULL; } +static void +cifs_add_credits(struct TCP_Server_Info *server, const unsigned int add) +{ + spin_lock(&server->req_lock); + server->credits += add; + server->in_flight--; + spin_unlock(&server->req_lock); + wake_up(&server->request_q); +} + +static void +cifs_set_credits(struct TCP_Server_Info *server, const int val) +{ + spin_lock(&server->req_lock); + server->credits = val; + server->oplocks = val > 1 ? enable_oplocks : false; + spin_unlock(&server->req_lock); +} + +static int * +cifs_get_credits_field(struct TCP_Server_Info *server) +{ + return &server->credits; +} + struct smb_version_operations smb1_operations = { .send_cancel = send_nt_cancel, .compare_fids = cifs_compare_fids, .setup_request = cifs_setup_request, .check_receive = cifs_check_receive, + .add_credits = cifs_add_credits, + .set_credits = cifs_set_credits, + .get_credits_field = cifs_get_credits_field, .read_data_offset = cifs_read_data_offset, .read_data_length = cifs_read_data_length, .map_error = map_smb_to_linux_error, diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 87bd998..1b36ffe 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -304,7 +304,8 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int optype, static int wait_for_free_request(struct TCP_Server_Info *server, const int optype) { - return wait_for_free_credits(server, optype, get_credits_field(server)); + return wait_for_free_credits(server, optype, + server->ops->get_credits_field(server)); } static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf, @@ -396,7 +397,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov, rc = cifs_setup_async_request(server, iov, nvec, &mid); if (rc) { mutex_unlock(&server->srv_mutex); - cifs_add_credits(server, 1); + add_credits(server, 1); wake_up(&server->request_q); return rc; } @@ -418,7 +419,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov, return rc; out_err: delete_mid(mid); - cifs_add_credits(server, 1); + add_credits(server, 1); wake_up(&server->request_q); return rc; } @@ -582,7 +583,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, mutex_unlock(&ses->server->srv_mutex); cifs_small_buf_release(buf); /* Update # of requests on wire to server */ - cifs_add_credits(ses->server, 1); + add_credits(ses->server, 1); return rc; } @@ -612,7 +613,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, midQ->callback = DeleteMidQEntry; spin_unlock(&GlobalMid_Lock); cifs_small_buf_release(buf); - cifs_add_credits(ses->server, 1); + add_credits(ses->server, 1); return rc; } spin_unlock(&GlobalMid_Lock); @@ -622,7 +623,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, rc = cifs_sync_mid_result(midQ, ses->server); if (rc != 0) { - cifs_add_credits(ses->server, 1); + add_credits(ses->server, 1); return rc; } @@ -648,7 +649,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, midQ->resp_buf = NULL; out: delete_mid(midQ); - cifs_add_credits(ses->server, 1); + add_credits(ses->server, 1); return rc; } @@ -698,7 +699,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, if (rc) { mutex_unlock(&ses->server->srv_mutex); /* Update # of requests on wire to server */ - cifs_add_credits(ses->server, 1); + add_credits(ses->server, 1); return rc; } @@ -730,7 +731,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, /* no longer considered to be "in-flight" */ midQ->callback = DeleteMidQEntry; spin_unlock(&GlobalMid_Lock); - cifs_add_credits(ses->server, 1); + add_credits(ses->server, 1); return rc; } spin_unlock(&GlobalMid_Lock); @@ -738,7 +739,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, rc = cifs_sync_mid_result(midQ, ses->server); if (rc != 0) { - cifs_add_credits(ses->server, 1); + add_credits(ses->server, 1); return rc; } @@ -754,7 +755,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, rc = cifs_check_receive(midQ, ses->server, 0); out: delete_mid(midQ); - cifs_add_credits(ses->server, 1); + add_credits(ses->server, 1); return rc; } -- cgit v0.10.2 From 675f36fb1dfc14eb9f6136cbac505e819ae4be9d Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 24 Feb 2011 17:58:00 +0000 Subject: CIFS: Introduce SMB2 Kconfig option SMB2 is the followon to the CIFS (and SMB) protocols and the default for Windows since Windows Vista, and also now implemented by various non-Windows servers. SMB2 is more secure, has various performance advantages, including larger i/o sizes, flow control, better caching model and more. SMB2 also resolves some scalability limits in the CIFS protocol and adds many new features while being much simpler (only a few dozen commands instead of hundreds) and since the protocol is clearer it is also more consistently implemented across servers and thus easier to optimize. After much discussion with Jeff Layton, Jeremy Allison and others at Connectathon, we decided to move the SMB2 code from a distinct .ko and fstype into distinct C files that optionally build in cifs.ko. As a result the Kconfig gets simpler. To avoid destabilizing CIFS, the SMB2 code is going to be moved into its own experimental CONFIG_CIFS_SMB2 ifdef as it is merged and rereviewed. The changes to stable CIFS (builds with the SMB2 ifdef off) are expected to be fairly small. Reviewed-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 2b243af..a08306a 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -158,3 +158,23 @@ config CIFS_NFSD_EXPORT depends on CIFS && EXPERIMENTAL && BROKEN help Allows NFS server to export a CIFS mounted share (nfsd over cifs) + +config CIFS_SMB2 + bool "SMB2 network file system support (EXPERIMENTAL)" + depends on EXPERIMENTAL && INET && BROKEN + select NLS + select KEYS + select FSCACHE + select DNS_RESOLVER + + help + This enables experimental support for the SMB2 (Server Message Block + version 2) protocol. The SMB2 protocol is the successor to the + popular CIFS and SMB network file sharing protocols. SMB2 is the + native file sharing mechanism for recent versions of Windows + operating systems (since Vista). SMB2 enablement will eventually + allow users better performance, security and features, than would be + possible with cifs. Note that smb2 mount options also are simpler + (compared to cifs) due to protocol improvements. + + Unless you are a developer or tester, say N. -- cgit v0.10.2 From 1080ef758fb87f286b25277d8373e680a9e73363 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 24 Feb 2011 18:07:19 +0000 Subject: CIFS: Introduce SMB2 mounts as vers=2.1 As with Linux nfs client, which uses "nfsvers=" or "vers=" to indicate which protocol to use for mount, specifying "vers=2.1" will force an SMB2 mount. When vers is not specified CIFS is used "vers=1" We can eventually autonegotiate down from SMB2 to CIFS when SMB2 is stable enough to make it the default, but this is for the future. At that time we could also implement a "maxprotocol" mount option as smbclient and Samba have today, but that would be premature until SMB2 is stable. Intially the SMB2 Kconfig option will depend on "BROKEN" until the merge is complete, and then be "EXPERIMENTAL" When it is no longer experimental we can consider changing the default protocol to attempt first. Signed-off-by: Pavel Shilovsky Signed-off-by: Jeff Layton Acked-by: Shirish Pargaonkar Signed-off-by: Steve French diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index 0fe7025..4b41275 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -15,3 +15,5 @@ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o + +cifs-$(CONFIG_CIFS_SMB2) += smb2ops.o diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 2396456..c0a027e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -152,6 +152,7 @@ struct cifs_cred { enum smb_version { Smb_1 = 1, + Smb_21, }; struct mid_q_entry; @@ -1122,4 +1123,7 @@ extern struct workqueue_struct *cifsiod_wq; #define SMB1_VERSION_STRING "1.0" extern struct smb_version_operations smb1_operations; extern struct smb_version_values smb1_values; +#define SMB21_VERSION_STRING "2.1" +extern struct smb_version_operations smb21_operations; +extern struct smb_version_values smb21_values; #endif /* _CIFS_GLOB_H */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index c71c11c..ccafded 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1,7 +1,7 @@ /* * fs/cifs/connect.c * - * Copyright (C) International Business Machines Corp., 2002,2009 + * Copyright (C) International Business Machines Corp., 2002,2011 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -278,6 +278,7 @@ static const match_table_t cifs_cacheflavor_tokens = { static const match_table_t cifs_smb_version_tokens = { { Smb_1, SMB1_VERSION_STRING }, + { Smb_21, SMB21_VERSION_STRING }, }; static int ip_connect(struct TCP_Server_Info *server); @@ -1221,6 +1222,12 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol) vol->ops = &smb1_operations; vol->vals = &smb1_values; break; +#ifdef CONFIG_CIFS_SMB2 + case Smb_21: + vol->ops = &smb21_operations; + vol->vals = &smb21_values; + break; +#endif default: cERROR(1, "Unknown vers= option specified: %s", value); return 1; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c new file mode 100644 index 0000000..f065e89 --- /dev/null +++ b/fs/cifs/smb2ops.c @@ -0,0 +1,27 @@ +/* + * SMB2 version specific operations + * + * Copyright (c) 2012, Jeff Layton + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "cifsglob.h" + +struct smb_version_operations smb21_operations = { +}; + +struct smb_version_values smb21_values = { + .version_string = SMB21_VERSION_STRING, +}; -- cgit v0.10.2 From ea4b574028f0c30d736ab6b13b518af8533a86c4 Mon Sep 17 00:00:00 2001 From: Sedat Dilek Date: Sat, 28 Apr 2012 14:13:53 +0200 Subject: cifs: Fix comment as d_alloc_root() is replaced by d_make_root() For more details see . Signed-off-by: Sedat Dilek Signed-off-by: Steve French diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index d6660f7..3c9c794 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -124,7 +124,7 @@ cifs_read_super(struct super_block *sb) goto out_no_root; } - /* do that *after* d_alloc_root() - we want NULL ->d_op for root here */ + /* do that *after* d_make_root() - we want NULL ->d_op for root here */ if (cifs_sb_master_tcon(cifs_sb)->nocase) sb->s_d_op = &cifs_ci_dentry_ops; else -- cgit v0.10.2 From 2c0c2a08bed7a3b791f88d09d16ace56acb3dd98 Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Mon, 21 May 2012 09:20:12 -0500 Subject: cifs: fix oops while traversing open file list (try #4) While traversing the linked list of open file handles, if the identfied file handle is invalid, a reopen is attempted and if it fails, we resume traversing where we stopped and cifs can oops while accessing invalid next element, for list might have changed. So mark the invalid file handle and attempt reopen if no valid file handle is found in rest of the list. If reopen fails, move the invalid file handle to the end of the list and start traversing the list again from the begining. Repeat this four times before giving up and returning an error if file reopen keeps failing. Cc: Signed-off-by: Shirish Pargaonkar Reviewed-by: Jeff Layton Signed-off-by: Steve French diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index c0a027e..20350a9 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -43,6 +43,7 @@ #define CIFS_MIN_RCV_POOL 4 +#define MAX_REOPEN_ATT 5 /* these many maximum attempts to reopen a file */ /* * default attribute cache timeout (jiffies) */ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index ed9b5a8..253170d 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1565,10 +1565,11 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, bool fsuid_only) { - struct cifsFileInfo *open_file; + struct cifsFileInfo *open_file, *inv_file = NULL; struct cifs_sb_info *cifs_sb; bool any_available = false; int rc; + unsigned int refind = 0; /* Having a null inode here (because mapping->host was set to zero by the VFS or MM) should not happen but we had reports of on oops (due to @@ -1588,40 +1589,25 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, spin_lock(&cifs_file_list_lock); refind_writable: + if (refind > MAX_REOPEN_ATT) { + spin_unlock(&cifs_file_list_lock); + return NULL; + } list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (!any_available && open_file->pid != current->tgid) continue; if (fsuid_only && open_file->uid != current_fsuid()) continue; if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { - cifsFileInfo_get(open_file); - if (!open_file->invalidHandle) { /* found a good writable file */ + cifsFileInfo_get(open_file); spin_unlock(&cifs_file_list_lock); return open_file; + } else { + if (!inv_file) + inv_file = open_file; } - - spin_unlock(&cifs_file_list_lock); - - /* Had to unlock since following call can block */ - rc = cifs_reopen_file(open_file, false); - if (!rc) - return open_file; - - /* if it fails, try another handle if possible */ - cFYI(1, "wp failed on reopen file"); - cifsFileInfo_put(open_file); - - spin_lock(&cifs_file_list_lock); - - /* else we simply continue to the next entry. Thus - we do not loop on reopen errors. If we - can not reopen the file, for example if we - reconnected to a server with another client - racing to delete or lock the file we would not - make progress if we restarted before the beginning - of the loop here. */ } } /* couldn't find useable FH with same pid, try any available */ @@ -1629,7 +1615,30 @@ refind_writable: any_available = true; goto refind_writable; } + + if (inv_file) { + any_available = false; + cifsFileInfo_get(inv_file); + } + spin_unlock(&cifs_file_list_lock); + + if (inv_file) { + rc = cifs_reopen_file(inv_file, false); + if (!rc) + return inv_file; + else { + spin_lock(&cifs_file_list_lock); + list_move_tail(&inv_file->flist, + &cifs_inode->openFileList); + spin_unlock(&cifs_file_list_lock); + cifsFileInfo_put(inv_file); + spin_lock(&cifs_file_list_lock); + ++refind; + goto refind_writable; + } + } + return NULL; } -- cgit v0.10.2