Commit c9a2e4a8 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 6995a6a5 d46b0da7
...@@ -169,7 +169,13 @@ cifs_read_super(struct super_block *sb) ...@@ -169,7 +169,13 @@ cifs_read_super(struct super_block *sb)
else else
sb->s_maxbytes = MAX_NON_LFS; 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) && if ((tcon->ses->server->vals->protocol_id == SMB10_PROT_ID) &&
((tcon->ses->capabilities & ((tcon->ses->capabilities &
tcon->ses->server->vals->cap_nt_find) == 0) && tcon->ses->server->vals->cap_nt_find) == 0) &&
......
...@@ -1391,6 +1391,11 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file); ...@@ -1391,6 +1391,11 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
struct cifsInodeInfo { struct cifsInodeInfo {
bool can_cache_brlcks; bool can_cache_brlcks;
struct list_head llist; /* locks helb by this inode */ 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 */ struct rw_semaphore lock_sem; /* protect the fields above */
/* BB add in lists for dirty pages i.e. write caching info for oplock */ /* BB add in lists for dirty pages i.e. write caching info for oplock */
struct list_head openFileList; struct list_head openFileList;
......
...@@ -170,6 +170,7 @@ extern int cifs_unlock_range(struct cifsFileInfo *cfile, ...@@ -170,6 +170,7 @@ extern int cifs_unlock_range(struct cifsFileInfo *cfile,
struct file_lock *flock, const unsigned int xid); struct file_lock *flock, const unsigned int xid);
extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); 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, extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid,
struct file *file, struct file *file,
struct tcon_link *tlink, struct tcon_link *tlink,
......
...@@ -564,9 +564,11 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -564,9 +564,11 @@ cifs_reconnect(struct TCP_Server_Info *server)
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead); mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
kref_get(&mid_entry->refcount);
if (mid_entry->mid_state == MID_REQUEST_SUBMITTED) if (mid_entry->mid_state == MID_REQUEST_SUBMITTED)
mid_entry->mid_state = MID_RETRY_NEEDED; mid_entry->mid_state = MID_RETRY_NEEDED;
list_move(&mid_entry->qhead, &retry_list); list_move(&mid_entry->qhead, &retry_list);
mid_entry->mid_flags |= MID_DELETED;
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
mutex_unlock(&server->srv_mutex); mutex_unlock(&server->srv_mutex);
...@@ -576,6 +578,7 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -576,6 +578,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
mid_entry = list_entry(tmp, struct mid_q_entry, qhead); mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
list_del_init(&mid_entry->qhead); list_del_init(&mid_entry->qhead);
mid_entry->callback(mid_entry); mid_entry->callback(mid_entry);
cifs_mid_q_entry_release(mid_entry);
} }
if (cifs_rdma_enabled(server)) { if (cifs_rdma_enabled(server)) {
...@@ -895,8 +898,10 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed) ...@@ -895,8 +898,10 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed)
if (mid->mid_flags & MID_DELETED) if (mid->mid_flags & MID_DELETED)
printk_once(KERN_WARNING printk_once(KERN_WARNING
"trying to dequeue a deleted mid\n"); "trying to dequeue a deleted mid\n");
else else {
list_del_init(&mid->qhead); list_del_init(&mid->qhead);
mid->mid_flags |= MID_DELETED;
}
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
} }
...@@ -966,8 +971,10 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) ...@@ -966,8 +971,10 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead); mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
cifs_dbg(FYI, "Clearing mid 0x%llx\n", mid_entry->mid); cifs_dbg(FYI, "Clearing mid 0x%llx\n", mid_entry->mid);
kref_get(&mid_entry->refcount);
mid_entry->mid_state = MID_SHUTDOWN; mid_entry->mid_state = MID_SHUTDOWN;
list_move(&mid_entry->qhead, &dispose_list); list_move(&mid_entry->qhead, &dispose_list);
mid_entry->mid_flags |= MID_DELETED;
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
...@@ -977,6 +984,7 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) ...@@ -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); cifs_dbg(FYI, "Callback mid 0x%llx\n", mid_entry->mid);
list_del_init(&mid_entry->qhead); list_del_init(&mid_entry->qhead);
mid_entry->callback(mid_entry); 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 */ /* 1/8th of sec is more than enough time for them to exit */
msleep(125); msleep(125);
...@@ -3882,8 +3890,12 @@ generic_ip_connect(struct TCP_Server_Info *server) ...@@ -3882,8 +3890,12 @@ generic_ip_connect(struct TCP_Server_Info *server)
rc = socket->ops->connect(socket, saddr, slen, rc = socket->ops->connect(socket, saddr, slen,
server->noblockcnt ? O_NONBLOCK : 0); 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; rc = 0;
if (rc < 0) { if (rc < 0) {
cifs_dbg(FYI, "Error %d connecting to server\n", rc); cifs_dbg(FYI, "Error %d connecting to server\n", rc);
......
...@@ -281,6 +281,13 @@ cifs_has_mand_locks(struct cifsInodeInfo *cinode) ...@@ -281,6 +281,13 @@ cifs_has_mand_locks(struct cifsInodeInfo *cinode)
return has_locks; return has_locks;
} }
void
cifs_down_write(struct rw_semaphore *sem)
{
while (!down_write_trylock(sem))
msleep(10);
}
struct cifsFileInfo * struct cifsFileInfo *
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
struct tcon_link *tlink, __u32 oplock) struct tcon_link *tlink, __u32 oplock)
...@@ -306,7 +313,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, ...@@ -306,7 +313,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
INIT_LIST_HEAD(&fdlocks->locks); INIT_LIST_HEAD(&fdlocks->locks);
fdlocks->cfile = cfile; fdlocks->cfile = cfile;
cfile->llist = fdlocks; cfile->llist = fdlocks;
down_write(&cinode->lock_sem); cifs_down_write(&cinode->lock_sem);
list_add(&fdlocks->llist, &cinode->llist); list_add(&fdlocks->llist, &cinode->llist);
up_write(&cinode->lock_sem); up_write(&cinode->lock_sem);
...@@ -405,10 +412,11 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler) ...@@ -405,10 +412,11 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
bool oplock_break_cancelled; bool oplock_break_cancelled;
spin_lock(&tcon->open_file_lock); spin_lock(&tcon->open_file_lock);
spin_lock(&cifsi->open_file_lock);
spin_lock(&cifs_file->file_info_lock); spin_lock(&cifs_file->file_info_lock);
if (--cifs_file->count > 0) { if (--cifs_file->count > 0) {
spin_unlock(&cifs_file->file_info_lock); spin_unlock(&cifs_file->file_info_lock);
spin_unlock(&cifsi->open_file_lock);
spin_unlock(&tcon->open_file_lock); spin_unlock(&tcon->open_file_lock);
return; return;
} }
...@@ -421,9 +429,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler) ...@@ -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); cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open);
/* remove it from the lists */ /* remove it from the lists */
spin_lock(&cifsi->open_file_lock);
list_del(&cifs_file->flist); list_del(&cifs_file->flist);
spin_unlock(&cifsi->open_file_lock);
list_del(&cifs_file->tlist); list_del(&cifs_file->tlist);
atomic_dec(&tcon->num_local_opens); atomic_dec(&tcon->num_local_opens);
...@@ -440,6 +446,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler) ...@@ -440,6 +446,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
cifs_set_oplock_level(cifsi, 0); cifs_set_oplock_level(cifsi, 0);
} }
spin_unlock(&cifsi->open_file_lock);
spin_unlock(&tcon->open_file_lock); spin_unlock(&tcon->open_file_lock);
oplock_break_cancelled = wait_oplock_handler ? oplock_break_cancelled = wait_oplock_handler ?
...@@ -464,7 +471,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool 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 * Delete any outstanding lock records. We'll lose them when the file
* is closed anyway. * 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_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {
list_del(&li->llist); list_del(&li->llist);
cifs_del_lock_waiters(li); cifs_del_lock_waiters(li);
...@@ -1027,7 +1034,7 @@ static void ...@@ -1027,7 +1034,7 @@ static void
cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock) cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock)
{ {
struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); 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); list_add_tail(&lock->llist, &cfile->llist->locks);
up_write(&cinode->lock_sem); up_write(&cinode->lock_sem);
} }
...@@ -1049,7 +1056,7 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock, ...@@ -1049,7 +1056,7 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock,
try_again: try_again:
exist = false; exist = false;
down_write(&cinode->lock_sem); cifs_down_write(&cinode->lock_sem);
exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length, exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length,
lock->type, lock->flags, &conf_lock, lock->type, lock->flags, &conf_lock,
...@@ -1072,7 +1079,7 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock, ...@@ -1072,7 +1079,7 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock,
(lock->blist.next == &lock->blist)); (lock->blist.next == &lock->blist));
if (!rc) if (!rc)
goto try_again; goto try_again;
down_write(&cinode->lock_sem); cifs_down_write(&cinode->lock_sem);
list_del_init(&lock->blist); list_del_init(&lock->blist);
} }
...@@ -1125,7 +1132,7 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock) ...@@ -1125,7 +1132,7 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock)
return rc; return rc;
try_again: try_again:
down_write(&cinode->lock_sem); cifs_down_write(&cinode->lock_sem);
if (!cinode->can_cache_brlcks) { if (!cinode->can_cache_brlcks) {
up_write(&cinode->lock_sem); up_write(&cinode->lock_sem);
return rc; return rc;
...@@ -1331,7 +1338,7 @@ cifs_push_locks(struct cifsFileInfo *cfile) ...@@ -1331,7 +1338,7 @@ cifs_push_locks(struct cifsFileInfo *cfile)
int rc = 0; int rc = 0;
/* we are going to update can_cache_brlcks here - need a write access */ /* 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) { if (!cinode->can_cache_brlcks) {
up_write(&cinode->lock_sem); up_write(&cinode->lock_sem);
return rc; return rc;
...@@ -1522,7 +1529,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, ...@@ -1522,7 +1529,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
down_write(&cinode->lock_sem); cifs_down_write(&cinode->lock_sem);
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
cur = buf; cur = buf;
num = 0; num = 0;
......
...@@ -2475,9 +2475,9 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) ...@@ -2475,9 +2475,9 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
rc = tcon->ses->server->ops->flush(xid, tcon, &wfile->fid); rc = tcon->ses->server->ops->flush(xid, tcon, &wfile->fid);
cifsFileInfo_put(wfile); cifsFileInfo_put(wfile);
if (rc) if (rc)
return rc; goto cifs_setattr_exit;
} else if (rc != -EBADF) } else if (rc != -EBADF)
return rc; goto cifs_setattr_exit;
else else
rc = 0; rc = 0;
} }
......
...@@ -171,6 +171,9 @@ cifs_get_next_mid(struct TCP_Server_Info *server) ...@@ -171,6 +171,9 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
/* we do not want to loop forever */ /* we do not want to loop forever */
last_mid = cur_mid; last_mid = cur_mid;
cur_mid++; cur_mid++;
/* avoid 0xFFFF MID */
if (cur_mid == 0xffff)
cur_mid++;
/* /*
* This nested loop looks more expensive than it is. * This nested loop looks more expensive than it is.
......
...@@ -145,7 +145,7 @@ smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, ...@@ -145,7 +145,7 @@ smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
cur = buf; cur = buf;
down_write(&cinode->lock_sem); cifs_down_write(&cinode->lock_sem);
list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
if (flock->fl_start > li->offset || if (flock->fl_start > li->offset ||
(flock->fl_start + length) < (flock->fl_start + length) <
......
...@@ -86,22 +86,8 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) ...@@ -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) static void _cifs_mid_q_entry_release(struct kref *refcount)
{ {
struct mid_q_entry *mid = container_of(refcount, struct mid_q_entry, struct mid_q_entry *midEntry =
refcount); 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)
{
#ifdef CONFIG_CIFS_STATS2 #ifdef CONFIG_CIFS_STATS2
__le16 command = midEntry->server->vals->lock_cmd; __le16 command = midEntry->server->vals->lock_cmd;
__u16 smb_cmd = le16_to_cpu(midEntry->command); __u16 smb_cmd = le16_to_cpu(midEntry->command);
...@@ -166,6 +152,19 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) ...@@ -166,6 +152,19 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
} }
} }
#endif #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); cifs_mid_q_entry_release(midEntry);
} }
...@@ -173,8 +172,10 @@ void ...@@ -173,8 +172,10 @@ void
cifs_delete_mid(struct mid_q_entry *mid) cifs_delete_mid(struct mid_q_entry *mid)
{ {
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
list_del_init(&mid->qhead); if (!(mid->mid_flags & MID_DELETED)) {
mid->mid_flags |= MID_DELETED; list_del_init(&mid->qhead);
mid->mid_flags |= MID_DELETED;
}
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
DeleteMidQEntry(mid); DeleteMidQEntry(mid);
...@@ -872,7 +873,10 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) ...@@ -872,7 +873,10 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
rc = -EHOSTDOWN; rc = -EHOSTDOWN;
break; break;
default: 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", cifs_server_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n",
__func__, mid->mid, mid->mid_state); __func__, mid->mid, mid->mid_state);
rc = -EIO; rc = -EIO;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment