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 /fs/cifs/file.c | |
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>
Diffstat (limited to 'fs/cifs/file.c')
-rw-r--r-- | fs/cifs/file.c | 66 |
1 files changed, 39 insertions, 27 deletions
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 | ||