| // SPDX-License-Identifier: LGPL-2.1 | |
| /* | |
| * CIFS filesystem cache interface | |
| * | |
| * Copyright (c) 2010 Novell, Inc. | |
| * Author(s): Suresh Jayaraman <sjayaraman@suse.de> | |
| * | |
| */ | |
| #include "fscache.h" | |
| #include "cifsglob.h" | |
| #include "cifs_debug.h" | |
| #include "cifs_fs_sb.h" | |
| #include "cifsproto.h" | |
| /* | |
| * Key for fscache inode. [!] Contents must match comparisons in cifs_find_inode(). | |
| */ | |
| struct cifs_fscache_inode_key { | |
| __le64 uniqueid; /* server inode number */ | |
| __le64 createtime; /* creation time on server */ | |
| u8 type; /* S_IFMT file type */ | |
| } __packed; | |
| static void cifs_fscache_fill_volume_coherency( | |
| struct cifs_tcon *tcon, | |
| struct cifs_fscache_volume_coherency_data *cd) | |
| { | |
| memset(cd, 0, sizeof(*cd)); | |
| cd->resource_id = cpu_to_le64(tcon->resource_id); | |
| cd->vol_create_time = tcon->vol_create_time; | |
| cd->vol_serial_number = cpu_to_le32(tcon->vol_serial_number); | |
| } | |
| int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) | |
| { | |
| struct cifs_fscache_volume_coherency_data cd; | |
| struct TCP_Server_Info *server = tcon->ses->server; | |
| struct fscache_volume *vcookie; | |
| const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr; | |
| char *sharename; | |
| char *key; | |
| int ret = -ENOMEM; | |
| if (tcon->fscache_acquired) | |
| return 0; | |
| mutex_lock(&tcon->fscache_lock); | |
| if (tcon->fscache_acquired) { | |
| mutex_unlock(&tcon->fscache_lock); | |
| return 0; | |
| } | |
| tcon->fscache_acquired = true; | |
| tcon->fscache = NULL; | |
| switch (sa->sa_family) { | |
| case AF_INET: | |
| case AF_INET6: | |
| break; | |
| default: | |
| mutex_unlock(&tcon->fscache_lock); | |
| cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family); | |
| return -EINVAL; | |
| } | |
| memset(&key, 0, sizeof(key)); | |
| sharename = extract_sharename(tcon->tree_name); | |
| if (IS_ERR(sharename)) { | |
| mutex_unlock(&tcon->fscache_lock); | |
| cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__); | |
| return PTR_ERR(sharename); | |
| } | |
| strreplace(sharename, '/', ';'); | |
| key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename); | |
| if (!key) | |
| goto out; | |
| cifs_fscache_fill_volume_coherency(tcon, &cd); | |
| vcookie = fscache_acquire_volume(key, | |
| NULL, /* preferred_cache */ | |
| &cd, sizeof(cd)); | |
| cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, vcookie); | |
| if (IS_ERR(vcookie)) { | |
| if (vcookie != ERR_PTR(-EBUSY)) { | |
| ret = PTR_ERR(vcookie); | |
| goto out_2; | |
| } | |
| pr_err("Cache volume key already in use (%s)\n", key); | |
| vcookie = NULL; | |
| trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, | |
| netfs_trace_tcon_ref_see_fscache_collision); | |
| } else { | |
| trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, | |
| netfs_trace_tcon_ref_see_fscache_okay); | |
| } | |
| tcon->fscache = vcookie; | |
| ret = 0; | |
| out_2: | |
| kfree(key); | |
| out: | |
| kfree(sharename); | |
| mutex_unlock(&tcon->fscache_lock); | |
| return ret; | |
| } | |
| void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) | |
| { | |
| struct cifs_fscache_volume_coherency_data cd; | |
| cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache); | |
| cifs_fscache_fill_volume_coherency(tcon, &cd); | |
| fscache_relinquish_volume(tcon->fscache, &cd, false); | |
| tcon->fscache = NULL; | |
| trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, | |
| netfs_trace_tcon_ref_see_fscache_relinq); | |
| } | |
| void cifs_fscache_get_inode_cookie(struct inode *inode) | |
| { | |
| struct cifs_fscache_inode_coherency_data cd; | |
| struct cifs_fscache_inode_key key; | |
| struct cifsInodeInfo *cifsi = CIFS_I(inode); | |
| struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | |
| struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); | |
| key.uniqueid = cpu_to_le64(cifsi->uniqueid); | |
| key.createtime = cpu_to_le64(cifsi->createtime); | |
| key.type = (inode->i_mode & S_IFMT) >> 12; | |
| cifs_fscache_fill_coherency(&cifsi->netfs.inode, &cd); | |
| cifsi->netfs.cache = | |
| fscache_acquire_cookie(tcon->fscache, 0, | |
| &key, sizeof(key), | |
| &cd, sizeof(cd), | |
| i_size_read(&cifsi->netfs.inode)); | |
| if (cifsi->netfs.cache) | |
| mapping_set_release_always(inode->i_mapping); | |
| } | |
| void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) | |
| { | |
| if (update) { | |
| struct cifs_fscache_inode_coherency_data cd; | |
| loff_t i_size = i_size_read(inode); | |
| cifs_fscache_fill_coherency(inode, &cd); | |
| fscache_unuse_cookie(cifs_inode_cookie(inode), &cd, &i_size); | |
| } else { | |
| fscache_unuse_cookie(cifs_inode_cookie(inode), NULL, NULL); | |
| } | |
| } | |
| void cifs_fscache_release_inode_cookie(struct inode *inode) | |
| { | |
| struct cifsInodeInfo *cifsi = CIFS_I(inode); | |
| struct fscache_cookie *cookie = cifs_inode_cookie(inode); | |
| if (cookie) { | |
| cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cookie); | |
| fscache_relinquish_cookie(cookie, false); | |
| cifsi->netfs.cache = NULL; | |
| } | |
| } |