forked from Minki/linux
Seven cifs/smb3 fixes, including three for stable
-----BEGIN PGP SIGNATURE----- iQGyBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAl208asACgkQiiy9cAdy T1EVJAv41lwLL01FQtWsoMB8yn46q7iboc0eqVJ4CEnZj25Fe3VgokKm1+2N6/15 g8tQeLnE8kzjQyAxEgI260PHRF6Dg0RSL7zwxnJOFpspWLb9+U4UEryCDao/v4Ao LYr88F6XeYDGojyuGKokT/jlsxWfdPwaYhpOPabjO76f6Jv7cNfIa9EKzbEAtq3e a9to2AVnJ02FLGTP3QM1ThBtCu3JFFMI07u2Rp8ANiJYtM5Mg2piOkO1PQ3hGLIM /3+1E7B12KgG7D3GA7ZuP1HLjyo+A91BhZVTzlBDq1VHTd8ccJDJklg28oXoMrMh dgW5XRcS/s60LsRzagpNMC0cPlAdBAd9N9uZaoXmQhNAg0I7CJ24H3tglwKejoUR m/duI1C89Fbo1kgjwfAztxhfvair2SYLu8DGijvYH/C5syNUR7V99uy1th+nmOgl vpBM9CbkM6mtSxFHtgcFuv4rwIf61EQaGoXiL/e3RLH4zc/HaUpBo2rDh0LPmwFB koXIw6E= =fTOk -----END PGP SIGNATURE----- Merge tag '5.4-rc5-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6 Pull cifs fixes from Steve French: "Seven cifs/smb3 fixes, including three for stable" * tag '5.4-rc5-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6: cifs: Fix cifsInodeInfo lock_sem deadlock when reconnect occurs CIFS: Fix use after free of file info structures CIFS: Fix retry mid list corruption on reconnects cifs: Fix missed free operations CIFS: avoid using MID 0xFFFF cifs: clarify comment about timestamp granularity for old servers cifs: Handle -EINPROGRESS only when noblockcnt is set
This commit is contained in:
commit
c9a2e4a829
@ -169,7 +169,13 @@ cifs_read_super(struct super_block *sb)
|
||||
else
|
||||
sb->s_maxbytes = MAX_NON_LFS;
|
||||
|
||||
/* Some very old servers like DOS and OS/2 used 2 second granularity */
|
||||
/*
|
||||
* Some very old servers like DOS and OS/2 used 2 second granularity
|
||||
* (while all current servers use 100ns granularity - see MS-DTYP)
|
||||
* but 1 second is the maximum allowed granularity for the VFS
|
||||
* so for old servers set time granularity to 1 second while for
|
||||
* everything else (current servers) set it to 100ns.
|
||||
*/
|
||||
if ((tcon->ses->server->vals->protocol_id == SMB10_PROT_ID) &&
|
||||
((tcon->ses->capabilities &
|
||||
tcon->ses->server->vals->cap_nt_find) == 0) &&
|
||||
|
@ -1391,6 +1391,11 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
|
||||
struct cifsInodeInfo {
|
||||
bool can_cache_brlcks;
|
||||
struct list_head llist; /* locks helb by this inode */
|
||||
/*
|
||||
* NOTE: Some code paths call down_read(lock_sem) twice, so
|
||||
* we must always use use cifs_down_write() instead of down_write()
|
||||
* for this semaphore to avoid deadlocks.
|
||||
*/
|
||||
struct rw_semaphore lock_sem; /* protect the fields above */
|
||||
/* BB add in lists for dirty pages i.e. write caching info for oplock */
|
||||
struct list_head openFileList;
|
||||
|
@ -170,6 +170,7 @@ extern int cifs_unlock_range(struct cifsFileInfo *cfile,
|
||||
struct file_lock *flock, const unsigned int xid);
|
||||
extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile);
|
||||
|
||||
extern void cifs_down_write(struct rw_semaphore *sem);
|
||||
extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid,
|
||||
struct file *file,
|
||||
struct tcon_link *tlink,
|
||||
|
@ -564,9 +564,11 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
|
||||
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
|
||||
kref_get(&mid_entry->refcount);
|
||||
if (mid_entry->mid_state == MID_REQUEST_SUBMITTED)
|
||||
mid_entry->mid_state = MID_RETRY_NEEDED;
|
||||
list_move(&mid_entry->qhead, &retry_list);
|
||||
mid_entry->mid_flags |= MID_DELETED;
|
||||
}
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
@ -576,6 +578,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
||||
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
|
||||
list_del_init(&mid_entry->qhead);
|
||||
mid_entry->callback(mid_entry);
|
||||
cifs_mid_q_entry_release(mid_entry);
|
||||
}
|
||||
|
||||
if (cifs_rdma_enabled(server)) {
|
||||
@ -895,8 +898,10 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed)
|
||||
if (mid->mid_flags & MID_DELETED)
|
||||
printk_once(KERN_WARNING
|
||||
"trying to dequeue a deleted mid\n");
|
||||
else
|
||||
else {
|
||||
list_del_init(&mid->qhead);
|
||||
mid->mid_flags |= MID_DELETED;
|
||||
}
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
|
||||
@ -966,8 +971,10 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
|
||||
list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
|
||||
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
|
||||
cifs_dbg(FYI, "Clearing mid 0x%llx\n", mid_entry->mid);
|
||||
kref_get(&mid_entry->refcount);
|
||||
mid_entry->mid_state = MID_SHUTDOWN;
|
||||
list_move(&mid_entry->qhead, &dispose_list);
|
||||
mid_entry->mid_flags |= MID_DELETED;
|
||||
}
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
|
||||
@ -977,6 +984,7 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
|
||||
cifs_dbg(FYI, "Callback mid 0x%llx\n", mid_entry->mid);
|
||||
list_del_init(&mid_entry->qhead);
|
||||
mid_entry->callback(mid_entry);
|
||||
cifs_mid_q_entry_release(mid_entry);
|
||||
}
|
||||
/* 1/8th of sec is more than enough time for them to exit */
|
||||
msleep(125);
|
||||
@ -3882,8 +3890,12 @@ generic_ip_connect(struct TCP_Server_Info *server)
|
||||
|
||||
rc = socket->ops->connect(socket, saddr, slen,
|
||||
server->noblockcnt ? O_NONBLOCK : 0);
|
||||
|
||||
if (rc == -EINPROGRESS)
|
||||
/*
|
||||
* When mounting SMB root file systems, we do not want to block in
|
||||
* connect. Otherwise bail out and then let cifs_reconnect() perform
|
||||
* reconnect failover - if possible.
|
||||
*/
|
||||
if (server->noblockcnt && rc == -EINPROGRESS)
|
||||
rc = 0;
|
||||
if (rc < 0) {
|
||||
cifs_dbg(FYI, "Error %d connecting to server\n", rc);
|
||||
|
@ -281,6 +281,13 @@ cifs_has_mand_locks(struct cifsInodeInfo *cinode)
|
||||
return has_locks;
|
||||
}
|
||||
|
||||
void
|
||||
cifs_down_write(struct rw_semaphore *sem)
|
||||
{
|
||||
while (!down_write_trylock(sem))
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
struct cifsFileInfo *
|
||||
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||
struct tcon_link *tlink, __u32 oplock)
|
||||
@ -306,7 +313,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||
INIT_LIST_HEAD(&fdlocks->locks);
|
||||
fdlocks->cfile = cfile;
|
||||
cfile->llist = fdlocks;
|
||||
down_write(&cinode->lock_sem);
|
||||
cifs_down_write(&cinode->lock_sem);
|
||||
list_add(&fdlocks->llist, &cinode->llist);
|
||||
up_write(&cinode->lock_sem);
|
||||
|
||||
@ -405,10 +412,11 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
|
||||
bool oplock_break_cancelled;
|
||||
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
|
||||
spin_lock(&cifsi->open_file_lock);
|
||||
spin_lock(&cifs_file->file_info_lock);
|
||||
if (--cifs_file->count > 0) {
|
||||
spin_unlock(&cifs_file->file_info_lock);
|
||||
spin_unlock(&cifsi->open_file_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
return;
|
||||
}
|
||||
@ -421,9 +429,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
|
||||
cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open);
|
||||
|
||||
/* remove it from the lists */
|
||||
spin_lock(&cifsi->open_file_lock);
|
||||
list_del(&cifs_file->flist);
|
||||
spin_unlock(&cifsi->open_file_lock);
|
||||
list_del(&cifs_file->tlist);
|
||||
atomic_dec(&tcon->num_local_opens);
|
||||
|
||||
@ -440,6 +446,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
|
||||
cifs_set_oplock_level(cifsi, 0);
|
||||
}
|
||||
|
||||
spin_unlock(&cifsi->open_file_lock);
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
|
||||
oplock_break_cancelled = wait_oplock_handler ?
|
||||
@ -464,7 +471,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
|
||||
* Delete any outstanding lock records. We'll lose them when the file
|
||||
* is closed anyway.
|
||||
*/
|
||||
down_write(&cifsi->lock_sem);
|
||||
cifs_down_write(&cifsi->lock_sem);
|
||||
list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {
|
||||
list_del(&li->llist);
|
||||
cifs_del_lock_waiters(li);
|
||||
@ -1027,7 +1034,7 @@ static void
|
||||
cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock)
|
||||
{
|
||||
struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
|
||||
down_write(&cinode->lock_sem);
|
||||
cifs_down_write(&cinode->lock_sem);
|
||||
list_add_tail(&lock->llist, &cfile->llist->locks);
|
||||
up_write(&cinode->lock_sem);
|
||||
}
|
||||
@ -1049,7 +1056,7 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock,
|
||||
|
||||
try_again:
|
||||
exist = false;
|
||||
down_write(&cinode->lock_sem);
|
||||
cifs_down_write(&cinode->lock_sem);
|
||||
|
||||
exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length,
|
||||
lock->type, lock->flags, &conf_lock,
|
||||
@ -1072,7 +1079,7 @@ try_again:
|
||||
(lock->blist.next == &lock->blist));
|
||||
if (!rc)
|
||||
goto try_again;
|
||||
down_write(&cinode->lock_sem);
|
||||
cifs_down_write(&cinode->lock_sem);
|
||||
list_del_init(&lock->blist);
|
||||
}
|
||||
|
||||
@ -1125,7 +1132,7 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock)
|
||||
return rc;
|
||||
|
||||
try_again:
|
||||
down_write(&cinode->lock_sem);
|
||||
cifs_down_write(&cinode->lock_sem);
|
||||
if (!cinode->can_cache_brlcks) {
|
||||
up_write(&cinode->lock_sem);
|
||||
return rc;
|
||||
@ -1331,7 +1338,7 @@ cifs_push_locks(struct cifsFileInfo *cfile)
|
||||
int rc = 0;
|
||||
|
||||
/* we are going to update can_cache_brlcks here - need a write access */
|
||||
down_write(&cinode->lock_sem);
|
||||
cifs_down_write(&cinode->lock_sem);
|
||||
if (!cinode->can_cache_brlcks) {
|
||||
up_write(&cinode->lock_sem);
|
||||
return rc;
|
||||
@ -1522,7 +1529,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
down_write(&cinode->lock_sem);
|
||||
cifs_down_write(&cinode->lock_sem);
|
||||
for (i = 0; i < 2; i++) {
|
||||
cur = buf;
|
||||
num = 0;
|
||||
|
@ -2475,9 +2475,9 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
|
||||
rc = tcon->ses->server->ops->flush(xid, tcon, &wfile->fid);
|
||||
cifsFileInfo_put(wfile);
|
||||
if (rc)
|
||||
return rc;
|
||||
goto cifs_setattr_exit;
|
||||
} else if (rc != -EBADF)
|
||||
return rc;
|
||||
goto cifs_setattr_exit;
|
||||
else
|
||||
rc = 0;
|
||||
}
|
||||
|
@ -171,6 +171,9 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
|
||||
/* we do not want to loop forever */
|
||||
last_mid = cur_mid;
|
||||
cur_mid++;
|
||||
/* avoid 0xFFFF MID */
|
||||
if (cur_mid == 0xffff)
|
||||
cur_mid++;
|
||||
|
||||
/*
|
||||
* This nested loop looks more expensive than it is.
|
||||
|
@ -145,7 +145,7 @@ smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
|
||||
|
||||
cur = buf;
|
||||
|
||||
down_write(&cinode->lock_sem);
|
||||
cifs_down_write(&cinode->lock_sem);
|
||||
list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
|
||||
if (flock->fl_start > li->offset ||
|
||||
(flock->fl_start + length) <
|
||||
|
@ -86,22 +86,8 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
|
||||
|
||||
static void _cifs_mid_q_entry_release(struct kref *refcount)
|
||||
{
|
||||
struct mid_q_entry *mid = container_of(refcount, struct mid_q_entry,
|
||||
refcount);
|
||||
|
||||
mempool_free(mid, cifs_mid_poolp);
|
||||
}
|
||||
|
||||
void cifs_mid_q_entry_release(struct mid_q_entry *midEntry)
|
||||
{
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
kref_put(&midEntry->refcount, _cifs_mid_q_entry_release);
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
|
||||
void
|
||||
DeleteMidQEntry(struct mid_q_entry *midEntry)
|
||||
{
|
||||
struct mid_q_entry *midEntry =
|
||||
container_of(refcount, struct mid_q_entry, refcount);
|
||||
#ifdef CONFIG_CIFS_STATS2
|
||||
__le16 command = midEntry->server->vals->lock_cmd;
|
||||
__u16 smb_cmd = le16_to_cpu(midEntry->command);
|
||||
@ -166,6 +152,19 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
mempool_free(midEntry, cifs_mid_poolp);
|
||||
}
|
||||
|
||||
void cifs_mid_q_entry_release(struct mid_q_entry *midEntry)
|
||||
{
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
kref_put(&midEntry->refcount, _cifs_mid_q_entry_release);
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
|
||||
void DeleteMidQEntry(struct mid_q_entry *midEntry)
|
||||
{
|
||||
cifs_mid_q_entry_release(midEntry);
|
||||
}
|
||||
|
||||
@ -173,8 +172,10 @@ void
|
||||
cifs_delete_mid(struct mid_q_entry *mid)
|
||||
{
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
list_del_init(&mid->qhead);
|
||||
mid->mid_flags |= MID_DELETED;
|
||||
if (!(mid->mid_flags & MID_DELETED)) {
|
||||
list_del_init(&mid->qhead);
|
||||
mid->mid_flags |= MID_DELETED;
|
||||
}
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
|
||||
DeleteMidQEntry(mid);
|
||||
@ -872,7 +873,10 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
|
||||
rc = -EHOSTDOWN;
|
||||
break;
|
||||
default:
|
||||
list_del_init(&mid->qhead);
|
||||
if (!(mid->mid_flags & MID_DELETED)) {
|
||||
list_del_init(&mid->qhead);
|
||||
mid->mid_flags |= MID_DELETED;
|
||||
}
|
||||
cifs_server_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n",
|
||||
__func__, mid->mid, mid->mid_state);
|
||||
rc = -EIO;
|
||||
|
Loading…
Reference in New Issue
Block a user