diff options
author | Steve French <smfrench@gmail.com> | 2016-09-22 19:58:16 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2016-10-12 13:08:32 -0400 |
commit | 3afca265b5f53a0b15b79531c13858049505582d (patch) | |
tree | ac9c7c97ee38fb4c156731643120833c5e53f63c | |
parent | d171356ff11ab1825e456dfb979755e01b3c54a1 (diff) |
Clarify locking of cifs file and tcon structures and make more granular
Remove the global file_list_lock to simplify cifs/smb3 locking and
have spinlocks that more closely match the information they are
protecting.
Add new tcon->open_file_lock and file->file_info_lock spinlocks.
Locks continue to follow a heirachy,
cifs_socket --> cifs_ses --> cifs_tcon --> cifs_file
where global tcp_ses_lock still protects socket and cifs_ses, while the
the newer locks protect the lower level structure's information
(tcon and cifs_file respectively).
CC: Stable <stable@vger.kernel.org>
Signed-off-by: Steve French <steve.french@primarydata.com>
Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Reviewed-by: Germano Percossi <germano.percossi@citrix.com>
-rw-r--r-- | fs/cifs/cifsfs.c | 1 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 30 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 4 | ||||
-rw-r--r-- | fs/cifs/file.c | 66 | ||||
-rw-r--r-- | fs/cifs/misc.c | 15 | ||||
-rw-r--r-- | fs/cifs/readdir.c | 6 | ||||
-rw-r--r-- | fs/cifs/smb2misc.c | 16 |
7 files changed, 75 insertions, 63 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 978dbf049a77..d2b2703fed7d 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -1262,7 +1262,6 @@ init_cifs(void) | |||
1262 | GlobalTotalActiveXid = 0; | 1262 | GlobalTotalActiveXid = 0; |
1263 | GlobalMaxActiveXid = 0; | 1263 | GlobalMaxActiveXid = 0; |
1264 | spin_lock_init(&cifs_tcp_ses_lock); | 1264 | spin_lock_init(&cifs_tcp_ses_lock); |
1265 | spin_lock_init(&cifs_file_list_lock); | ||
1266 | spin_lock_init(&GlobalMid_Lock); | 1265 | spin_lock_init(&GlobalMid_Lock); |
1267 | 1266 | ||
1268 | get_random_bytes(&cifs_lock_secret, sizeof(cifs_lock_secret)); | 1267 | get_random_bytes(&cifs_lock_secret, sizeof(cifs_lock_secret)); |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 8f1d8c1e72be..65f78b7a9062 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -833,6 +833,7 @@ struct cifs_tcon { | |||
833 | struct list_head tcon_list; | 833 | struct list_head tcon_list; |
834 | int tc_count; | 834 | int tc_count; |
835 | struct list_head openFileList; | 835 | struct list_head openFileList; |
836 | spinlock_t open_file_lock; /* protects list above */ | ||
836 | struct cifs_ses *ses; /* pointer to session associated with */ | 837 | struct cifs_ses *ses; /* pointer to session associated with */ |
837 | char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ | 838 | char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ |
838 | char *nativeFileSystem; | 839 | char *nativeFileSystem; |
@@ -889,7 +890,7 @@ struct cifs_tcon { | |||
889 | #endif /* CONFIG_CIFS_STATS2 */ | 890 | #endif /* CONFIG_CIFS_STATS2 */ |
890 | __u64 bytes_read; | 891 | __u64 bytes_read; |
891 | __u64 bytes_written; | 892 | __u64 bytes_written; |
892 | spinlock_t stat_lock; | 893 | spinlock_t stat_lock; /* protects the two fields above */ |
893 | #endif /* CONFIG_CIFS_STATS */ | 894 | #endif /* CONFIG_CIFS_STATS */ |
894 | FILE_SYSTEM_DEVICE_INFO fsDevInfo; | 895 | FILE_SYSTEM_DEVICE_INFO fsDevInfo; |
895 | FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */ | 896 | FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */ |
@@ -1040,8 +1041,10 @@ struct cifs_fid_locks { | |||
1040 | }; | 1041 | }; |
1041 | 1042 | ||
1042 | struct cifsFileInfo { | 1043 | struct cifsFileInfo { |
1044 | /* following two lists are protected by tcon->open_file_lock */ | ||
1043 | struct list_head tlist; /* pointer to next fid owned by tcon */ | 1045 | struct list_head tlist; /* pointer to next fid owned by tcon */ |
1044 | struct list_head flist; /* next fid (file instance) for this inode */ | 1046 | struct list_head flist; /* next fid (file instance) for this inode */ |
1047 | /* lock list below protected by cifsi->lock_sem */ | ||
1045 | struct cifs_fid_locks *llist; /* brlocks held by this fid */ | 1048 | struct cifs_fid_locks *llist; /* brlocks held by this fid */ |
1046 | kuid_t uid; /* allows finding which FileInfo structure */ | 1049 | kuid_t uid; /* allows finding which FileInfo structure */ |
1047 | __u32 pid; /* process id who opened file */ | 1050 | __u32 pid; /* process id who opened file */ |
@@ -1049,11 +1052,12 @@ struct cifsFileInfo { | |||
1049 | /* BB add lock scope info here if needed */ ; | 1052 | /* BB add lock scope info here if needed */ ; |
1050 | /* lock scope id (0 if none) */ | 1053 | /* lock scope id (0 if none) */ |
1051 | struct dentry *dentry; | 1054 | struct dentry *dentry; |
1052 | unsigned int f_flags; | ||
1053 | struct tcon_link *tlink; | 1055 | struct tcon_link *tlink; |
1056 | unsigned int f_flags; | ||
1054 | bool invalidHandle:1; /* file closed via session abend */ | 1057 | bool invalidHandle:1; /* file closed via session abend */ |
1055 | bool oplock_break_cancelled:1; | 1058 | bool oplock_break_cancelled:1; |
1056 | int count; /* refcount protected by cifs_file_list_lock */ | 1059 | int count; |
1060 | spinlock_t file_info_lock; /* protects four flag/count fields above */ | ||
1057 | struct mutex fh_mutex; /* prevents reopen race after dead ses*/ | 1061 | struct mutex fh_mutex; /* prevents reopen race after dead ses*/ |
1058 | struct cifs_search_info srch_inf; | 1062 | struct cifs_search_info srch_inf; |
1059 | struct work_struct oplock_break; /* work for oplock breaks */ | 1063 | struct work_struct oplock_break; /* work for oplock breaks */ |
@@ -1120,7 +1124,7 @@ struct cifs_writedata { | |||
1120 | 1124 | ||
1121 | /* | 1125 | /* |
1122 | * Take a reference on the file private data. Must be called with | 1126 | * Take a reference on the file private data. Must be called with |
1123 | * cifs_file_list_lock held. | 1127 | * cfile->file_info_lock held. |
1124 | */ | 1128 | */ |
1125 | static inline void | 1129 | static inline void |
1126 | cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) | 1130 | cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) |
@@ -1514,8 +1518,10 @@ require use of the stronger protocol */ | |||
1514 | * GlobalMid_Lock protects: | 1518 | * GlobalMid_Lock protects: |
1515 | * list operations on pending_mid_q and oplockQ | 1519 | * list operations on pending_mid_q and oplockQ |
1516 | * updates to XID counters, multiplex id and SMB sequence numbers | 1520 | * updates to XID counters, multiplex id and SMB sequence numbers |
1517 | * cifs_file_list_lock protects: | 1521 | * tcp_ses_lock protects: |
1518 | * list operations on tcp and SMB session lists and tCon lists | 1522 | * list operations on tcp and SMB session lists |
1523 | * tcon->open_file_lock protects the list of open files hanging off the tcon | ||
1524 | * cfile->file_info_lock protects counters and fields in cifs file struct | ||
1519 | * f_owner.lock protects certain per file struct operations | 1525 | * f_owner.lock protects certain per file struct operations |
1520 | * mapping->page_lock protects certain per page operations | 1526 | * mapping->page_lock protects certain per page operations |
1521 | * | 1527 | * |
@@ -1547,18 +1553,12 @@ GLOBAL_EXTERN struct list_head cifs_tcp_ses_list; | |||
1547 | * tcp session, and the list of tcon's per smb session. It also protects | 1553 | * tcp session, and the list of tcon's per smb session. It also protects |
1548 | * the reference counters for the server, smb session, and tcon. Finally, | 1554 | * the reference counters for the server, smb session, and tcon. Finally, |
1549 | * changes to the tcon->tidStatus should be done while holding this lock. | 1555 | * changes to the tcon->tidStatus should be done while holding this lock. |
1556 | * generally the locks should be taken in order tcp_ses_lock before | ||
1557 | * tcon->open_file_lock and that before file->file_info_lock since the | ||
1558 | * structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file | ||
1550 | */ | 1559 | */ |
1551 | GLOBAL_EXTERN spinlock_t cifs_tcp_ses_lock; | 1560 | GLOBAL_EXTERN spinlock_t cifs_tcp_ses_lock; |
1552 | 1561 | ||
1553 | /* | ||
1554 | * This lock protects the cifs_file->llist and cifs_file->flist | ||
1555 | * list operations, and updates to some flags (cifs_file->invalidHandle) | ||
1556 | * It will be moved to either use the tcon->stat_lock or equivalent later. | ||
1557 | * If cifs_tcp_ses_lock and the lock below are both needed to be held, then | ||
1558 | * the cifs_tcp_ses_lock must be grabbed first and released last. | ||
1559 | */ | ||
1560 | GLOBAL_EXTERN spinlock_t cifs_file_list_lock; | ||
1561 | |||
1562 | #ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */ | 1562 | #ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */ |
1563 | /* Outstanding dir notify requests */ | 1563 | /* Outstanding dir notify requests */ |
1564 | GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; | 1564 | GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; |
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f82d2823622f..3f3185febc58 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c | |||
@@ -98,13 +98,13 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon) | |||
98 | struct list_head *tmp1; | 98 | struct list_head *tmp1; |
99 | 99 | ||
100 | /* list all files open on tree connection and mark them invalid */ | 100 | /* list all files open on tree connection and mark them invalid */ |
101 | spin_lock(&cifs_file_list_lock); | 101 | spin_lock(&tcon->open_file_lock); |
102 | list_for_each_safe(tmp, tmp1, &tcon->openFileList) { | 102 | list_for_each_safe(tmp, tmp1, &tcon->openFileList) { |
103 | open_file = list_entry(tmp, struct cifsFileInfo, tlist); | 103 | open_file = list_entry(tmp, struct cifsFileInfo, tlist); |
104 | open_file->invalidHandle = true; | 104 | open_file->invalidHandle = true; |
105 | open_file->oplock_break_cancelled = true; | 105 | open_file->oplock_break_cancelled = true; |
106 | } | 106 | } |
107 | spin_unlock(&cifs_file_list_lock); | 107 | spin_unlock(&tcon->open_file_lock); |
108 | /* | 108 | /* |
109 | * BB Add call to invalidate_inodes(sb) for all superblocks mounted | 109 | * BB Add call to invalidate_inodes(sb) for all superblocks mounted |
110 | * to this tcon. | 110 | * to this tcon. |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index a95fe8b1afe9..ee5ceae22411 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -305,6 +305,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | |||
305 | cfile->tlink = cifs_get_tlink(tlink); | 305 | cfile->tlink = cifs_get_tlink(tlink); |
306 | INIT_WORK(&cfile->oplock_break, cifs_oplock_break); | 306 | INIT_WORK(&cfile->oplock_break, cifs_oplock_break); |
307 | mutex_init(&cfile->fh_mutex); | 307 | mutex_init(&cfile->fh_mutex); |
308 | spin_lock_init(&cfile->file_info_lock); | ||
308 | 309 | ||
309 | cifs_sb_active(inode->i_sb); | 310 | cifs_sb_active(inode->i_sb); |
310 | 311 | ||
@@ -317,7 +318,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | |||
317 | oplock = 0; | 318 | oplock = 0; |
318 | } | 319 | } |
319 | 320 | ||
320 | spin_lock(&cifs_file_list_lock); | 321 | spin_lock(&tcon->open_file_lock); |
321 | if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock) | 322 | if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock) |
322 | oplock = fid->pending_open->oplock; | 323 | oplock = fid->pending_open->oplock; |
323 | list_del(&fid->pending_open->olist); | 324 | list_del(&fid->pending_open->olist); |
@@ -326,12 +327,13 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | |||
326 | server->ops->set_fid(cfile, fid, oplock); | 327 | server->ops->set_fid(cfile, fid, oplock); |
327 | 328 | ||
328 | list_add(&cfile->tlist, &tcon->openFileList); | 329 | list_add(&cfile->tlist, &tcon->openFileList); |
330 | |||
329 | /* if readable file instance put first in list*/ | 331 | /* if readable file instance put first in list*/ |
330 | if (file->f_mode & FMODE_READ) | 332 | if (file->f_mode & FMODE_READ) |
331 | list_add(&cfile->flist, &cinode->openFileList); | 333 | list_add(&cfile->flist, &cinode->openFileList); |
332 | else | 334 | else |
333 | list_add_tail(&cfile->flist, &cinode->openFileList); | 335 | list_add_tail(&cfile->flist, &cinode->openFileList); |
334 | spin_unlock(&cifs_file_list_lock); | 336 | spin_unlock(&tcon->open_file_lock); |
335 | 337 | ||
336 | if (fid->purge_cache) | 338 | if (fid->purge_cache) |
337 | cifs_zap_mapping(inode); | 339 | cifs_zap_mapping(inode); |
@@ -343,16 +345,16 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | |||
343 | struct cifsFileInfo * | 345 | struct cifsFileInfo * |
344 | cifsFileInfo_get(struct cifsFileInfo *cifs_file) | 346 | cifsFileInfo_get(struct cifsFileInfo *cifs_file) |
345 | { | 347 | { |
346 | spin_lock(&cifs_file_list_lock); | 348 | spin_lock(&cifs_file->file_info_lock); |
347 | cifsFileInfo_get_locked(cifs_file); | 349 | cifsFileInfo_get_locked(cifs_file); |
348 | spin_unlock(&cifs_file_list_lock); | 350 | spin_unlock(&cifs_file->file_info_lock); |
349 | return cifs_file; | 351 | return cifs_file; |
350 | } | 352 | } |
351 | 353 | ||
352 | /* | 354 | /* |
353 | * Release a reference on the file private data. This may involve closing | 355 | * Release a reference on the file private data. This may involve closing |
354 | * the filehandle out on the server. Must be called without holding | 356 | * the filehandle out on the server. Must be called without holding |
355 | * cifs_file_list_lock. | 357 | * tcon->open_file_lock and cifs_file->file_info_lock. |
356 | */ | 358 | */ |
357 | void cifsFileInfo_put(struct cifsFileInfo *cifs_file) | 359 | void cifsFileInfo_put(struct cifsFileInfo *cifs_file) |
358 | { | 360 | { |
@@ -367,11 +369,15 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) | |||
367 | struct cifs_pending_open open; | 369 | struct cifs_pending_open open; |
368 | bool oplock_break_cancelled; | 370 | bool oplock_break_cancelled; |
369 | 371 | ||
370 | spin_lock(&cifs_file_list_lock); | 372 | spin_lock(&tcon->open_file_lock); |
373 | |||
374 | spin_lock(&cifs_file->file_info_lock); | ||
371 | if (--cifs_file->count > 0) { | 375 | if (--cifs_file->count > 0) { |
372 | spin_unlock(&cifs_file_list_lock); | 376 | spin_unlock(&cifs_file->file_info_lock); |
377 | spin_unlock(&tcon->open_file_lock); | ||
373 | return; | 378 | return; |
374 | } | 379 | } |
380 | spin_unlock(&cifs_file->file_info_lock); | ||
375 | 381 | ||
376 | if (server->ops->get_lease_key) | 382 | if (server->ops->get_lease_key) |
377 | server->ops->get_lease_key(inode, &fid); | 383 | server->ops->get_lease_key(inode, &fid); |
@@ -395,7 +401,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) | |||
395 | set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags); | 401 | set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags); |
396 | cifs_set_oplock_level(cifsi, 0); | 402 | cifs_set_oplock_level(cifsi, 0); |
397 | } | 403 | } |
398 | spin_unlock(&cifs_file_list_lock); | 404 | |
405 | spin_unlock(&tcon->open_file_lock); | ||
399 | 406 | ||
400 | oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break); | 407 | oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break); |
401 | 408 | ||
@@ -772,10 +779,10 @@ int cifs_closedir(struct inode *inode, struct file *file) | |||
772 | server = tcon->ses->server; | 779 | server = tcon->ses->server; |
773 | 780 | ||
774 | cifs_dbg(FYI, "Freeing private data in close dir\n"); | 781 | cifs_dbg(FYI, "Freeing private data in close dir\n"); |
775 | spin_lock(&cifs_file_list_lock); | 782 | spin_lock(&cfile->file_info_lock); |
776 | if (server->ops->dir_needs_close(cfile)) { | 783 | if (server->ops->dir_needs_close(cfile)) { |
777 | cfile->invalidHandle = true; | 784 | cfile->invalidHandle = true; |
778 | spin_unlock(&cifs_file_list_lock); | 785 | spin_unlock(&cfile->file_info_lock); |
779 | if (server->ops->close_dir) | 786 | if (server->ops->close_dir) |
780 | rc = server->ops->close_dir(xid, tcon, &cfile->fid); | 787 | rc = server->ops->close_dir(xid, tcon, &cfile->fid); |
781 | else | 788 | else |
@@ -784,7 +791,7 @@ int cifs_closedir(struct inode *inode, struct file *file) | |||
784 | /* not much we can do if it fails anyway, ignore rc */ | 791 | /* not much we can do if it fails anyway, ignore rc */ |
785 | rc = 0; | 792 | rc = 0; |
786 | } else | 793 | } else |
787 | spin_unlock(&cifs_file_list_lock); | 794 | spin_unlock(&cfile->file_info_lock); |
788 | 795 | ||
789 | buf = cfile->srch_inf.ntwrk_buf_start; | 796 | buf = cfile->srch_inf.ntwrk_buf_start; |
790 | if (buf) { | 797 | if (buf) { |
@@ -1728,12 +1735,13 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, | |||
1728 | { | 1735 | { |
1729 | struct cifsFileInfo *open_file = NULL; | 1736 | struct cifsFileInfo *open_file = NULL; |
1730 | struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); | 1737 | struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); |
1738 | struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); | ||
1731 | 1739 | ||
1732 | /* only filter by fsuid on multiuser mounts */ | 1740 | /* only filter by fsuid on multiuser mounts */ |
1733 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) | 1741 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) |
1734 | fsuid_only = false; | 1742 | fsuid_only = false; |
1735 | 1743 | ||
1736 | spin_lock(&cifs_file_list_lock); | 1744 | spin_lock(&tcon->open_file_lock); |
1737 | /* we could simply get the first_list_entry since write-only entries | 1745 | /* we could simply get the first_list_entry since write-only entries |
1738 | are always at the end of the list but since the first entry might | 1746 | are always at the end of the list but since the first entry might |
1739 | have a close pending, we go through the whole list */ | 1747 | have a close pending, we go through the whole list */ |
@@ -1744,8 +1752,8 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, | |||
1744 | if (!open_file->invalidHandle) { | 1752 | if (!open_file->invalidHandle) { |
1745 | /* found a good file */ | 1753 | /* found a good file */ |
1746 | /* lock it so it will not be closed on us */ | 1754 | /* lock it so it will not be closed on us */ |
1747 | cifsFileInfo_get_locked(open_file); | 1755 | cifsFileInfo_get(open_file); |
1748 | spin_unlock(&cifs_file_list_lock); | 1756 | spin_unlock(&tcon->open_file_lock); |
1749 | return open_file; | 1757 | return open_file; |
1750 | } /* else might as well continue, and look for | 1758 | } /* else might as well continue, and look for |
1751 | another, or simply have the caller reopen it | 1759 | another, or simply have the caller reopen it |
@@ -1753,7 +1761,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, | |||
1753 | } else /* write only file */ | 1761 | } else /* write only file */ |
1754 | break; /* write only files are last so must be done */ | 1762 | break; /* write only files are last so must be done */ |
1755 | } | 1763 | } |
1756 | spin_unlock(&cifs_file_list_lock); | 1764 | spin_unlock(&tcon->open_file_lock); |
1757 | return NULL; | 1765 | return NULL; |
1758 | } | 1766 | } |
1759 | 1767 | ||
@@ -1762,6 +1770,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, | |||
1762 | { | 1770 | { |
1763 | struct cifsFileInfo *open_file, *inv_file = NULL; | 1771 | struct cifsFileInfo *open_file, *inv_file = NULL; |
1764 | struct cifs_sb_info *cifs_sb; | 1772 | struct cifs_sb_info *cifs_sb; |
1773 | struct cifs_tcon *tcon; | ||
1765 | bool any_available = false; | 1774 | bool any_available = false; |
1766 | int rc; | 1775 | int rc; |
1767 | unsigned int refind = 0; | 1776 | unsigned int refind = 0; |
@@ -1777,15 +1786,16 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode, | |||
1777 | } | 1786 | } |
1778 | 1787 | ||
1779 | cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); | 1788 | cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb); |
1789 | tcon = cifs_sb_master_tcon(cifs_sb); | ||
1780 | 1790 | ||
1781 | /* only filter by fsuid on multiuser mounts */ | 1791 | /* only filter by fsuid on multiuser mounts */ |
1782 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) | 1792 | if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) |
1783 | fsuid_only = false; | 1793 | fsuid_only = false; |
1784 | 1794 | ||
1785 | spin_lock(&cifs_file_list_lock); | 1795 | spin_lock(&tcon->open_file_lock); |
1786 | refind_writable: | 1796 | refind_writable: |
1787 | if (refind > MAX_REOPEN_ATT) { | 1797 | if (refind > MAX_REOPEN_ATT) { |
1788 | spin_unlock(&cifs_file_list_lock); | 1798 | spin_unlock(&tcon->open_file_lock); |
1789 | return NULL; | 1799 | return NULL; |
1790 | } | 1800 | } |
1791 | list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { | 1801 | list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { |
@@ -1796,8 +1806,8 @@ refind_writable: | |||
1796 | if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { | 1806 | if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { |
1797 | if (!open_file->invalidHandle) { | 1807 | if (!open_file->invalidHandle) { |
1798 | /* found a good writable file */ | 1808 | /* found a good writable file */ |
1799 | cifsFileInfo_get_locked(open_file); | 1809 | cifsFileInfo_get(open_file); |
1800 | spin_unlock(&cifs_file_list_lock); | 1810 | spin_unlock(&tcon->open_file_lock); |
1801 | return open_file; | 1811 | return open_file; |
1802 | } else { | 1812 | } else { |
1803 | if (!inv_file) | 1813 | if (!inv_file) |
@@ -1813,24 +1823,24 @@ refind_writable: | |||
1813 | 1823 | ||
1814 | if (inv_file) { | 1824 | if (inv_file) { |
1815 | any_available = false; | 1825 | any_available = false; |
1816 | cifsFileInfo_get_locked(inv_file); | 1826 | cifsFileInfo_get(inv_file); |
1817 | } | 1827 | } |
1818 | 1828 | ||
1819 | spin_unlock(&cifs_file_list_lock); | 1829 | spin_unlock(&tcon->open_file_lock); |
1820 | 1830 | ||
1821 | if (inv_file) { | 1831 | if (inv_file) { |
1822 | rc = cifs_reopen_file(inv_file, false); | 1832 | rc = cifs_reopen_file(inv_file, false); |
1823 | if (!rc) | 1833 | if (!rc) |
1824 | return inv_file; | 1834 | return inv_file; |
1825 | else { | 1835 | else { |
1826 | spin_lock(&cifs_file_list_lock); | 1836 | spin_lock(&tcon->open_file_lock); |
1827 | list_move_tail(&inv_file->flist, | 1837 | list_move_tail(&inv_file->flist, |
1828 | &cifs_inode->openFileList); | 1838 | &cifs_inode->openFileList); |
1829 | spin_unlock(&cifs_file_list_lock); | 1839 | spin_unlock(&tcon->open_file_lock); |
1830 | cifsFileInfo_put(inv_file); | 1840 | cifsFileInfo_put(inv_file); |
1831 | spin_lock(&cifs_file_list_lock); | ||
1832 | ++refind; | 1841 | ++refind; |
1833 | inv_file = NULL; | 1842 | inv_file = NULL; |
1843 | spin_lock(&tcon->open_file_lock); | ||
1834 | goto refind_writable; | 1844 | goto refind_writable; |
1835 | } | 1845 | } |
1836 | } | 1846 | } |
@@ -3612,15 +3622,17 @@ static int cifs_readpage(struct file *file, struct page *page) | |||
3612 | static int is_inode_writable(struct cifsInodeInfo *cifs_inode) | 3622 | static int is_inode_writable(struct cifsInodeInfo *cifs_inode) |
3613 | { | 3623 | { |
3614 | struct cifsFileInfo *open_file; | 3624 | struct cifsFileInfo *open_file; |
3625 | struct cifs_tcon *tcon = | ||
3626 | cifs_sb_master_tcon(CIFS_SB(cifs_inode->vfs_inode.i_sb)); | ||
3615 | 3627 | ||
3616 | spin_lock(&cifs_file_list_lock); | 3628 | spin_lock(&tcon->open_file_lock); |
3617 | list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { | 3629 | list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { |
3618 | if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { | 3630 | if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { |
3619 | spin_unlock(&cifs_file_list_lock); | 3631 | spin_unlock(&tcon->open_file_lock); |
3620 | return 1; | 3632 | return 1; |
3621 | } | 3633 | } |
3622 | } | 3634 | } |
3623 | spin_unlock(&cifs_file_list_lock); | 3635 | spin_unlock(&tcon->open_file_lock); |
3624 | return 0; | 3636 | return 0; |
3625 | } | 3637 | } |
3626 | 3638 | ||
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 813fe13c2ae1..c6729156f9a0 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c | |||
@@ -120,6 +120,7 @@ tconInfoAlloc(void) | |||
120 | ++ret_buf->tc_count; | 120 | ++ret_buf->tc_count; |
121 | INIT_LIST_HEAD(&ret_buf->openFileList); | 121 | INIT_LIST_HEAD(&ret_buf->openFileList); |
122 | INIT_LIST_HEAD(&ret_buf->tcon_list); | 122 | INIT_LIST_HEAD(&ret_buf->tcon_list); |
123 | spin_lock_init(&ret_buf->open_file_lock); | ||
123 | #ifdef CONFIG_CIFS_STATS | 124 | #ifdef CONFIG_CIFS_STATS |
124 | spin_lock_init(&ret_buf->stat_lock); | 125 | spin_lock_init(&ret_buf->stat_lock); |
125 | #endif | 126 | #endif |
@@ -465,7 +466,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) | |||
465 | continue; | 466 | continue; |
466 | 467 | ||
467 | cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); | 468 | cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); |
468 | spin_lock(&cifs_file_list_lock); | 469 | spin_lock(&tcon->open_file_lock); |
469 | list_for_each(tmp2, &tcon->openFileList) { | 470 | list_for_each(tmp2, &tcon->openFileList) { |
470 | netfile = list_entry(tmp2, struct cifsFileInfo, | 471 | netfile = list_entry(tmp2, struct cifsFileInfo, |
471 | tlist); | 472 | tlist); |
@@ -495,11 +496,11 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) | |||
495 | &netfile->oplock_break); | 496 | &netfile->oplock_break); |
496 | netfile->oplock_break_cancelled = false; | 497 | netfile->oplock_break_cancelled = false; |
497 | 498 | ||
498 | spin_unlock(&cifs_file_list_lock); | 499 | spin_unlock(&tcon->open_file_lock); |
499 | spin_unlock(&cifs_tcp_ses_lock); | 500 | spin_unlock(&cifs_tcp_ses_lock); |
500 | return true; | 501 | return true; |
501 | } | 502 | } |
502 | spin_unlock(&cifs_file_list_lock); | 503 | spin_unlock(&tcon->open_file_lock); |
503 | spin_unlock(&cifs_tcp_ses_lock); | 504 | spin_unlock(&cifs_tcp_ses_lock); |
504 | cifs_dbg(FYI, "No matching file for oplock break\n"); | 505 | cifs_dbg(FYI, "No matching file for oplock break\n"); |
505 | return true; | 506 | return true; |
@@ -613,9 +614,9 @@ backup_cred(struct cifs_sb_info *cifs_sb) | |||
613 | void | 614 | void |
614 | cifs_del_pending_open(struct cifs_pending_open *open) | 615 | cifs_del_pending_open(struct cifs_pending_open *open) |
615 | { | 616 | { |
616 | spin_lock(&cifs_file_list_lock); | 617 | spin_lock(&tlink_tcon(open->tlink)->open_file_lock); |
617 | list_del(&open->olist); | 618 | list_del(&open->olist); |
618 | spin_unlock(&cifs_file_list_lock); | 619 | spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); |
619 | } | 620 | } |
620 | 621 | ||
621 | void | 622 | void |
@@ -635,7 +636,7 @@ void | |||
635 | cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, | 636 | cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, |
636 | struct cifs_pending_open *open) | 637 | struct cifs_pending_open *open) |
637 | { | 638 | { |
638 | spin_lock(&cifs_file_list_lock); | 639 | spin_lock(&tlink_tcon(tlink)->open_file_lock); |
639 | cifs_add_pending_open_locked(fid, tlink, open); | 640 | cifs_add_pending_open_locked(fid, tlink, open); |
640 | spin_unlock(&cifs_file_list_lock); | 641 | spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); |
641 | } | 642 | } |
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 65cf85dcda09..8f6a2a5863b9 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c | |||
@@ -597,14 +597,14 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, | |||
597 | is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) { | 597 | is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) { |
598 | /* close and restart search */ | 598 | /* close and restart search */ |
599 | cifs_dbg(FYI, "search backing up - close and restart search\n"); | 599 | cifs_dbg(FYI, "search backing up - close and restart search\n"); |
600 | spin_lock(&cifs_file_list_lock); | 600 | spin_lock(&cfile->file_info_lock); |
601 | if (server->ops->dir_needs_close(cfile)) { | 601 | if (server->ops->dir_needs_close(cfile)) { |
602 | cfile->invalidHandle = true; | 602 | cfile->invalidHandle = true; |
603 | spin_unlock(&cifs_file_list_lock); | 603 | spin_unlock(&cfile->file_info_lock); |
604 | if (server->ops->close_dir) | 604 | if (server->ops->close_dir) |
605 | server->ops->close_dir(xid, tcon, &cfile->fid); | 605 | server->ops->close_dir(xid, tcon, &cfile->fid); |
606 | } else | 606 | } else |
607 | spin_unlock(&cifs_file_list_lock); | 607 | spin_unlock(&cfile->file_info_lock); |
608 | if (cfile->srch_inf.ntwrk_buf_start) { | 608 | if (cfile->srch_inf.ntwrk_buf_start) { |
609 | cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n"); | 609 | cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n"); |
610 | if (cfile->srch_inf.smallBuf) | 610 | if (cfile->srch_inf.smallBuf) |
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 389fb9f8c84e..3d383489b9cf 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c | |||
@@ -549,19 +549,19 @@ smb2_is_valid_lease_break(char *buffer) | |||
549 | list_for_each(tmp1, &server->smb_ses_list) { | 549 | list_for_each(tmp1, &server->smb_ses_list) { |
550 | ses = list_entry(tmp1, struct cifs_ses, smb_ses_list); | 550 | ses = list_entry(tmp1, struct cifs_ses, smb_ses_list); |
551 | 551 | ||
552 | spin_lock(&cifs_file_list_lock); | ||
553 | list_for_each(tmp2, &ses->tcon_list) { | 552 | list_for_each(tmp2, &ses->tcon_list) { |
554 | tcon = list_entry(tmp2, struct cifs_tcon, | 553 | tcon = list_entry(tmp2, struct cifs_tcon, |
555 | tcon_list); | 554 | tcon_list); |
555 | spin_lock(&tcon->open_file_lock); | ||
556 | cifs_stats_inc( | 556 | cifs_stats_inc( |
557 | &tcon->stats.cifs_stats.num_oplock_brks); | 557 | &tcon->stats.cifs_stats.num_oplock_brks); |
558 | if (smb2_tcon_has_lease(tcon, rsp, lw)) { | 558 | if (smb2_tcon_has_lease(tcon, rsp, lw)) { |
559 | spin_unlock(&cifs_file_list_lock); | 559 | spin_unlock(&tcon->open_file_lock); |
560 | spin_unlock(&cifs_tcp_ses_lock); | 560 | spin_unlock(&cifs_tcp_ses_lock); |
561 | return true; | 561 | return true; |
562 | } | 562 | } |
563 | spin_unlock(&tcon->open_file_lock); | ||
563 | } | 564 | } |
564 | spin_unlock(&cifs_file_list_lock); | ||
565 | } | 565 | } |
566 | } | 566 | } |
567 | spin_unlock(&cifs_tcp_ses_lock); | 567 | spin_unlock(&cifs_tcp_ses_lock); |
@@ -603,7 +603,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) | |||
603 | tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); | 603 | tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); |
604 | 604 | ||
605 | cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); | 605 | cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); |
606 | spin_lock(&cifs_file_list_lock); | 606 | spin_lock(&tcon->open_file_lock); |
607 | list_for_each(tmp2, &tcon->openFileList) { | 607 | list_for_each(tmp2, &tcon->openFileList) { |
608 | cfile = list_entry(tmp2, struct cifsFileInfo, | 608 | cfile = list_entry(tmp2, struct cifsFileInfo, |
609 | tlist); | 609 | tlist); |
@@ -615,7 +615,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) | |||
615 | 615 | ||
616 | cifs_dbg(FYI, "file id match, oplock break\n"); | 616 | cifs_dbg(FYI, "file id match, oplock break\n"); |
617 | cinode = CIFS_I(d_inode(cfile->dentry)); | 617 | cinode = CIFS_I(d_inode(cfile->dentry)); |
618 | 618 | spin_lock(&cfile->file_info_lock); | |
619 | if (!CIFS_CACHE_WRITE(cinode) && | 619 | if (!CIFS_CACHE_WRITE(cinode) && |
620 | rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE) | 620 | rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE) |
621 | cfile->oplock_break_cancelled = true; | 621 | cfile->oplock_break_cancelled = true; |
@@ -637,14 +637,14 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) | |||
637 | clear_bit( | 637 | clear_bit( |
638 | CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, | 638 | CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, |
639 | &cinode->flags); | 639 | &cinode->flags); |
640 | 640 | spin_unlock(&cfile->file_info_lock); | |
641 | queue_work(cifsiod_wq, &cfile->oplock_break); | 641 | queue_work(cifsiod_wq, &cfile->oplock_break); |
642 | 642 | ||
643 | spin_unlock(&cifs_file_list_lock); | 643 | spin_unlock(&tcon->open_file_lock); |
644 | spin_unlock(&cifs_tcp_ses_lock); | 644 | spin_unlock(&cifs_tcp_ses_lock); |
645 | return true; | 645 | return true; |
646 | } | 646 | } |
647 | spin_unlock(&cifs_file_list_lock); | 647 | spin_unlock(&tcon->open_file_lock); |
648 | spin_unlock(&cifs_tcp_ses_lock); | 648 | spin_unlock(&cifs_tcp_ses_lock); |
649 | cifs_dbg(FYI, "No matching file for oplock break\n"); | 649 | cifs_dbg(FYI, "No matching file for oplock break\n"); |
650 | return true; | 650 | return true; |