mirror of https://gitee.com/openkylin/linux.git
Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
Pull cifs fixes from Steve French: "Various fixes for stable for CIFS/SMB3 especially for better interoperability for SMB3 to Macs. It also includes Pavel's improvements to SMB3 async i/o support (which is much faster now)" * 'for-next' of git://git.samba.org/sfrench/cifs-2.6: CIFS: add misssing SFM mapping for doublequote SMB3: Work around mount failure when using SMB3 dialect to Macs cifs: fix CIFS_IOC_GET_MNT_INFO oops CIFS: fix mapping of SFM_SPACE and SFM_PERIOD CIFS: fix oplock break deadlocks cifs: fix CIFS_ENUMERATE_SNAPSHOTS oops cifs: fix leak in FSCTL_ENUM_SNAPS response handling Set unicode flag on cifs echo request to avoid Mac error CIFS: Add asynchronous write support through kernel AIO CIFS: Add asynchronous read support through kernel AIO CIFS: Add asynchronous context to support kernel AIO cifs: fix IPv6 link local, with scope id, address parsing cifs: small underflow in cnvrtDosUnixTm()
This commit is contained in:
commit
fe7a719b30
|
@ -83,6 +83,9 @@ convert_sfm_char(const __u16 src_char, char *target)
|
|||
case SFM_COLON:
|
||||
*target = ':';
|
||||
break;
|
||||
case SFM_DOUBLEQUOTE:
|
||||
*target = '"';
|
||||
break;
|
||||
case SFM_ASTERISK:
|
||||
*target = '*';
|
||||
break;
|
||||
|
@ -418,6 +421,9 @@ static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
|
|||
case ':':
|
||||
dest_char = cpu_to_le16(SFM_COLON);
|
||||
break;
|
||||
case '"':
|
||||
dest_char = cpu_to_le16(SFM_DOUBLEQUOTE);
|
||||
break;
|
||||
case '*':
|
||||
dest_char = cpu_to_le16(SFM_ASTERISK);
|
||||
break;
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
* not conflict (although almost does) with the mapping above.
|
||||
*/
|
||||
|
||||
#define SFM_DOUBLEQUOTE ((__u16) 0xF020)
|
||||
#define SFM_ASTERISK ((__u16) 0xF021)
|
||||
#define SFM_QUESTION ((__u16) 0xF025)
|
||||
#define SFM_COLON ((__u16) 0xF022)
|
||||
|
@ -64,8 +65,8 @@
|
|||
#define SFM_LESSTHAN ((__u16) 0xF023)
|
||||
#define SFM_PIPE ((__u16) 0xF027)
|
||||
#define SFM_SLASH ((__u16) 0xF026)
|
||||
#define SFM_PERIOD ((__u16) 0xF028)
|
||||
#define SFM_SPACE ((__u16) 0xF029)
|
||||
#define SFM_SPACE ((__u16) 0xF028)
|
||||
#define SFM_PERIOD ((__u16) 0xF029)
|
||||
|
||||
/*
|
||||
* Mapping mechanism to use when one of the seven reserved characters is
|
||||
|
|
|
@ -88,6 +88,7 @@ extern mempool_t *cifs_req_poolp;
|
|||
extern mempool_t *cifs_mid_poolp;
|
||||
|
||||
struct workqueue_struct *cifsiod_wq;
|
||||
struct workqueue_struct *cifsoplockd_wq;
|
||||
__u32 cifs_lock_secret;
|
||||
|
||||
/*
|
||||
|
@ -1375,9 +1376,16 @@ init_cifs(void)
|
|||
goto out_clean_proc;
|
||||
}
|
||||
|
||||
cifsoplockd_wq = alloc_workqueue("cifsoplockd",
|
||||
WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
|
||||
if (!cifsoplockd_wq) {
|
||||
rc = -ENOMEM;
|
||||
goto out_destroy_cifsiod_wq;
|
||||
}
|
||||
|
||||
rc = cifs_fscache_register();
|
||||
if (rc)
|
||||
goto out_destroy_wq;
|
||||
goto out_destroy_cifsoplockd_wq;
|
||||
|
||||
rc = cifs_init_inodecache();
|
||||
if (rc)
|
||||
|
@ -1425,7 +1433,9 @@ init_cifs(void)
|
|||
cifs_destroy_inodecache();
|
||||
out_unreg_fscache:
|
||||
cifs_fscache_unregister();
|
||||
out_destroy_wq:
|
||||
out_destroy_cifsoplockd_wq:
|
||||
destroy_workqueue(cifsoplockd_wq);
|
||||
out_destroy_cifsiod_wq:
|
||||
destroy_workqueue(cifsiod_wq);
|
||||
out_clean_proc:
|
||||
cifs_proc_clean();
|
||||
|
@ -1448,6 +1458,7 @@ exit_cifs(void)
|
|||
cifs_destroy_mids();
|
||||
cifs_destroy_inodecache();
|
||||
cifs_fscache_unregister();
|
||||
destroy_workqueue(cifsoplockd_wq);
|
||||
destroy_workqueue(cifsiod_wq);
|
||||
cifs_proc_clean();
|
||||
}
|
||||
|
|
|
@ -1115,6 +1115,23 @@ struct cifs_io_parms {
|
|||
struct cifs_tcon *tcon;
|
||||
};
|
||||
|
||||
struct cifs_aio_ctx {
|
||||
struct kref refcount;
|
||||
struct list_head list;
|
||||
struct mutex aio_mutex;
|
||||
struct completion done;
|
||||
struct iov_iter iter;
|
||||
struct kiocb *iocb;
|
||||
struct cifsFileInfo *cfile;
|
||||
struct bio_vec *bv;
|
||||
loff_t pos;
|
||||
unsigned int npages;
|
||||
ssize_t rc;
|
||||
unsigned int len;
|
||||
unsigned int total_len;
|
||||
bool should_dirty;
|
||||
};
|
||||
|
||||
struct cifs_readdata;
|
||||
|
||||
/* asynchronous read support */
|
||||
|
@ -1124,6 +1141,7 @@ struct cifs_readdata {
|
|||
struct completion done;
|
||||
struct cifsFileInfo *cfile;
|
||||
struct address_space *mapping;
|
||||
struct cifs_aio_ctx *ctx;
|
||||
__u64 offset;
|
||||
unsigned int bytes;
|
||||
unsigned int got_bytes;
|
||||
|
@ -1154,6 +1172,7 @@ struct cifs_writedata {
|
|||
enum writeback_sync_modes sync_mode;
|
||||
struct work_struct work;
|
||||
struct cifsFileInfo *cfile;
|
||||
struct cifs_aio_ctx *ctx;
|
||||
__u64 offset;
|
||||
pid_t pid;
|
||||
unsigned int bytes;
|
||||
|
@ -1683,6 +1702,7 @@ void cifs_oplock_break(struct work_struct *work);
|
|||
|
||||
extern const struct slow_work_ops cifs_oplock_break_ops;
|
||||
extern struct workqueue_struct *cifsiod_wq;
|
||||
extern struct workqueue_struct *cifsoplockd_wq;
|
||||
extern __u32 cifs_lock_secret;
|
||||
|
||||
extern mempool_t *cifs_mid_poolp;
|
||||
|
|
|
@ -535,4 +535,7 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
|
|||
struct shash_desc *shash);
|
||||
enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
|
||||
enum securityEnum);
|
||||
struct cifs_aio_ctx *cifs_aio_ctx_alloc(void);
|
||||
void cifs_aio_ctx_release(struct kref *refcount);
|
||||
int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw);
|
||||
#endif /* _CIFSPROTO_H */
|
||||
|
|
|
@ -718,6 +718,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (server->capabilities & CAP_UNICODE)
|
||||
smb->hdr.Flags2 |= SMBFLG2_UNICODE;
|
||||
|
||||
/* set up echo request */
|
||||
smb->hdr.Tid = 0xffff;
|
||||
smb->hdr.WordCount = 1;
|
||||
|
|
|
@ -1946,9 +1946,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
|
|||
}
|
||||
|
||||
if (!got_ip) {
|
||||
int len;
|
||||
const char *slash;
|
||||
|
||||
/* No ip= option specified? Try to get it from UNC */
|
||||
if (!cifs_convert_address(dstaddr, &vol->UNC[2],
|
||||
strlen(&vol->UNC[2]))) {
|
||||
/* Use the address part of the UNC. */
|
||||
slash = strchr(&vol->UNC[2], '\\');
|
||||
len = slash - &vol->UNC[2];
|
||||
if (!cifs_convert_address(dstaddr, &vol->UNC[2], len)) {
|
||||
pr_err("Unable to determine destination address.\n");
|
||||
goto cifs_parse_mount_err;
|
||||
}
|
||||
|
|
361
fs/cifs/file.c
361
fs/cifs/file.c
|
@ -2458,11 +2458,14 @@ cifs_uncached_writedata_release(struct kref *refcount)
|
|||
struct cifs_writedata *wdata = container_of(refcount,
|
||||
struct cifs_writedata, refcount);
|
||||
|
||||
kref_put(&wdata->ctx->refcount, cifs_aio_ctx_release);
|
||||
for (i = 0; i < wdata->nr_pages; i++)
|
||||
put_page(wdata->pages[i]);
|
||||
cifs_writedata_release(refcount);
|
||||
}
|
||||
|
||||
static void collect_uncached_write_data(struct cifs_aio_ctx *ctx);
|
||||
|
||||
static void
|
||||
cifs_uncached_writev_complete(struct work_struct *work)
|
||||
{
|
||||
|
@ -2478,7 +2481,8 @@ cifs_uncached_writev_complete(struct work_struct *work)
|
|||
spin_unlock(&inode->i_lock);
|
||||
|
||||
complete(&wdata->done);
|
||||
|
||||
collect_uncached_write_data(wdata->ctx);
|
||||
/* the below call can possibly free the last ref to aio ctx */
|
||||
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
|
||||
}
|
||||
|
||||
|
@ -2527,7 +2531,8 @@ wdata_fill_from_iovec(struct cifs_writedata *wdata, struct iov_iter *from,
|
|||
static int
|
||||
cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
|
||||
struct cifsFileInfo *open_file,
|
||||
struct cifs_sb_info *cifs_sb, struct list_head *wdata_list)
|
||||
struct cifs_sb_info *cifs_sb, struct list_head *wdata_list,
|
||||
struct cifs_aio_ctx *ctx)
|
||||
{
|
||||
int rc = 0;
|
||||
size_t cur_len;
|
||||
|
@ -2595,6 +2600,8 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
|
|||
wdata->pagesz = PAGE_SIZE;
|
||||
wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE);
|
||||
wdata->credits = credits;
|
||||
wdata->ctx = ctx;
|
||||
kref_get(&ctx->refcount);
|
||||
|
||||
if (!wdata->cfile->invalidHandle ||
|
||||
!(rc = cifs_reopen_file(wdata->cfile, false)))
|
||||
|
@ -2620,15 +2627,95 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static void collect_uncached_write_data(struct cifs_aio_ctx *ctx)
|
||||
{
|
||||
struct cifs_writedata *wdata, *tmp;
|
||||
struct cifs_tcon *tcon;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct dentry *dentry = ctx->cfile->dentry;
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
tcon = tlink_tcon(ctx->cfile->tlink);
|
||||
cifs_sb = CIFS_SB(dentry->d_sb);
|
||||
|
||||
mutex_lock(&ctx->aio_mutex);
|
||||
|
||||
if (list_empty(&ctx->list)) {
|
||||
mutex_unlock(&ctx->aio_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = ctx->rc;
|
||||
/*
|
||||
* Wait for and collect replies for any successful sends in order of
|
||||
* increasing offset. Once an error is hit, then return without waiting
|
||||
* for any more replies.
|
||||
*/
|
||||
restart_loop:
|
||||
list_for_each_entry_safe(wdata, tmp, &ctx->list, list) {
|
||||
if (!rc) {
|
||||
if (!try_wait_for_completion(&wdata->done)) {
|
||||
mutex_unlock(&ctx->aio_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (wdata->result)
|
||||
rc = wdata->result;
|
||||
else
|
||||
ctx->total_len += wdata->bytes;
|
||||
|
||||
/* resend call if it's a retryable error */
|
||||
if (rc == -EAGAIN) {
|
||||
struct list_head tmp_list;
|
||||
struct iov_iter tmp_from = ctx->iter;
|
||||
|
||||
INIT_LIST_HEAD(&tmp_list);
|
||||
list_del_init(&wdata->list);
|
||||
|
||||
iov_iter_advance(&tmp_from,
|
||||
wdata->offset - ctx->pos);
|
||||
|
||||
rc = cifs_write_from_iter(wdata->offset,
|
||||
wdata->bytes, &tmp_from,
|
||||
ctx->cfile, cifs_sb, &tmp_list,
|
||||
ctx);
|
||||
|
||||
list_splice(&tmp_list, &ctx->list);
|
||||
|
||||
kref_put(&wdata->refcount,
|
||||
cifs_uncached_writedata_release);
|
||||
goto restart_loop;
|
||||
}
|
||||
}
|
||||
list_del_init(&wdata->list);
|
||||
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
|
||||
}
|
||||
|
||||
for (i = 0; i < ctx->npages; i++)
|
||||
put_page(ctx->bv[i].bv_page);
|
||||
|
||||
cifs_stats_bytes_written(tcon, ctx->total_len);
|
||||
set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(dentry->d_inode)->flags);
|
||||
|
||||
ctx->rc = (rc == 0) ? ctx->total_len : rc;
|
||||
|
||||
mutex_unlock(&ctx->aio_mutex);
|
||||
|
||||
if (ctx->iocb && ctx->iocb->ki_complete)
|
||||
ctx->iocb->ki_complete(ctx->iocb, ctx->rc, 0);
|
||||
else
|
||||
complete(&ctx->done);
|
||||
}
|
||||
|
||||
ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
struct file *file = iocb->ki_filp;
|
||||
ssize_t total_written = 0;
|
||||
struct cifsFileInfo *open_file;
|
||||
struct cifsFileInfo *cfile;
|
||||
struct cifs_tcon *tcon;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct cifs_writedata *wdata, *tmp;
|
||||
struct list_head wdata_list;
|
||||
struct cifs_aio_ctx *ctx;
|
||||
struct iov_iter saved_from = *from;
|
||||
int rc;
|
||||
|
||||
|
@ -2642,16 +2729,35 @@ ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from)
|
|||
if (rc <= 0)
|
||||
return rc;
|
||||
|
||||
INIT_LIST_HEAD(&wdata_list);
|
||||
cifs_sb = CIFS_FILE_SB(file);
|
||||
open_file = file->private_data;
|
||||
tcon = tlink_tcon(open_file->tlink);
|
||||
cfile = file->private_data;
|
||||
tcon = tlink_tcon(cfile->tlink);
|
||||
|
||||
if (!tcon->ses->server->ops->async_writev)
|
||||
return -ENOSYS;
|
||||
|
||||
rc = cifs_write_from_iter(iocb->ki_pos, iov_iter_count(from), from,
|
||||
open_file, cifs_sb, &wdata_list);
|
||||
ctx = cifs_aio_ctx_alloc();
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->cfile = cifsFileInfo_get(cfile);
|
||||
|
||||
if (!is_sync_kiocb(iocb))
|
||||
ctx->iocb = iocb;
|
||||
|
||||
ctx->pos = iocb->ki_pos;
|
||||
|
||||
rc = setup_aio_ctx_iter(ctx, from, WRITE);
|
||||
if (rc) {
|
||||
kref_put(&ctx->refcount, cifs_aio_ctx_release);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* grab a lock here due to read response handlers can access ctx */
|
||||
mutex_lock(&ctx->aio_mutex);
|
||||
|
||||
rc = cifs_write_from_iter(iocb->ki_pos, ctx->len, &saved_from,
|
||||
cfile, cifs_sb, &ctx->list, ctx);
|
||||
|
||||
/*
|
||||
* If at least one write was successfully sent, then discard any rc
|
||||
|
@ -2659,58 +2765,38 @@ ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from)
|
|||
* we'll end up returning whatever was written. If it fails, then
|
||||
* we'll get a new rc value from that.
|
||||
*/
|
||||
if (!list_empty(&wdata_list))
|
||||
if (!list_empty(&ctx->list))
|
||||
rc = 0;
|
||||
|
||||
/*
|
||||
* Wait for and collect replies for any successful sends in order of
|
||||
* increasing offset. Once an error is hit or we get a fatal signal
|
||||
* while waiting, then return without waiting for any more replies.
|
||||
*/
|
||||
restart_loop:
|
||||
list_for_each_entry_safe(wdata, tmp, &wdata_list, list) {
|
||||
if (!rc) {
|
||||
/* FIXME: freezable too? */
|
||||
rc = wait_for_completion_killable(&wdata->done);
|
||||
if (rc)
|
||||
rc = -EINTR;
|
||||
else if (wdata->result)
|
||||
rc = wdata->result;
|
||||
else
|
||||
total_written += wdata->bytes;
|
||||
mutex_unlock(&ctx->aio_mutex);
|
||||
|
||||
/* resend call if it's a retryable error */
|
||||
if (rc == -EAGAIN) {
|
||||
struct list_head tmp_list;
|
||||
struct iov_iter tmp_from = saved_from;
|
||||
|
||||
INIT_LIST_HEAD(&tmp_list);
|
||||
list_del_init(&wdata->list);
|
||||
|
||||
iov_iter_advance(&tmp_from,
|
||||
wdata->offset - iocb->ki_pos);
|
||||
|
||||
rc = cifs_write_from_iter(wdata->offset,
|
||||
wdata->bytes, &tmp_from,
|
||||
open_file, cifs_sb, &tmp_list);
|
||||
|
||||
list_splice(&tmp_list, &wdata_list);
|
||||
|
||||
kref_put(&wdata->refcount,
|
||||
cifs_uncached_writedata_release);
|
||||
goto restart_loop;
|
||||
}
|
||||
}
|
||||
list_del_init(&wdata->list);
|
||||
kref_put(&wdata->refcount, cifs_uncached_writedata_release);
|
||||
if (rc) {
|
||||
kref_put(&ctx->refcount, cifs_aio_ctx_release);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!is_sync_kiocb(iocb)) {
|
||||
kref_put(&ctx->refcount, cifs_aio_ctx_release);
|
||||
return -EIOCBQUEUED;
|
||||
}
|
||||
|
||||
rc = wait_for_completion_killable(&ctx->done);
|
||||
if (rc) {
|
||||
mutex_lock(&ctx->aio_mutex);
|
||||
ctx->rc = rc = -EINTR;
|
||||
total_written = ctx->total_len;
|
||||
mutex_unlock(&ctx->aio_mutex);
|
||||
} else {
|
||||
rc = ctx->rc;
|
||||
total_written = ctx->total_len;
|
||||
}
|
||||
|
||||
kref_put(&ctx->refcount, cifs_aio_ctx_release);
|
||||
|
||||
if (unlikely(!total_written))
|
||||
return rc;
|
||||
|
||||
iocb->ki_pos += total_written;
|
||||
set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(file_inode(file))->flags);
|
||||
cifs_stats_bytes_written(tcon, total_written);
|
||||
return total_written;
|
||||
}
|
||||
|
||||
|
@ -2859,6 +2945,7 @@ cifs_uncached_readdata_release(struct kref *refcount)
|
|||
struct cifs_readdata, refcount);
|
||||
unsigned int i;
|
||||
|
||||
kref_put(&rdata->ctx->refcount, cifs_aio_ctx_release);
|
||||
for (i = 0; i < rdata->nr_pages; i++) {
|
||||
put_page(rdata->pages[i]);
|
||||
rdata->pages[i] = NULL;
|
||||
|
@ -2900,6 +2987,8 @@ cifs_readdata_to_iov(struct cifs_readdata *rdata, struct iov_iter *iter)
|
|||
return remaining ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
static void collect_uncached_read_data(struct cifs_aio_ctx *ctx);
|
||||
|
||||
static void
|
||||
cifs_uncached_readv_complete(struct work_struct *work)
|
||||
{
|
||||
|
@ -2907,6 +2996,8 @@ cifs_uncached_readv_complete(struct work_struct *work)
|
|||
struct cifs_readdata, work);
|
||||
|
||||
complete(&rdata->done);
|
||||
collect_uncached_read_data(rdata->ctx);
|
||||
/* the below call can possibly free the last ref to aio ctx */
|
||||
kref_put(&rdata->refcount, cifs_uncached_readdata_release);
|
||||
}
|
||||
|
||||
|
@ -2973,7 +3064,8 @@ cifs_uncached_copy_into_pages(struct TCP_Server_Info *server,
|
|||
|
||||
static int
|
||||
cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
|
||||
struct cifs_sb_info *cifs_sb, struct list_head *rdata_list)
|
||||
struct cifs_sb_info *cifs_sb, struct list_head *rdata_list,
|
||||
struct cifs_aio_ctx *ctx)
|
||||
{
|
||||
struct cifs_readdata *rdata;
|
||||
unsigned int npages, rsize, credits;
|
||||
|
@ -3020,6 +3112,8 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
|
|||
rdata->read_into_pages = cifs_uncached_read_into_pages;
|
||||
rdata->copy_into_pages = cifs_uncached_copy_into_pages;
|
||||
rdata->credits = credits;
|
||||
rdata->ctx = ctx;
|
||||
kref_get(&ctx->refcount);
|
||||
|
||||
if (!rdata->cfile->invalidHandle ||
|
||||
!(rc = cifs_reopen_file(rdata->cfile, true)))
|
||||
|
@ -3042,50 +3136,37 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
|
|||
return rc;
|
||||
}
|
||||
|
||||
ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to)
|
||||
static void
|
||||
collect_uncached_read_data(struct cifs_aio_ctx *ctx)
|
||||
{
|
||||
struct file *file = iocb->ki_filp;
|
||||
ssize_t rc;
|
||||
size_t len;
|
||||
ssize_t total_read = 0;
|
||||
loff_t offset = iocb->ki_pos;
|
||||
struct cifs_readdata *rdata, *tmp;
|
||||
struct iov_iter *to = &ctx->iter;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct cifs_tcon *tcon;
|
||||
struct cifsFileInfo *open_file;
|
||||
struct cifs_readdata *rdata, *tmp;
|
||||
struct list_head rdata_list;
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
len = iov_iter_count(to);
|
||||
if (!len)
|
||||
return 0;
|
||||
tcon = tlink_tcon(ctx->cfile->tlink);
|
||||
cifs_sb = CIFS_SB(ctx->cfile->dentry->d_sb);
|
||||
|
||||
INIT_LIST_HEAD(&rdata_list);
|
||||
cifs_sb = CIFS_FILE_SB(file);
|
||||
open_file = file->private_data;
|
||||
tcon = tlink_tcon(open_file->tlink);
|
||||
mutex_lock(&ctx->aio_mutex);
|
||||
|
||||
if (!tcon->ses->server->ops->async_readv)
|
||||
return -ENOSYS;
|
||||
if (list_empty(&ctx->list)) {
|
||||
mutex_unlock(&ctx->aio_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((file->f_flags & O_ACCMODE) == O_WRONLY)
|
||||
cifs_dbg(FYI, "attempting read on write only file instance\n");
|
||||
|
||||
rc = cifs_send_async_read(offset, len, open_file, cifs_sb, &rdata_list);
|
||||
|
||||
/* if at least one read request send succeeded, then reset rc */
|
||||
if (!list_empty(&rdata_list))
|
||||
rc = 0;
|
||||
|
||||
len = iov_iter_count(to);
|
||||
rc = ctx->rc;
|
||||
/* the loop below should proceed in the order of increasing offsets */
|
||||
again:
|
||||
list_for_each_entry_safe(rdata, tmp, &rdata_list, list) {
|
||||
list_for_each_entry_safe(rdata, tmp, &ctx->list, list) {
|
||||
if (!rc) {
|
||||
/* FIXME: freezable sleep too? */
|
||||
rc = wait_for_completion_killable(&rdata->done);
|
||||
if (rc)
|
||||
rc = -EINTR;
|
||||
else if (rdata->result == -EAGAIN) {
|
||||
if (!try_wait_for_completion(&rdata->done)) {
|
||||
mutex_unlock(&ctx->aio_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rdata->result == -EAGAIN) {
|
||||
/* resend call if it's a retryable error */
|
||||
struct list_head tmp_list;
|
||||
unsigned int got_bytes = rdata->got_bytes;
|
||||
|
@ -3111,9 +3192,9 @@ ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to)
|
|||
rdata->offset + got_bytes,
|
||||
rdata->bytes - got_bytes,
|
||||
rdata->cfile, cifs_sb,
|
||||
&tmp_list);
|
||||
&tmp_list, ctx);
|
||||
|
||||
list_splice(&tmp_list, &rdata_list);
|
||||
list_splice(&tmp_list, &ctx->list);
|
||||
|
||||
kref_put(&rdata->refcount,
|
||||
cifs_uncached_readdata_release);
|
||||
|
@ -3131,14 +3212,110 @@ ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to)
|
|||
kref_put(&rdata->refcount, cifs_uncached_readdata_release);
|
||||
}
|
||||
|
||||
total_read = len - iov_iter_count(to);
|
||||
for (i = 0; i < ctx->npages; i++) {
|
||||
if (ctx->should_dirty)
|
||||
set_page_dirty(ctx->bv[i].bv_page);
|
||||
put_page(ctx->bv[i].bv_page);
|
||||
}
|
||||
|
||||
cifs_stats_bytes_read(tcon, total_read);
|
||||
ctx->total_len = ctx->len - iov_iter_count(to);
|
||||
|
||||
cifs_stats_bytes_read(tcon, ctx->total_len);
|
||||
|
||||
/* mask nodata case */
|
||||
if (rc == -ENODATA)
|
||||
rc = 0;
|
||||
|
||||
ctx->rc = (rc == 0) ? ctx->total_len : rc;
|
||||
|
||||
mutex_unlock(&ctx->aio_mutex);
|
||||
|
||||
if (ctx->iocb && ctx->iocb->ki_complete)
|
||||
ctx->iocb->ki_complete(ctx->iocb, ctx->rc, 0);
|
||||
else
|
||||
complete(&ctx->done);
|
||||
}
|
||||
|
||||
ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to)
|
||||
{
|
||||
struct file *file = iocb->ki_filp;
|
||||
ssize_t rc;
|
||||
size_t len;
|
||||
ssize_t total_read = 0;
|
||||
loff_t offset = iocb->ki_pos;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct cifs_tcon *tcon;
|
||||
struct cifsFileInfo *cfile;
|
||||
struct cifs_aio_ctx *ctx;
|
||||
|
||||
len = iov_iter_count(to);
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
cifs_sb = CIFS_FILE_SB(file);
|
||||
cfile = file->private_data;
|
||||
tcon = tlink_tcon(cfile->tlink);
|
||||
|
||||
if (!tcon->ses->server->ops->async_readv)
|
||||
return -ENOSYS;
|
||||
|
||||
if ((file->f_flags & O_ACCMODE) == O_WRONLY)
|
||||
cifs_dbg(FYI, "attempting read on write only file instance\n");
|
||||
|
||||
ctx = cifs_aio_ctx_alloc();
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->cfile = cifsFileInfo_get(cfile);
|
||||
|
||||
if (!is_sync_kiocb(iocb))
|
||||
ctx->iocb = iocb;
|
||||
|
||||
if (to->type & ITER_IOVEC)
|
||||
ctx->should_dirty = true;
|
||||
|
||||
rc = setup_aio_ctx_iter(ctx, to, READ);
|
||||
if (rc) {
|
||||
kref_put(&ctx->refcount, cifs_aio_ctx_release);
|
||||
return rc;
|
||||
}
|
||||
|
||||
len = ctx->len;
|
||||
|
||||
/* grab a lock here due to read response handlers can access ctx */
|
||||
mutex_lock(&ctx->aio_mutex);
|
||||
|
||||
rc = cifs_send_async_read(offset, len, cfile, cifs_sb, &ctx->list, ctx);
|
||||
|
||||
/* if at least one read request send succeeded, then reset rc */
|
||||
if (!list_empty(&ctx->list))
|
||||
rc = 0;
|
||||
|
||||
mutex_unlock(&ctx->aio_mutex);
|
||||
|
||||
if (rc) {
|
||||
kref_put(&ctx->refcount, cifs_aio_ctx_release);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!is_sync_kiocb(iocb)) {
|
||||
kref_put(&ctx->refcount, cifs_aio_ctx_release);
|
||||
return -EIOCBQUEUED;
|
||||
}
|
||||
|
||||
rc = wait_for_completion_killable(&ctx->done);
|
||||
if (rc) {
|
||||
mutex_lock(&ctx->aio_mutex);
|
||||
ctx->rc = rc = -EINTR;
|
||||
total_read = ctx->total_len;
|
||||
mutex_unlock(&ctx->aio_mutex);
|
||||
} else {
|
||||
rc = ctx->rc;
|
||||
total_read = ctx->total_len;
|
||||
}
|
||||
|
||||
kref_put(&ctx->refcount, cifs_aio_ctx_release);
|
||||
|
||||
if (total_read) {
|
||||
iocb->ki_pos += total_read;
|
||||
return total_read;
|
||||
|
|
|
@ -209,10 +209,14 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
|||
rc = -EOPNOTSUPP;
|
||||
break;
|
||||
case CIFS_IOC_GET_MNT_INFO:
|
||||
if (pSMBFile == NULL)
|
||||
break;
|
||||
tcon = tlink_tcon(pSMBFile->tlink);
|
||||
rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
|
||||
break;
|
||||
case CIFS_ENUMERATE_SNAPSHOTS:
|
||||
if (pSMBFile == NULL)
|
||||
break;
|
||||
if (arg == 0) {
|
||||
rc = -EINVAL;
|
||||
goto cifs_ioc_exit;
|
||||
|
|
122
fs/cifs/misc.c
122
fs/cifs/misc.c
|
@ -22,6 +22,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/mempool.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include "cifspdu.h"
|
||||
#include "cifsglob.h"
|
||||
#include "cifsproto.h"
|
||||
|
@ -488,7 +489,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
|
|||
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
||||
&pCifsInode->flags);
|
||||
|
||||
queue_work(cifsiod_wq,
|
||||
queue_work(cifsoplockd_wq,
|
||||
&netfile->oplock_break);
|
||||
netfile->oplock_break_cancelled = false;
|
||||
|
||||
|
@ -741,3 +742,122 @@ parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
|
|||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct cifs_aio_ctx *
|
||||
cifs_aio_ctx_alloc(void)
|
||||
{
|
||||
struct cifs_aio_ctx *ctx;
|
||||
|
||||
ctx = kzalloc(sizeof(struct cifs_aio_ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&ctx->list);
|
||||
mutex_init(&ctx->aio_mutex);
|
||||
init_completion(&ctx->done);
|
||||
kref_init(&ctx->refcount);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void
|
||||
cifs_aio_ctx_release(struct kref *refcount)
|
||||
{
|
||||
struct cifs_aio_ctx *ctx = container_of(refcount,
|
||||
struct cifs_aio_ctx, refcount);
|
||||
|
||||
cifsFileInfo_put(ctx->cfile);
|
||||
kvfree(ctx->bv);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
#define CIFS_AIO_KMALLOC_LIMIT (1024 * 1024)
|
||||
|
||||
int
|
||||
setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw)
|
||||
{
|
||||
ssize_t rc;
|
||||
unsigned int cur_npages;
|
||||
unsigned int npages = 0;
|
||||
unsigned int i;
|
||||
size_t len;
|
||||
size_t count = iov_iter_count(iter);
|
||||
unsigned int saved_len;
|
||||
size_t start;
|
||||
unsigned int max_pages = iov_iter_npages(iter, INT_MAX);
|
||||
struct page **pages = NULL;
|
||||
struct bio_vec *bv = NULL;
|
||||
|
||||
if (iter->type & ITER_KVEC) {
|
||||
memcpy(&ctx->iter, iter, sizeof(struct iov_iter));
|
||||
ctx->len = count;
|
||||
iov_iter_advance(iter, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (max_pages * sizeof(struct bio_vec) <= CIFS_AIO_KMALLOC_LIMIT)
|
||||
bv = kmalloc_array(max_pages, sizeof(struct bio_vec),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!bv) {
|
||||
bv = vmalloc(max_pages * sizeof(struct bio_vec));
|
||||
if (!bv)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (max_pages * sizeof(struct page *) <= CIFS_AIO_KMALLOC_LIMIT)
|
||||
pages = kmalloc_array(max_pages, sizeof(struct page *),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!pages) {
|
||||
pages = vmalloc(max_pages * sizeof(struct page *));
|
||||
if (!bv) {
|
||||
kvfree(bv);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
saved_len = count;
|
||||
|
||||
while (count && npages < max_pages) {
|
||||
rc = iov_iter_get_pages(iter, pages, count, max_pages, &start);
|
||||
if (rc < 0) {
|
||||
cifs_dbg(VFS, "couldn't get user pages (rc=%zd)\n", rc);
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc > count) {
|
||||
cifs_dbg(VFS, "get pages rc=%zd more than %zu\n", rc,
|
||||
count);
|
||||
break;
|
||||
}
|
||||
|
||||
iov_iter_advance(iter, rc);
|
||||
count -= rc;
|
||||
rc += start;
|
||||
cur_npages = DIV_ROUND_UP(rc, PAGE_SIZE);
|
||||
|
||||
if (npages + cur_npages > max_pages) {
|
||||
cifs_dbg(VFS, "out of vec array capacity (%u vs %u)\n",
|
||||
npages + cur_npages, max_pages);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < cur_npages; i++) {
|
||||
len = rc > PAGE_SIZE ? PAGE_SIZE : rc;
|
||||
bv[npages + i].bv_page = pages[i];
|
||||
bv[npages + i].bv_offset = start;
|
||||
bv[npages + i].bv_len = len - start;
|
||||
rc -= len;
|
||||
start = 0;
|
||||
}
|
||||
|
||||
npages += cur_npages;
|
||||
}
|
||||
|
||||
kvfree(pages);
|
||||
ctx->bv = bv;
|
||||
ctx->len = saved_len - count;
|
||||
ctx->npages = npages;
|
||||
iov_iter_bvec(&ctx->iter, ITER_BVEC | rw, ctx->bv, npages, ctx->len);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -980,10 +980,10 @@ struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
|
|||
cifs_dbg(VFS, "illegal hours %d\n", st->Hours);
|
||||
days = sd->Day;
|
||||
month = sd->Month;
|
||||
if ((days > 31) || (month > 12)) {
|
||||
if (days < 1 || days > 31 || month < 1 || month > 12) {
|
||||
cifs_dbg(VFS, "illegal date, month %d day: %d\n", month, days);
|
||||
if (month > 12)
|
||||
month = 12;
|
||||
days = clamp(days, 1, 31);
|
||||
month = clamp(month, 1, 12);
|
||||
}
|
||||
month -= 1;
|
||||
days += total_days_of_prev_months[month];
|
||||
|
|
|
@ -499,7 +499,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
|
|||
else
|
||||
cfile->oplock_break_cancelled = true;
|
||||
|
||||
queue_work(cifsiod_wq, &cfile->oplock_break);
|
||||
queue_work(cifsoplockd_wq, &cfile->oplock_break);
|
||||
kfree(lw);
|
||||
return true;
|
||||
}
|
||||
|
@ -643,7 +643,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
|
|||
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
|
||||
&cinode->flags);
|
||||
spin_unlock(&cfile->file_info_lock);
|
||||
queue_work(cifsiod_wq, &cfile->oplock_break);
|
||||
queue_work(cifsoplockd_wq,
|
||||
&cfile->oplock_break);
|
||||
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
|
|
@ -942,6 +942,7 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
}
|
||||
if (snapshot_in.snapshot_array_size < sizeof(struct smb_snapshot_array)) {
|
||||
rc = -ERANGE;
|
||||
kfree(retbuf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -633,8 +633,12 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
|
|||
}
|
||||
|
||||
if (rsplen != sizeof(struct validate_negotiate_info_rsp)) {
|
||||
cifs_dbg(VFS, "invalid size of protocol negotiate response\n");
|
||||
return -EIO;
|
||||
cifs_dbg(VFS, "invalid protocol negotiate response size: %d\n",
|
||||
rsplen);
|
||||
|
||||
/* relax check since Mac returns max bufsize allowed on ioctl */
|
||||
if (rsplen > CIFSMaxBufSize)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* check validate negotiate info response matches what we got earlier */
|
||||
|
@ -1854,8 +1858,12 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
|||
* than one credit. Windows typically sets this smaller, but for some
|
||||
* ioctls it may be useful to allow server to send more. No point
|
||||
* limiting what the server can send as long as fits in one credit
|
||||
* Unfortunately - we can not handle more than CIFS_MAX_MSG_SIZE
|
||||
* (by default, note that it can be overridden to make max larger)
|
||||
* in responses (except for read responses which can be bigger.
|
||||
* We may want to bump this limit up
|
||||
*/
|
||||
req->MaxOutputResponse = cpu_to_le32(0xFF00); /* < 64K uses 1 credit */
|
||||
req->MaxOutputResponse = cpu_to_le32(CIFSMaxBufSize);
|
||||
|
||||
if (is_fsctl)
|
||||
req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL);
|
||||
|
|
Loading…
Reference in New Issue