0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031 #include <linux/export.h>
0032 #include <linux/nfs_fs.h>
0033 #include "nfs4session.h"
0034 #include "internal.h"
0035 #include "pnfs.h"
0036
0037 #include "nfs4trace.h"
0038
0039 #define NFSDBG_FACILITY NFSDBG_PNFS
0040
0041
0042
0043
0044 #define NFS4_DEVICE_ID_HASH_BITS 5
0045 #define NFS4_DEVICE_ID_HASH_SIZE (1 << NFS4_DEVICE_ID_HASH_BITS)
0046 #define NFS4_DEVICE_ID_HASH_MASK (NFS4_DEVICE_ID_HASH_SIZE - 1)
0047
0048
0049 static struct hlist_head nfs4_deviceid_cache[NFS4_DEVICE_ID_HASH_SIZE];
0050 static DEFINE_SPINLOCK(nfs4_deviceid_lock);
0051
0052 #ifdef NFS_DEBUG
0053 void
0054 nfs4_print_deviceid(const struct nfs4_deviceid *id)
0055 {
0056 u32 *p = (u32 *)id;
0057
0058 dprintk("%s: device id= [%x%x%x%x]\n", __func__,
0059 p[0], p[1], p[2], p[3]);
0060 }
0061 EXPORT_SYMBOL_GPL(nfs4_print_deviceid);
0062 #endif
0063
0064 static inline u32
0065 nfs4_deviceid_hash(const struct nfs4_deviceid *id)
0066 {
0067 unsigned char *cptr = (unsigned char *)id->data;
0068 unsigned int nbytes = NFS4_DEVICEID4_SIZE;
0069 u32 x = 0;
0070
0071 while (nbytes--) {
0072 x *= 37;
0073 x += *cptr++;
0074 }
0075 return x & NFS4_DEVICE_ID_HASH_MASK;
0076 }
0077
0078 static struct nfs4_deviceid_node *
0079 _lookup_deviceid(const struct pnfs_layoutdriver_type *ld,
0080 const struct nfs_client *clp, const struct nfs4_deviceid *id,
0081 long hash)
0082 {
0083 struct nfs4_deviceid_node *d;
0084
0085 hlist_for_each_entry_rcu(d, &nfs4_deviceid_cache[hash], node)
0086 if (d->ld == ld && d->nfs_client == clp &&
0087 !memcmp(&d->deviceid, id, sizeof(*id))) {
0088 if (atomic_read(&d->ref))
0089 return d;
0090 else
0091 continue;
0092 }
0093 return NULL;
0094 }
0095
0096 static struct nfs4_deviceid_node *
0097 nfs4_get_device_info(struct nfs_server *server,
0098 const struct nfs4_deviceid *dev_id,
0099 const struct cred *cred, gfp_t gfp_flags)
0100 {
0101 struct nfs4_deviceid_node *d = NULL;
0102 struct pnfs_device *pdev = NULL;
0103 struct page **pages = NULL;
0104 u32 max_resp_sz;
0105 int max_pages;
0106 int rc, i;
0107
0108
0109
0110
0111
0112 max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
0113 if (server->pnfs_curr_ld->max_deviceinfo_size &&
0114 server->pnfs_curr_ld->max_deviceinfo_size < max_resp_sz)
0115 max_resp_sz = server->pnfs_curr_ld->max_deviceinfo_size;
0116 max_pages = nfs_page_array_len(0, max_resp_sz);
0117 dprintk("%s: server %p max_resp_sz %u max_pages %d\n",
0118 __func__, server, max_resp_sz, max_pages);
0119
0120 pdev = kzalloc(sizeof(*pdev), gfp_flags);
0121 if (!pdev)
0122 return NULL;
0123
0124 pages = kcalloc(max_pages, sizeof(struct page *), gfp_flags);
0125 if (!pages)
0126 goto out_free_pdev;
0127
0128 for (i = 0; i < max_pages; i++) {
0129 pages[i] = alloc_page(gfp_flags);
0130 if (!pages[i])
0131 goto out_free_pages;
0132 }
0133
0134 memcpy(&pdev->dev_id, dev_id, sizeof(*dev_id));
0135 pdev->layout_type = server->pnfs_curr_ld->id;
0136 pdev->pages = pages;
0137 pdev->pgbase = 0;
0138 pdev->pglen = max_resp_sz;
0139 pdev->mincount = 0;
0140 pdev->maxcount = max_resp_sz - nfs41_maxgetdevinfo_overhead;
0141
0142 rc = nfs4_proc_getdeviceinfo(server, pdev, cred);
0143 dprintk("%s getdevice info returns %d\n", __func__, rc);
0144 if (rc)
0145 goto out_free_pages;
0146
0147
0148
0149
0150
0151 d = server->pnfs_curr_ld->alloc_deviceid_node(server, pdev,
0152 gfp_flags);
0153 if (d && pdev->nocache)
0154 set_bit(NFS_DEVICEID_NOCACHE, &d->flags);
0155
0156 out_free_pages:
0157 for (i = 0; i < max_pages; i++)
0158 __free_page(pages[i]);
0159 kfree(pages);
0160 out_free_pdev:
0161 kfree(pdev);
0162 dprintk("<-- %s d %p\n", __func__, d);
0163 return d;
0164 }
0165
0166
0167
0168
0169
0170
0171
0172 static struct nfs4_deviceid_node *
0173 __nfs4_find_get_deviceid(struct nfs_server *server,
0174 const struct nfs4_deviceid *id, long hash)
0175 {
0176 struct nfs4_deviceid_node *d;
0177
0178 rcu_read_lock();
0179 d = _lookup_deviceid(server->pnfs_curr_ld, server->nfs_client, id,
0180 hash);
0181 if (d != NULL && !atomic_inc_not_zero(&d->ref))
0182 d = NULL;
0183 rcu_read_unlock();
0184 return d;
0185 }
0186
0187 struct nfs4_deviceid_node *
0188 nfs4_find_get_deviceid(struct nfs_server *server,
0189 const struct nfs4_deviceid *id, const struct cred *cred,
0190 gfp_t gfp_mask)
0191 {
0192 long hash = nfs4_deviceid_hash(id);
0193 struct nfs4_deviceid_node *d, *new;
0194
0195 d = __nfs4_find_get_deviceid(server, id, hash);
0196 if (d)
0197 goto found;
0198
0199 new = nfs4_get_device_info(server, id, cred, gfp_mask);
0200 if (!new) {
0201 trace_nfs4_find_deviceid(server, id, -ENOENT);
0202 return new;
0203 }
0204
0205 spin_lock(&nfs4_deviceid_lock);
0206 d = __nfs4_find_get_deviceid(server, id, hash);
0207 if (d) {
0208 spin_unlock(&nfs4_deviceid_lock);
0209 server->pnfs_curr_ld->free_deviceid_node(new);
0210 } else {
0211 atomic_inc(&new->ref);
0212 hlist_add_head_rcu(&new->node, &nfs4_deviceid_cache[hash]);
0213 spin_unlock(&nfs4_deviceid_lock);
0214 d = new;
0215 }
0216 found:
0217 trace_nfs4_find_deviceid(server, id, 0);
0218 return d;
0219 }
0220 EXPORT_SYMBOL_GPL(nfs4_find_get_deviceid);
0221
0222
0223
0224
0225
0226
0227
0228
0229
0230 void
0231 nfs4_delete_deviceid(const struct pnfs_layoutdriver_type *ld,
0232 const struct nfs_client *clp, const struct nfs4_deviceid *id)
0233 {
0234 struct nfs4_deviceid_node *d;
0235
0236 spin_lock(&nfs4_deviceid_lock);
0237 rcu_read_lock();
0238 d = _lookup_deviceid(ld, clp, id, nfs4_deviceid_hash(id));
0239 rcu_read_unlock();
0240 if (!d) {
0241 spin_unlock(&nfs4_deviceid_lock);
0242 return;
0243 }
0244 hlist_del_init_rcu(&d->node);
0245 clear_bit(NFS_DEVICEID_NOCACHE, &d->flags);
0246 spin_unlock(&nfs4_deviceid_lock);
0247
0248
0249 nfs4_put_deviceid_node(d);
0250 }
0251 EXPORT_SYMBOL_GPL(nfs4_delete_deviceid);
0252
0253 void
0254 nfs4_init_deviceid_node(struct nfs4_deviceid_node *d, struct nfs_server *server,
0255 const struct nfs4_deviceid *id)
0256 {
0257 INIT_HLIST_NODE(&d->node);
0258 INIT_HLIST_NODE(&d->tmpnode);
0259 d->ld = server->pnfs_curr_ld;
0260 d->nfs_client = server->nfs_client;
0261 d->flags = 0;
0262 d->deviceid = *id;
0263 atomic_set(&d->ref, 1);
0264 }
0265 EXPORT_SYMBOL_GPL(nfs4_init_deviceid_node);
0266
0267
0268
0269
0270
0271
0272
0273
0274
0275
0276
0277 bool
0278 nfs4_put_deviceid_node(struct nfs4_deviceid_node *d)
0279 {
0280 if (test_bit(NFS_DEVICEID_NOCACHE, &d->flags)) {
0281 if (atomic_add_unless(&d->ref, -1, 2))
0282 return false;
0283 nfs4_delete_deviceid(d->ld, d->nfs_client, &d->deviceid);
0284 }
0285 if (!atomic_dec_and_test(&d->ref))
0286 return false;
0287 trace_nfs4_deviceid_free(d->nfs_client, &d->deviceid);
0288 d->ld->free_deviceid_node(d);
0289 return true;
0290 }
0291 EXPORT_SYMBOL_GPL(nfs4_put_deviceid_node);
0292
0293 void
0294 nfs4_mark_deviceid_available(struct nfs4_deviceid_node *node)
0295 {
0296 if (test_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags)) {
0297 clear_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags);
0298 smp_mb__after_atomic();
0299 }
0300 }
0301 EXPORT_SYMBOL_GPL(nfs4_mark_deviceid_available);
0302
0303 void
0304 nfs4_mark_deviceid_unavailable(struct nfs4_deviceid_node *node)
0305 {
0306 node->timestamp_unavailable = jiffies;
0307 smp_mb__before_atomic();
0308 set_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags);
0309 smp_mb__after_atomic();
0310 }
0311 EXPORT_SYMBOL_GPL(nfs4_mark_deviceid_unavailable);
0312
0313 bool
0314 nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node *node)
0315 {
0316 if (test_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags)) {
0317 unsigned long start, end;
0318
0319 end = jiffies;
0320 start = end - PNFS_DEVICE_RETRY_TIMEOUT;
0321 if (time_in_range(node->timestamp_unavailable, start, end))
0322 return true;
0323 clear_bit(NFS_DEVICEID_UNAVAILABLE, &node->flags);
0324 smp_mb__after_atomic();
0325 }
0326 return false;
0327 }
0328 EXPORT_SYMBOL_GPL(nfs4_test_deviceid_unavailable);
0329
0330 static void
0331 _deviceid_purge_client(const struct nfs_client *clp, long hash)
0332 {
0333 struct nfs4_deviceid_node *d;
0334 HLIST_HEAD(tmp);
0335
0336 spin_lock(&nfs4_deviceid_lock);
0337 rcu_read_lock();
0338 hlist_for_each_entry_rcu(d, &nfs4_deviceid_cache[hash], node)
0339 if (d->nfs_client == clp && atomic_read(&d->ref)) {
0340 hlist_del_init_rcu(&d->node);
0341 hlist_add_head(&d->tmpnode, &tmp);
0342 clear_bit(NFS_DEVICEID_NOCACHE, &d->flags);
0343 }
0344 rcu_read_unlock();
0345 spin_unlock(&nfs4_deviceid_lock);
0346
0347 if (hlist_empty(&tmp))
0348 return;
0349
0350 while (!hlist_empty(&tmp)) {
0351 d = hlist_entry(tmp.first, struct nfs4_deviceid_node, tmpnode);
0352 hlist_del(&d->tmpnode);
0353 nfs4_put_deviceid_node(d);
0354 }
0355 }
0356
0357 void
0358 nfs4_deviceid_purge_client(const struct nfs_client *clp)
0359 {
0360 long h;
0361
0362 if (!(clp->cl_exchange_flags & EXCHGID4_FLAG_USE_PNFS_MDS))
0363 return;
0364 for (h = 0; h < NFS4_DEVICE_ID_HASH_SIZE; h++)
0365 _deviceid_purge_client(clp, h);
0366 }
0367
0368
0369
0370
0371 void
0372 nfs4_deviceid_mark_client_invalid(struct nfs_client *clp)
0373 {
0374 struct nfs4_deviceid_node *d;
0375 int i;
0376
0377 rcu_read_lock();
0378 for (i = 0; i < NFS4_DEVICE_ID_HASH_SIZE; i ++){
0379 hlist_for_each_entry_rcu(d, &nfs4_deviceid_cache[i], node)
0380 if (d->nfs_client == clp)
0381 set_bit(NFS_DEVICEID_INVALID, &d->flags);
0382 }
0383 rcu_read_unlock();
0384 }