blob: 1d1f7c03931b8285bb933cd4b59355f85d963385 [file] [log] [blame]
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * DFS referral cache routines
4 *
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03005 * Copyright (c) 2018-2019 Paulo Alcantara <palcantara@suse.de>
Paulo Alcantara54be1f62018-11-14 16:01:21 -02006 */
7
8#include <linux/rcupdate.h>
9#include <linux/rculist.h>
10#include <linux/jhash.h>
11#include <linux/ktime.h>
12#include <linux/slab.h>
13#include <linux/nls.h>
14#include <linux/workqueue.h>
15#include "cifsglob.h"
16#include "smb2pdu.h"
17#include "smb2proto.h"
18#include "cifsproto.h"
19#include "cifs_debug.h"
20#include "cifs_unicode.h"
21#include "smb2glob.h"
22
23#include "dfs_cache.h"
24
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030025#define CACHE_HTABLE_SIZE 32
26#define CACHE_MAX_ENTRIES 64
Paulo Alcantara54be1f62018-11-14 16:01:21 -020027
28#define IS_INTERLINK_SET(v) ((v) & (DFSREF_REFERRAL_SERVER | \
29 DFSREF_STORAGE_SERVER))
30
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030031struct cache_dfs_tgt {
32 char *name;
33 struct list_head list;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020034};
35
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030036struct cache_entry {
37 struct hlist_node hlist;
38 const char *path;
39 int ttl;
40 int srvtype;
41 int flags;
42 struct timespec64 etime;
43 int path_consumed;
44 int numtgts;
45 struct list_head tlist;
46 struct cache_dfs_tgt *tgthint;
47 struct rcu_head rcu;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020048};
49
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030050struct vol_info {
51 char *fullpath;
52 struct smb_vol smb_vol;
53 char *mntdata;
54 struct list_head list;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020055};
56
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030057static struct kmem_cache *cache_slab __read_mostly;
58static struct workqueue_struct *dfscache_wq __read_mostly;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020059
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030060static int cache_ttl;
61static struct nls_table *cache_nlsc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020062
63/*
64 * Number of entries in the cache
65 */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030066static size_t cache_count;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020067
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030068static struct hlist_head cache_htable[CACHE_HTABLE_SIZE];
69static DEFINE_MUTEX(list_lock);
70
71static LIST_HEAD(vol_list);
72static DEFINE_MUTEX(vol_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -020073
74static void refresh_cache_worker(struct work_struct *work);
75
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030076static DECLARE_DELAYED_WORK(refresh_task, refresh_cache_worker);
77
Paulo Alcantara (SUSE)ff2f7fc2019-12-04 17:38:01 -030078static int get_normalized_path(const char *path, char **npath)
Paulo Alcantara54be1f62018-11-14 16:01:21 -020079{
Paulo Alcantara (SUSE)ff2f7fc2019-12-04 17:38:01 -030080 if (!path || strlen(path) < 3 || (*path != '\\' && *path != '/'))
81 return -EINVAL;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020082
Paulo Alcantara54be1f62018-11-14 16:01:21 -020083 if (*path == '\\') {
84 *npath = (char *)path;
85 } else {
86 *npath = kstrndup(path, strlen(path), GFP_KERNEL);
87 if (!*npath)
88 return -ENOMEM;
89 convert_delimiter(*npath, '\\');
90 }
91 return 0;
92}
93
94static inline void free_normalized_path(const char *path, char *npath)
95{
96 if (path != npath)
97 kfree(npath);
98}
99
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300100static inline bool cache_entry_expired(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200101{
102 struct timespec64 ts;
103
Stephen Rothwell54e4f732018-12-17 20:11:46 +1100104 ktime_get_coarse_real_ts64(&ts);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300105 return timespec64_compare(&ts, &ce->etime) >= 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200106}
107
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300108static inline void free_tgts(struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200109{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300110 struct cache_dfs_tgt *t, *n;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200111
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300112 list_for_each_entry_safe(t, n, &ce->tlist, list) {
113 list_del(&t->list);
114 kfree(t->name);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200115 kfree(t);
116 }
117}
118
119static void free_cache_entry(struct rcu_head *rcu)
120{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300121 struct cache_entry *ce = container_of(rcu, struct cache_entry, rcu);
122
123 kmem_cache_free(cache_slab, ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200124}
125
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300126static inline void flush_cache_ent(struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200127{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300128 if (hlist_unhashed(&ce->hlist))
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200129 return;
130
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300131 hlist_del_init_rcu(&ce->hlist);
Paulo Alcantara (SUSE)199c6bd2019-12-04 17:37:59 -0300132 kfree(ce->path);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200133 free_tgts(ce);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300134 cache_count--;
135 call_rcu(&ce->rcu, free_cache_entry);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200136}
137
138static void flush_cache_ents(void)
139{
140 int i;
141
142 rcu_read_lock();
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300143 for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
144 struct hlist_head *l = &cache_htable[i];
145 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200146
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300147 hlist_for_each_entry_rcu(ce, l, hlist)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200148 flush_cache_ent(ce);
149 }
150 rcu_read_unlock();
151}
152
153/*
154 * dfs cache /proc file
155 */
156static int dfscache_proc_show(struct seq_file *m, void *v)
157{
158 int bucket;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300159 struct cache_entry *ce;
160 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200161
162 seq_puts(m, "DFS cache\n---------\n");
163
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300164 mutex_lock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200165
166 rcu_read_lock();
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300167 hash_for_each_rcu(cache_htable, bucket, ce, hlist) {
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200168 seq_printf(m,
169 "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,"
170 "interlink=%s,path_consumed=%d,expired=%s\n",
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300171 ce->path,
172 ce->srvtype == DFS_TYPE_ROOT ? "root" : "link",
173 ce->ttl, ce->etime.tv_nsec,
174 IS_INTERLINK_SET(ce->flags) ? "yes" : "no",
175 ce->path_consumed,
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200176 cache_entry_expired(ce) ? "yes" : "no");
177
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300178 list_for_each_entry(t, &ce->tlist, list) {
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200179 seq_printf(m, " %s%s\n",
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300180 t->name,
181 ce->tgthint == t ? " (target hint)" : "");
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200182 }
183
184 }
185 rcu_read_unlock();
186
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300187 mutex_unlock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200188 return 0;
189}
190
191static ssize_t dfscache_proc_write(struct file *file, const char __user *buffer,
192 size_t count, loff_t *ppos)
193{
194 char c;
195 int rc;
196
197 rc = get_user(c, buffer);
198 if (rc)
199 return rc;
200
201 if (c != '0')
202 return -EINVAL;
203
204 cifs_dbg(FYI, "clearing dfs cache");
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300205 mutex_lock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200206 flush_cache_ents();
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300207 mutex_unlock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200208
209 return count;
210}
211
212static int dfscache_proc_open(struct inode *inode, struct file *file)
213{
214 return single_open(file, dfscache_proc_show, NULL);
215}
216
217const struct file_operations dfscache_proc_fops = {
218 .open = dfscache_proc_open,
219 .read = seq_read,
220 .llseek = seq_lseek,
221 .release = single_release,
222 .write = dfscache_proc_write,
223};
224
225#ifdef CONFIG_CIFS_DEBUG2
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300226static inline void dump_tgts(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200227{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300228 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200229
230 cifs_dbg(FYI, "target list:\n");
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300231 list_for_each_entry(t, &ce->tlist, list) {
232 cifs_dbg(FYI, " %s%s\n", t->name,
233 ce->tgthint == t ? " (target hint)" : "");
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200234 }
235}
236
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300237static inline void dump_ce(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200238{
239 cifs_dbg(FYI, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,"
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300240 "interlink=%s,path_consumed=%d,expired=%s\n", ce->path,
241 ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl,
242 ce->etime.tv_nsec,
243 IS_INTERLINK_SET(ce->flags) ? "yes" : "no",
244 ce->path_consumed,
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200245 cache_entry_expired(ce) ? "yes" : "no");
246 dump_tgts(ce);
247}
248
249static inline void dump_refs(const struct dfs_info3_param *refs, int numrefs)
250{
251 int i;
252
253 cifs_dbg(FYI, "DFS referrals returned by the server:\n");
254 for (i = 0; i < numrefs; i++) {
255 const struct dfs_info3_param *ref = &refs[i];
256
257 cifs_dbg(FYI,
258 "\n"
259 "flags: 0x%x\n"
260 "path_consumed: %d\n"
261 "server_type: 0x%x\n"
262 "ref_flag: 0x%x\n"
263 "path_name: %s\n"
264 "node_name: %s\n"
265 "ttl: %d (%dm)\n",
266 ref->flags, ref->path_consumed, ref->server_type,
267 ref->ref_flag, ref->path_name, ref->node_name,
268 ref->ttl, ref->ttl / 60);
269 }
270}
271#else
272#define dump_tgts(e)
273#define dump_ce(e)
274#define dump_refs(r, n)
275#endif
276
277/**
278 * dfs_cache_init - Initialize DFS referral cache.
279 *
280 * Return zero if initialized successfully, otherwise non-zero.
281 */
282int dfs_cache_init(void)
283{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300284 int rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200285 int i;
286
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300287 dfscache_wq = alloc_workqueue("cifs-dfscache",
288 WQ_FREEZABLE | WQ_MEM_RECLAIM, 1);
289 if (!dfscache_wq)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200290 return -ENOMEM;
291
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300292 cache_slab = kmem_cache_create("cifs_dfs_cache",
293 sizeof(struct cache_entry), 0,
294 SLAB_HWCACHE_ALIGN, NULL);
295 if (!cache_slab) {
296 rc = -ENOMEM;
297 goto out_destroy_wq;
298 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200299
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300300 for (i = 0; i < CACHE_HTABLE_SIZE; i++)
301 INIT_HLIST_HEAD(&cache_htable[i]);
302
303 cache_ttl = -1;
304 cache_nlsc = load_nls_default();
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200305
306 cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__);
307 return 0;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300308
309out_destroy_wq:
310 destroy_workqueue(dfscache_wq);
311 return rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200312}
313
314static inline unsigned int cache_entry_hash(const void *data, int size)
315{
316 unsigned int h;
317
318 h = jhash(data, size, 0);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300319 return h & (CACHE_HTABLE_SIZE - 1);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200320}
321
322/* Check whether second path component of @path is SYSVOL or NETLOGON */
323static inline bool is_sysvol_or_netlogon(const char *path)
324{
325 const char *s;
326 char sep = path[0];
327
328 s = strchr(path + 1, sep) + 1;
329 return !strncasecmp(s, "sysvol", strlen("sysvol")) ||
330 !strncasecmp(s, "netlogon", strlen("netlogon"));
331}
332
333/* Return target hint of a DFS cache entry */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300334static inline char *get_tgt_name(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200335{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300336 struct cache_dfs_tgt *t = ce->tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200337
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300338 return t ? t->name : ERR_PTR(-ENOENT);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200339}
340
341/* Return expire time out of a new entry's TTL */
342static inline struct timespec64 get_expire_time(int ttl)
343{
344 struct timespec64 ts = {
345 .tv_sec = ttl,
346 .tv_nsec = 0,
347 };
Stephen Rothwell54e4f732018-12-17 20:11:46 +1100348 struct timespec64 now;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200349
Stephen Rothwell54e4f732018-12-17 20:11:46 +1100350 ktime_get_coarse_real_ts64(&now);
351 return timespec64_add(now, ts);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200352}
353
354/* Allocate a new DFS target */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300355static inline struct cache_dfs_tgt *alloc_tgt(const char *name)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200356{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300357 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200358
359 t = kmalloc(sizeof(*t), GFP_KERNEL);
360 if (!t)
361 return ERR_PTR(-ENOMEM);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300362 t->name = kstrndup(name, strlen(name), GFP_KERNEL);
363 if (!t->name) {
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200364 kfree(t);
365 return ERR_PTR(-ENOMEM);
366 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300367 INIT_LIST_HEAD(&t->list);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200368 return t;
369}
370
371/*
372 * Copy DFS referral information to a cache entry and conditionally update
373 * target hint.
374 */
375static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300376 struct cache_entry *ce, const char *tgthint)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200377{
378 int i;
379
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300380 ce->ttl = refs[0].ttl;
381 ce->etime = get_expire_time(ce->ttl);
382 ce->srvtype = refs[0].server_type;
383 ce->flags = refs[0].ref_flag;
384 ce->path_consumed = refs[0].path_consumed;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200385
386 for (i = 0; i < numrefs; i++) {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300387 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200388
389 t = alloc_tgt(refs[i].node_name);
390 if (IS_ERR(t)) {
391 free_tgts(ce);
392 return PTR_ERR(t);
393 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300394 if (tgthint && !strcasecmp(t->name, tgthint)) {
395 list_add(&t->list, &ce->tlist);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200396 tgthint = NULL;
397 } else {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300398 list_add_tail(&t->list, &ce->tlist);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200399 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300400 ce->numtgts++;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200401 }
402
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300403 ce->tgthint = list_first_entry_or_null(&ce->tlist,
404 struct cache_dfs_tgt, list);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200405
406 return 0;
407}
408
409/* Allocate a new cache entry */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300410static struct cache_entry *alloc_cache_entry(const char *path,
411 const struct dfs_info3_param *refs,
412 int numrefs)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200413{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300414 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200415 int rc;
416
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300417 ce = kmem_cache_zalloc(cache_slab, GFP_KERNEL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200418 if (!ce)
419 return ERR_PTR(-ENOMEM);
420
Paulo Alcantara (SUSE)199c6bd2019-12-04 17:37:59 -0300421 ce->path = kstrndup(path, strlen(path), GFP_KERNEL);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300422 if (!ce->path) {
423 kmem_cache_free(cache_slab, ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200424 return ERR_PTR(-ENOMEM);
425 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300426 INIT_HLIST_NODE(&ce->hlist);
427 INIT_LIST_HEAD(&ce->tlist);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200428
429 rc = copy_ref_data(refs, numrefs, ce, NULL);
430 if (rc) {
Paulo Alcantara (SUSE)199c6bd2019-12-04 17:37:59 -0300431 kfree(ce->path);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300432 kmem_cache_free(cache_slab, ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200433 ce = ERR_PTR(rc);
434 }
435 return ce;
436}
437
438static void remove_oldest_entry(void)
439{
440 int bucket;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300441 struct cache_entry *ce;
442 struct cache_entry *to_del = NULL;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200443
444 rcu_read_lock();
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300445 hash_for_each_rcu(cache_htable, bucket, ce, hlist) {
446 if (!to_del || timespec64_compare(&ce->etime,
447 &to_del->etime) < 0)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200448 to_del = ce;
449 }
450 if (!to_del) {
451 cifs_dbg(FYI, "%s: no entry to remove", __func__);
452 goto out;
453 }
454 cifs_dbg(FYI, "%s: removing entry", __func__);
455 dump_ce(to_del);
456 flush_cache_ent(to_del);
457out:
458 rcu_read_unlock();
459}
460
461/* Add a new DFS cache entry */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300462static inline struct cache_entry *
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200463add_cache_entry(unsigned int hash, const char *path,
464 const struct dfs_info3_param *refs, int numrefs)
465{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300466 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200467
468 ce = alloc_cache_entry(path, refs, numrefs);
469 if (IS_ERR(ce))
470 return ce;
471
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300472 hlist_add_head_rcu(&ce->hlist, &cache_htable[hash]);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200473
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300474 mutex_lock(&vol_lock);
475 if (cache_ttl < 0) {
476 cache_ttl = ce->ttl;
477 queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200478 } else {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300479 cache_ttl = min_t(int, cache_ttl, ce->ttl);
480 mod_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200481 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300482 mutex_unlock(&vol_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200483
484 return ce;
485}
486
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200487/*
488 * Find a DFS cache entry in hash table and optionally check prefix path against
489 * @path.
490 * Use whole path components in the match.
491 * Return ERR_PTR(-ENOENT) if the entry is not found.
492 */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300493static struct cache_entry *lookup_cache_entry(const char *path,
494 unsigned int *hash)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200495{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300496 struct cache_entry *ce;
497 unsigned int h;
498 bool found = false;
499
500 h = cache_entry_hash(path, strlen(path));
501
502 rcu_read_lock();
503 hlist_for_each_entry_rcu(ce, &cache_htable[h], hlist) {
504 if (!strcasecmp(path, ce->path)) {
505 found = true;
506 dump_ce(ce);
507 break;
508 }
509 }
510 rcu_read_unlock();
511
512 if (!found)
513 ce = ERR_PTR(-ENOENT);
514 if (hash)
515 *hash = h;
516
517 return ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200518}
519
520static inline void destroy_slab_cache(void)
521{
522 rcu_barrier();
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300523 kmem_cache_destroy(cache_slab);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200524}
525
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300526static inline void free_vol(struct vol_info *vi)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200527{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300528 list_del(&vi->list);
529 kfree(vi->fullpath);
530 kfree(vi->mntdata);
531 cifs_cleanup_volume_info_contents(&vi->smb_vol);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200532 kfree(vi);
533}
534
535static inline void free_vol_list(void)
536{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300537 struct vol_info *vi, *nvi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200538
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300539 list_for_each_entry_safe(vi, nvi, &vol_list, list)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200540 free_vol(vi);
541}
542
543/**
544 * dfs_cache_destroy - destroy DFS referral cache
545 */
546void dfs_cache_destroy(void)
547{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300548 cancel_delayed_work_sync(&refresh_task);
549 unload_nls(cache_nlsc);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200550 free_vol_list();
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200551 flush_cache_ents();
552 destroy_slab_cache();
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300553 destroy_workqueue(dfscache_wq);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200554
555 cifs_dbg(FYI, "%s: destroyed DFS referral cache\n", __func__);
556}
557
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300558static inline struct cache_entry *
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200559__update_cache_entry(const char *path, const struct dfs_info3_param *refs,
560 int numrefs)
561{
562 int rc;
563 unsigned int h;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300564 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200565 char *s, *th = NULL;
566
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300567 ce = lookup_cache_entry(path, &h);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200568 if (IS_ERR(ce))
569 return ce;
570
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300571 if (ce->tgthint) {
572 s = ce->tgthint->name;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200573 th = kstrndup(s, strlen(s), GFP_KERNEL);
574 if (!th)
575 return ERR_PTR(-ENOMEM);
576 }
577
578 free_tgts(ce);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300579 ce->numtgts = 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200580
581 rc = copy_ref_data(refs, numrefs, ce, th);
582 kfree(th);
583
584 if (rc)
585 ce = ERR_PTR(rc);
586
587 return ce;
588}
589
590/* Update an expired cache entry by getting a new DFS referral from server */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300591static struct cache_entry *
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200592update_cache_entry(const unsigned int xid, struct cifs_ses *ses,
593 const struct nls_table *nls_codepage, int remap,
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300594 const char *path, struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200595{
596 int rc;
597 struct dfs_info3_param *refs = NULL;
598 int numrefs = 0;
599
600 cifs_dbg(FYI, "%s: update expired cache entry\n", __func__);
601 /*
602 * Check if caller provided enough parameters to update an expired
603 * entry.
604 */
605 if (!ses || !ses->server || !ses->server->ops->get_dfs_refer)
606 return ERR_PTR(-ETIME);
607 if (unlikely(!nls_codepage))
608 return ERR_PTR(-ETIME);
609
610 cifs_dbg(FYI, "%s: DFS referral request for %s\n", __func__, path);
611
612 rc = ses->server->ops->get_dfs_refer(xid, ses, path, &refs, &numrefs,
613 nls_codepage, remap);
614 if (rc)
615 ce = ERR_PTR(rc);
616 else
617 ce = __update_cache_entry(path, refs, numrefs);
618
619 dump_refs(refs, numrefs);
620 free_dfs_info_array(refs, numrefs);
621
622 return ce;
623}
624
625/*
626 * Find, create or update a DFS cache entry.
627 *
628 * If the entry wasn't found, it will create a new one. Or if it was found but
629 * expired, then it will update the entry accordingly.
630 *
631 * For interlinks, __cifs_dfs_mount() and expand_dfs_referral() are supposed to
632 * handle them properly.
633 */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300634static struct cache_entry *
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200635do_dfs_cache_find(const unsigned int xid, struct cifs_ses *ses,
636 const struct nls_table *nls_codepage, int remap,
637 const char *path, bool noreq)
638{
639 int rc;
640 unsigned int h;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300641 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200642 struct dfs_info3_param *nrefs;
643 int numnrefs;
644
645 cifs_dbg(FYI, "%s: search path: %s\n", __func__, path);
646
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300647 ce = lookup_cache_entry(path, &h);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200648 if (IS_ERR(ce)) {
649 cifs_dbg(FYI, "%s: cache miss\n", __func__);
650 /*
651 * If @noreq is set, no requests will be sent to the server for
652 * either updating or getting a new DFS referral.
653 */
654 if (noreq)
655 return ce;
656 /*
657 * No cache entry was found, so check for valid parameters that
658 * will be required to get a new DFS referral and then create a
659 * new cache entry.
660 */
661 if (!ses || !ses->server || !ses->server->ops->get_dfs_refer) {
662 ce = ERR_PTR(-EOPNOTSUPP);
663 return ce;
664 }
665 if (unlikely(!nls_codepage)) {
666 ce = ERR_PTR(-EINVAL);
667 return ce;
668 }
669
670 nrefs = NULL;
671 numnrefs = 0;
672
673 cifs_dbg(FYI, "%s: DFS referral request for %s\n", __func__,
674 path);
675
676 rc = ses->server->ops->get_dfs_refer(xid, ses, path, &nrefs,
677 &numnrefs, nls_codepage,
678 remap);
679 if (rc) {
680 ce = ERR_PTR(rc);
681 return ce;
682 }
683
684 dump_refs(nrefs, numnrefs);
685
686 cifs_dbg(FYI, "%s: new cache entry\n", __func__);
687
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300688 if (cache_count >= CACHE_MAX_ENTRIES) {
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200689 cifs_dbg(FYI, "%s: reached max cache size (%d)",
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300690 __func__, CACHE_MAX_ENTRIES);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200691 remove_oldest_entry();
692 }
693 ce = add_cache_entry(h, path, nrefs, numnrefs);
694 free_dfs_info_array(nrefs, numnrefs);
695
696 if (IS_ERR(ce))
697 return ce;
698
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300699 cache_count++;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200700 }
701
702 dump_ce(ce);
703
704 /* Just return the found cache entry in case @noreq is set */
705 if (noreq)
706 return ce;
707
708 if (cache_entry_expired(ce)) {
709 cifs_dbg(FYI, "%s: expired cache entry\n", __func__);
710 ce = update_cache_entry(xid, ses, nls_codepage, remap, path,
711 ce);
712 if (IS_ERR(ce)) {
713 cifs_dbg(FYI, "%s: failed to update expired entry\n",
714 __func__);
715 }
716 }
717 return ce;
718}
719
720/* Set up a new DFS referral from a given cache entry */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300721static int setup_ref(const char *path, const struct cache_entry *ce,
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200722 struct dfs_info3_param *ref, const char *tgt)
723{
724 int rc;
725
726 cifs_dbg(FYI, "%s: set up new ref\n", __func__);
727
728 memset(ref, 0, sizeof(*ref));
729
730 ref->path_name = kstrndup(path, strlen(path), GFP_KERNEL);
731 if (!ref->path_name)
732 return -ENOMEM;
733
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300734 ref->path_consumed = ce->path_consumed;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200735
736 ref->node_name = kstrndup(tgt, strlen(tgt), GFP_KERNEL);
737 if (!ref->node_name) {
738 rc = -ENOMEM;
739 goto err_free_path;
740 }
741
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300742 ref->ttl = ce->ttl;
743 ref->server_type = ce->srvtype;
744 ref->ref_flag = ce->flags;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200745
746 return 0;
747
748err_free_path:
749 kfree(ref->path_name);
750 ref->path_name = NULL;
751 return rc;
752}
753
754/* Return target list of a DFS cache entry */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300755static int get_tgt_list(const struct cache_entry *ce,
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200756 struct dfs_cache_tgt_list *tl)
757{
758 int rc;
759 struct list_head *head = &tl->tl_list;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300760 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200761 struct dfs_cache_tgt_iterator *it, *nit;
762
763 memset(tl, 0, sizeof(*tl));
764 INIT_LIST_HEAD(head);
765
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300766 list_for_each_entry(t, &ce->tlist, list) {
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200767 it = kzalloc(sizeof(*it), GFP_KERNEL);
768 if (!it) {
769 rc = -ENOMEM;
770 goto err_free_it;
771 }
772
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300773 it->it_name = kstrndup(t->name, strlen(t->name),
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200774 GFP_KERNEL);
775 if (!it->it_name) {
Dan Carpenterc715f892019-01-05 21:18:03 +0300776 kfree(it);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200777 rc = -ENOMEM;
778 goto err_free_it;
779 }
780
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300781 if (ce->tgthint == t)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200782 list_add(&it->it_list, head);
783 else
784 list_add_tail(&it->it_list, head);
785 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300786 tl->tl_numtgts = ce->numtgts;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200787
788 return 0;
789
790err_free_it:
791 list_for_each_entry_safe(it, nit, head, it_list) {
792 kfree(it->it_name);
793 kfree(it);
794 }
795 return rc;
796}
797
798/**
799 * dfs_cache_find - find a DFS cache entry
800 *
801 * If it doesn't find the cache entry, then it will get a DFS referral
802 * for @path and create a new entry.
803 *
804 * In case the cache entry exists but expired, it will get a DFS referral
805 * for @path and then update the respective cache entry.
806 *
807 * These parameters are passed down to the get_dfs_refer() call if it
808 * needs to be issued:
809 * @xid: syscall xid
810 * @ses: smb session to issue the request on
811 * @nls_codepage: charset conversion
812 * @remap: path character remapping type
813 * @path: path to lookup in DFS referral cache.
814 *
815 * @ref: when non-NULL, store single DFS referral result in it.
816 * @tgt_list: when non-NULL, store complete DFS target list in it.
817 *
818 * Return zero if the target was found, otherwise non-zero.
819 */
820int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses,
821 const struct nls_table *nls_codepage, int remap,
822 const char *path, struct dfs_info3_param *ref,
823 struct dfs_cache_tgt_list *tgt_list)
824{
825 int rc;
826 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300827 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200828
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200829 rc = get_normalized_path(path, &npath);
830 if (rc)
831 return rc;
832
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300833 mutex_lock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200834 ce = do_dfs_cache_find(xid, ses, nls_codepage, remap, npath, false);
835 if (!IS_ERR(ce)) {
836 if (ref)
837 rc = setup_ref(path, ce, ref, get_tgt_name(ce));
838 else
839 rc = 0;
840 if (!rc && tgt_list)
841 rc = get_tgt_list(ce, tgt_list);
842 } else {
843 rc = PTR_ERR(ce);
844 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300845 mutex_unlock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200846 free_normalized_path(path, npath);
847 return rc;
848}
849
850/**
851 * dfs_cache_noreq_find - find a DFS cache entry without sending any requests to
852 * the currently connected server.
853 *
854 * NOTE: This function will neither update a cache entry in case it was
855 * expired, nor create a new cache entry if @path hasn't been found. It heavily
856 * relies on an existing cache entry.
857 *
858 * @path: path to lookup in the DFS referral cache.
859 * @ref: when non-NULL, store single DFS referral result in it.
860 * @tgt_list: when non-NULL, store complete DFS target list in it.
861 *
862 * Return 0 if successful.
863 * Return -ENOENT if the entry was not found.
864 * Return non-zero for other errors.
865 */
866int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
867 struct dfs_cache_tgt_list *tgt_list)
868{
869 int rc;
870 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300871 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200872
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200873 rc = get_normalized_path(path, &npath);
874 if (rc)
875 return rc;
876
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300877 mutex_lock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200878 ce = do_dfs_cache_find(0, NULL, NULL, 0, npath, true);
879 if (IS_ERR(ce)) {
880 rc = PTR_ERR(ce);
881 goto out;
882 }
883
884 if (ref)
885 rc = setup_ref(path, ce, ref, get_tgt_name(ce));
886 else
887 rc = 0;
888 if (!rc && tgt_list)
889 rc = get_tgt_list(ce, tgt_list);
890out:
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300891 mutex_unlock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200892 free_normalized_path(path, npath);
893 return rc;
894}
895
896/**
897 * dfs_cache_update_tgthint - update target hint of a DFS cache entry
898 *
899 * If it doesn't find the cache entry, then it will get a DFS referral for @path
900 * and create a new entry.
901 *
902 * In case the cache entry exists but expired, it will get a DFS referral
903 * for @path and then update the respective cache entry.
904 *
905 * @xid: syscall id
906 * @ses: smb session
907 * @nls_codepage: charset conversion
908 * @remap: type of character remapping for paths
909 * @path: path to lookup in DFS referral cache.
910 * @it: DFS target iterator
911 *
912 * Return zero if the target hint was updated successfully, otherwise non-zero.
913 */
914int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses,
915 const struct nls_table *nls_codepage, int remap,
916 const char *path,
917 const struct dfs_cache_tgt_iterator *it)
918{
919 int rc;
920 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300921 struct cache_entry *ce;
922 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200923
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200924 rc = get_normalized_path(path, &npath);
925 if (rc)
926 return rc;
927
928 cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
929
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300930 mutex_lock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200931 ce = do_dfs_cache_find(xid, ses, nls_codepage, remap, npath, false);
932 if (IS_ERR(ce)) {
933 rc = PTR_ERR(ce);
934 goto out;
935 }
936
937 rc = 0;
938
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300939 t = ce->tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200940
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300941 if (likely(!strcasecmp(it->it_name, t->name)))
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200942 goto out;
943
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300944 list_for_each_entry(t, &ce->tlist, list) {
945 if (!strcasecmp(t->name, it->it_name)) {
946 ce->tgthint = t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200947 cifs_dbg(FYI, "%s: new target hint: %s\n", __func__,
948 it->it_name);
949 break;
950 }
951 }
952
953out:
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300954 mutex_unlock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200955 free_normalized_path(path, npath);
956 return rc;
957}
958
959/**
960 * dfs_cache_noreq_update_tgthint - update target hint of a DFS cache entry
961 * without sending any requests to the currently connected server.
962 *
963 * NOTE: This function will neither update a cache entry in case it was
964 * expired, nor create a new cache entry if @path hasn't been found. It heavily
965 * relies on an existing cache entry.
966 *
967 * @path: path to lookup in DFS referral cache.
968 * @it: target iterator which contains the target hint to update the cache
969 * entry with.
970 *
971 * Return zero if the target hint was updated successfully, otherwise non-zero.
972 */
973int dfs_cache_noreq_update_tgthint(const char *path,
974 const struct dfs_cache_tgt_iterator *it)
975{
976 int rc;
977 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300978 struct cache_entry *ce;
979 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200980
Paulo Alcantara (SUSE)ff2f7fc2019-12-04 17:38:01 -0300981 if (!it)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200982 return -EINVAL;
983
984 rc = get_normalized_path(path, &npath);
985 if (rc)
986 return rc;
987
988 cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
989
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300990 mutex_lock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200991
992 ce = do_dfs_cache_find(0, NULL, NULL, 0, npath, true);
993 if (IS_ERR(ce)) {
994 rc = PTR_ERR(ce);
995 goto out;
996 }
997
998 rc = 0;
999
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001000 t = ce->tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001001
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001002 if (unlikely(!strcasecmp(it->it_name, t->name)))
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001003 goto out;
1004
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001005 list_for_each_entry(t, &ce->tlist, list) {
1006 if (!strcasecmp(t->name, it->it_name)) {
1007 ce->tgthint = t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001008 cifs_dbg(FYI, "%s: new target hint: %s\n", __func__,
1009 it->it_name);
1010 break;
1011 }
1012 }
1013
1014out:
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001015 mutex_unlock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001016 free_normalized_path(path, npath);
1017 return rc;
1018}
1019
1020/**
1021 * dfs_cache_get_tgt_referral - returns a DFS referral (@ref) from a given
1022 * target iterator (@it).
1023 *
1024 * @path: path to lookup in DFS referral cache.
1025 * @it: DFS target iterator.
1026 * @ref: DFS referral pointer to set up the gathered information.
1027 *
1028 * Return zero if the DFS referral was set up correctly, otherwise non-zero.
1029 */
1030int dfs_cache_get_tgt_referral(const char *path,
1031 const struct dfs_cache_tgt_iterator *it,
1032 struct dfs_info3_param *ref)
1033{
1034 int rc;
1035 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001036 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001037 unsigned int h;
1038
1039 if (!it || !ref)
1040 return -EINVAL;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001041
1042 rc = get_normalized_path(path, &npath);
1043 if (rc)
1044 return rc;
1045
1046 cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
1047
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001048 mutex_lock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001049
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001050 ce = lookup_cache_entry(npath, &h);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001051 if (IS_ERR(ce)) {
1052 rc = PTR_ERR(ce);
1053 goto out;
1054 }
1055
1056 cifs_dbg(FYI, "%s: target name: %s\n", __func__, it->it_name);
1057
1058 rc = setup_ref(path, ce, ref, it->it_name);
1059
1060out:
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001061 mutex_unlock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001062 free_normalized_path(path, npath);
1063 return rc;
1064}
1065
1066static int dup_vol(struct smb_vol *vol, struct smb_vol *new)
1067{
1068 memcpy(new, vol, sizeof(*new));
1069
1070 if (vol->username) {
1071 new->username = kstrndup(vol->username, strlen(vol->username),
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001072 GFP_KERNEL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001073 if (!new->username)
1074 return -ENOMEM;
1075 }
1076 if (vol->password) {
1077 new->password = kstrndup(vol->password, strlen(vol->password),
1078 GFP_KERNEL);
1079 if (!new->password)
1080 goto err_free_username;
1081 }
1082 if (vol->UNC) {
1083 cifs_dbg(FYI, "%s: vol->UNC: %s\n", __func__, vol->UNC);
1084 new->UNC = kstrndup(vol->UNC, strlen(vol->UNC), GFP_KERNEL);
1085 if (!new->UNC)
1086 goto err_free_password;
1087 }
1088 if (vol->domainname) {
1089 new->domainname = kstrndup(vol->domainname,
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001090 strlen(vol->domainname), GFP_KERNEL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001091 if (!new->domainname)
1092 goto err_free_unc;
1093 }
1094 if (vol->iocharset) {
1095 new->iocharset = kstrndup(vol->iocharset,
1096 strlen(vol->iocharset), GFP_KERNEL);
1097 if (!new->iocharset)
1098 goto err_free_domainname;
1099 }
1100 if (vol->prepath) {
1101 cifs_dbg(FYI, "%s: vol->prepath: %s\n", __func__, vol->prepath);
1102 new->prepath = kstrndup(vol->prepath, strlen(vol->prepath),
1103 GFP_KERNEL);
1104 if (!new->prepath)
1105 goto err_free_iocharset;
1106 }
1107
1108 return 0;
1109
1110err_free_iocharset:
1111 kfree(new->iocharset);
1112err_free_domainname:
1113 kfree(new->domainname);
1114err_free_unc:
1115 kfree(new->UNC);
1116err_free_password:
Dan Carpenter34bca9b2018-12-20 14:32:43 +03001117 kzfree(new->password);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001118err_free_username:
1119 kfree(new->username);
1120 kfree(new);
1121 return -ENOMEM;
1122}
1123
1124/**
1125 * dfs_cache_add_vol - add a cifs volume during mount() that will be handled by
1126 * DFS cache refresh worker.
1127 *
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001128 * @mntdata: mount data.
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001129 * @vol: cifs volume.
1130 * @fullpath: origin full path.
1131 *
1132 * Return zero if volume was set up correctly, otherwise non-zero.
1133 */
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001134int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol, const char *fullpath)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001135{
1136 int rc;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001137 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001138
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001139 if (!vol || !fullpath || !mntdata)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001140 return -EINVAL;
1141
1142 cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
1143
1144 vi = kzalloc(sizeof(*vi), GFP_KERNEL);
1145 if (!vi)
1146 return -ENOMEM;
1147
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001148 vi->fullpath = kstrndup(fullpath, strlen(fullpath), GFP_KERNEL);
1149 if (!vi->fullpath) {
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001150 rc = -ENOMEM;
1151 goto err_free_vi;
1152 }
1153
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001154 rc = dup_vol(vol, &vi->smb_vol);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001155 if (rc)
1156 goto err_free_fullpath;
1157
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001158 vi->mntdata = mntdata;
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001159
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001160 mutex_lock(&vol_lock);
1161 list_add_tail(&vi->list, &vol_list);
1162 mutex_unlock(&vol_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001163 return 0;
1164
1165err_free_fullpath:
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001166 kfree(vi->fullpath);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001167err_free_vi:
1168 kfree(vi);
1169 return rc;
1170}
1171
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001172static inline struct vol_info *find_vol(const char *fullpath)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001173{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001174 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001175
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001176 list_for_each_entry(vi, &vol_list, list) {
1177 cifs_dbg(FYI, "%s: vi->fullpath: %s\n", __func__, vi->fullpath);
1178 if (!strcasecmp(vi->fullpath, fullpath))
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001179 return vi;
1180 }
1181 return ERR_PTR(-ENOENT);
1182}
1183
1184/**
1185 * dfs_cache_update_vol - update vol info in DFS cache after failover
1186 *
1187 * @fullpath: fullpath to look up in volume list.
1188 * @server: TCP ses pointer.
1189 *
1190 * Return zero if volume was updated, otherwise non-zero.
1191 */
1192int dfs_cache_update_vol(const char *fullpath, struct TCP_Server_Info *server)
1193{
1194 int rc;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001195 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001196
1197 if (!fullpath || !server)
1198 return -EINVAL;
1199
1200 cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
1201
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001202 mutex_lock(&vol_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001203
1204 vi = find_vol(fullpath);
1205 if (IS_ERR(vi)) {
1206 rc = PTR_ERR(vi);
1207 goto out;
1208 }
1209
1210 cifs_dbg(FYI, "%s: updating volume info\n", __func__);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001211 memcpy(&vi->smb_vol.dstaddr, &server->dstaddr,
1212 sizeof(vi->smb_vol.dstaddr));
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001213 rc = 0;
1214
1215out:
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001216 mutex_unlock(&vol_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001217 return rc;
1218}
1219
1220/**
1221 * dfs_cache_del_vol - remove volume info in DFS cache during umount()
1222 *
1223 * @fullpath: fullpath to look up in volume list.
1224 */
1225void dfs_cache_del_vol(const char *fullpath)
1226{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001227 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001228
1229 if (!fullpath || !*fullpath)
1230 return;
1231
1232 cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
1233
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001234 mutex_lock(&vol_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001235 vi = find_vol(fullpath);
1236 if (!IS_ERR(vi))
1237 free_vol(vi);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001238 mutex_unlock(&vol_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001239}
1240
1241/* Get all tcons that are within a DFS namespace and can be refreshed */
1242static void get_tcons(struct TCP_Server_Info *server, struct list_head *head)
1243{
1244 struct cifs_ses *ses;
1245 struct cifs_tcon *tcon;
1246
1247 INIT_LIST_HEAD(head);
1248
1249 spin_lock(&cifs_tcp_ses_lock);
1250 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
1251 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
1252 if (!tcon->need_reconnect && !tcon->need_reopen_files &&
1253 tcon->dfs_path) {
1254 tcon->tc_count++;
1255 list_add_tail(&tcon->ulist, head);
1256 }
1257 }
1258 if (ses->tcon_ipc && !ses->tcon_ipc->need_reconnect &&
1259 ses->tcon_ipc->dfs_path) {
1260 list_add_tail(&ses->tcon_ipc->ulist, head);
1261 }
1262 }
1263 spin_unlock(&cifs_tcp_ses_lock);
1264}
1265
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001266static bool is_dfs_link(const char *path)
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001267{
1268 char *s;
1269
1270 s = strchr(path + 1, '\\');
1271 if (!s)
1272 return false;
1273 return !!strchr(s + 1, '\\');
1274}
1275
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001276static char *get_dfs_root(const char *path)
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001277{
1278 char *s, *npath;
1279
1280 s = strchr(path + 1, '\\');
1281 if (!s)
1282 return ERR_PTR(-EINVAL);
1283
1284 s = strchr(s + 1, '\\');
1285 if (!s)
1286 return ERR_PTR(-EINVAL);
1287
1288 npath = kstrndup(path, s - path, GFP_KERNEL);
1289 if (!npath)
1290 return ERR_PTR(-ENOMEM);
1291
1292 return npath;
1293}
1294
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001295static inline void put_tcp_server(struct TCP_Server_Info *server)
1296{
1297 cifs_put_tcp_session(server, 0);
1298}
1299
1300static struct TCP_Server_Info *get_tcp_server(struct smb_vol *vol)
1301{
1302 struct TCP_Server_Info *server;
1303
1304 server = cifs_find_tcp_session(vol);
1305 if (IS_ERR_OR_NULL(server))
1306 return NULL;
1307
1308 spin_lock(&GlobalMid_Lock);
1309 if (server->tcpStatus != CifsGood) {
1310 spin_unlock(&GlobalMid_Lock);
1311 put_tcp_server(server);
1312 return NULL;
1313 }
1314 spin_unlock(&GlobalMid_Lock);
1315
1316 return server;
1317}
1318
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001319/* Find root SMB session out of a DFS link path */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001320static struct cifs_ses *find_root_ses(struct vol_info *vi,
1321 struct cifs_tcon *tcon,
1322 const char *path)
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001323{
1324 char *rpath;
1325 int rc;
1326 struct dfs_info3_param ref = {0};
1327 char *mdata = NULL, *devname = NULL;
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001328 struct TCP_Server_Info *server;
1329 struct cifs_ses *ses;
1330 struct smb_vol vol;
1331
1332 rpath = get_dfs_root(path);
1333 if (IS_ERR(rpath))
1334 return ERR_CAST(rpath);
1335
1336 memset(&vol, 0, sizeof(vol));
1337
1338 rc = dfs_cache_noreq_find(rpath, &ref, NULL);
1339 if (rc) {
1340 ses = ERR_PTR(rc);
1341 goto out;
1342 }
1343
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001344 mdata = cifs_compose_mount_options(vi->mntdata, rpath, &ref, &devname);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001345 free_dfs_info_param(&ref);
1346
1347 if (IS_ERR(mdata)) {
1348 ses = ERR_CAST(mdata);
1349 mdata = NULL;
1350 goto out;
1351 }
1352
Paulo Alcantara (SUSE)df3df922019-11-22 12:30:52 -03001353 rc = cifs_setup_volume_info(&vol, mdata, devname, false);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001354 kfree(devname);
1355
1356 if (rc) {
1357 ses = ERR_PTR(rc);
1358 goto out;
1359 }
1360
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001361 server = get_tcp_server(&vol);
1362 if (!server) {
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001363 ses = ERR_PTR(-EHOSTDOWN);
1364 goto out;
1365 }
1366
1367 ses = cifs_get_smb_ses(server, &vol);
1368
1369out:
1370 cifs_cleanup_volume_info_contents(&vol);
1371 kfree(mdata);
1372 kfree(rpath);
1373
1374 return ses;
1375}
1376
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001377/* Refresh DFS cache entry from a given tcon */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001378static void refresh_tcon(struct vol_info *vi, struct cifs_tcon *tcon)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001379{
1380 int rc = 0;
1381 unsigned int xid;
1382 char *path, *npath;
1383 unsigned int h;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001384 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001385 struct dfs_info3_param *refs = NULL;
1386 int numrefs = 0;
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001387 struct cifs_ses *root_ses = NULL, *ses;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001388
1389 xid = get_xid();
1390
1391 path = tcon->dfs_path + 1;
1392
1393 rc = get_normalized_path(path, &npath);
1394 if (rc)
1395 goto out;
1396
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001397 mutex_lock(&list_lock);
1398 ce = lookup_cache_entry(npath, &h);
1399 mutex_unlock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001400
1401 if (IS_ERR(ce)) {
1402 rc = PTR_ERR(ce);
1403 goto out;
1404 }
1405
1406 if (!cache_entry_expired(ce))
1407 goto out;
1408
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001409 /* If it's a DFS Link, then use root SMB session for refreshing it */
1410 if (is_dfs_link(npath)) {
1411 ses = root_ses = find_root_ses(vi, tcon, npath);
1412 if (IS_ERR(ses)) {
1413 rc = PTR_ERR(ses);
1414 root_ses = NULL;
1415 goto out;
1416 }
1417 } else {
1418 ses = tcon->ses;
1419 }
1420
1421 if (unlikely(!ses->server->ops->get_dfs_refer)) {
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001422 rc = -EOPNOTSUPP;
1423 } else {
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001424 rc = ses->server->ops->get_dfs_refer(xid, ses, path, &refs,
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001425 &numrefs, cache_nlsc,
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001426 tcon->remap);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001427 if (!rc) {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001428 mutex_lock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001429 ce = __update_cache_entry(npath, refs, numrefs);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001430 mutex_unlock(&list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001431 dump_refs(refs, numrefs);
1432 free_dfs_info_array(refs, numrefs);
1433 if (IS_ERR(ce))
1434 rc = PTR_ERR(ce);
1435 }
1436 }
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001437
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001438out:
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001439 if (root_ses)
1440 cifs_put_smb_ses(root_ses);
1441
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001442 free_xid(xid);
1443 free_normalized_path(path, npath);
1444}
1445
1446/*
1447 * Worker that will refresh DFS cache based on lowest TTL value from a DFS
1448 * referral.
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001449 */
1450static void refresh_cache_worker(struct work_struct *work)
1451{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001452 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001453 struct TCP_Server_Info *server;
1454 LIST_HEAD(list);
1455 struct cifs_tcon *tcon, *ntcon;
1456
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001457 mutex_lock(&vol_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001458
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001459 list_for_each_entry(vi, &vol_list, list) {
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001460 server = get_tcp_server(&vi->smb_vol);
1461 if (!server)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001462 continue;
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001463
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001464 get_tcons(server, &list);
1465 list_for_each_entry_safe(tcon, ntcon, &list, ulist) {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001466 refresh_tcon(vi, tcon);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001467 list_del_init(&tcon->ulist);
1468 cifs_put_tcon(tcon);
1469 }
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001470
1471 put_tcp_server(server);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001472 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001473 queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
1474 mutex_unlock(&vol_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001475}