diff options
Diffstat (limited to 'fs/nfs/pnfs_dev.c')
-rw-r--r-- | fs/nfs/pnfs_dev.c | 95 |
1 files changed, 81 insertions, 14 deletions
diff --git a/fs/nfs/pnfs_dev.c b/fs/nfs/pnfs_dev.c index 64a4b85c7dbc..8fd3839df299 100644 --- a/fs/nfs/pnfs_dev.c +++ b/fs/nfs/pnfs_dev.c | |||
@@ -66,6 +66,23 @@ nfs4_deviceid_hash(const struct nfs4_deviceid *id) | |||
66 | return x & NFS4_DEVICE_ID_HASH_MASK; | 66 | return x & NFS4_DEVICE_ID_HASH_MASK; |
67 | } | 67 | } |
68 | 68 | ||
69 | static struct nfs4_deviceid_node * | ||
70 | _lookup_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id, | ||
71 | long hash) | ||
72 | { | ||
73 | struct nfs4_deviceid_node *d; | ||
74 | struct hlist_node *n; | ||
75 | |||
76 | hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[hash], node) | ||
77 | if (d->nfs_client == clp && !memcmp(&d->deviceid, id, sizeof(*id))) { | ||
78 | if (atomic_read(&d->ref)) | ||
79 | return d; | ||
80 | else | ||
81 | continue; | ||
82 | } | ||
83 | return NULL; | ||
84 | } | ||
85 | |||
69 | /* | 86 | /* |
70 | * Lookup a deviceid in cache and get a reference count on it if found | 87 | * Lookup a deviceid in cache and get a reference count on it if found |
71 | * | 88 | * |
@@ -73,26 +90,76 @@ nfs4_deviceid_hash(const struct nfs4_deviceid *id) | |||
73 | * @id deviceid to look up | 90 | * @id deviceid to look up |
74 | */ | 91 | */ |
75 | struct nfs4_deviceid_node * | 92 | struct nfs4_deviceid_node * |
93 | _find_get_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id, | ||
94 | long hash) | ||
95 | { | ||
96 | struct nfs4_deviceid_node *d; | ||
97 | |||
98 | rcu_read_lock(); | ||
99 | d = _lookup_deviceid(clp, id, hash); | ||
100 | if (d && !atomic_inc_not_zero(&d->ref)) | ||
101 | d = NULL; | ||
102 | rcu_read_unlock(); | ||
103 | return d; | ||
104 | } | ||
105 | |||
106 | struct nfs4_deviceid_node * | ||
76 | nfs4_find_get_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id) | 107 | nfs4_find_get_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id) |
77 | { | 108 | { |
109 | return _find_get_deviceid(clp, id, nfs4_deviceid_hash(id)); | ||
110 | } | ||
111 | EXPORT_SYMBOL_GPL(nfs4_find_get_deviceid); | ||
112 | |||
113 | /* | ||
114 | * Unhash and put deviceid | ||
115 | * | ||
116 | * @clp nfs_client associated with deviceid | ||
117 | * @id the deviceid to unhash | ||
118 | * | ||
119 | * @ret the unhashed node, if found and dereferenced to zero, NULL otherwise. | ||
120 | */ | ||
121 | struct nfs4_deviceid_node * | ||
122 | nfs4_unhash_put_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id) | ||
123 | { | ||
78 | struct nfs4_deviceid_node *d; | 124 | struct nfs4_deviceid_node *d; |
79 | struct hlist_node *n; | ||
80 | long hash = nfs4_deviceid_hash(id); | ||
81 | 125 | ||
126 | spin_lock(&nfs4_deviceid_lock); | ||
82 | rcu_read_lock(); | 127 | rcu_read_lock(); |
83 | hlist_for_each_entry_rcu(d, n, &nfs4_deviceid_cache[hash], node) { | 128 | d = _lookup_deviceid(clp, id, nfs4_deviceid_hash(id)); |
84 | if (d->nfs_client == clp && !memcmp(&d->deviceid, id, sizeof(*id))) { | ||
85 | if (!atomic_inc_not_zero(&d->ref)) | ||
86 | goto fail; | ||
87 | rcu_read_unlock(); | ||
88 | return d; | ||
89 | } | ||
90 | } | ||
91 | fail: | ||
92 | rcu_read_unlock(); | 129 | rcu_read_unlock(); |
130 | if (!d) { | ||
131 | spin_unlock(&nfs4_deviceid_lock); | ||
132 | return NULL; | ||
133 | } | ||
134 | hlist_del_init_rcu(&d->node); | ||
135 | spin_unlock(&nfs4_deviceid_lock); | ||
136 | synchronize_rcu(); | ||
137 | |||
138 | /* balance the initial ref set in pnfs_insert_deviceid */ | ||
139 | if (atomic_dec_and_test(&d->ref)) | ||
140 | return d; | ||
141 | |||
93 | return NULL; | 142 | return NULL; |
94 | } | 143 | } |
95 | EXPORT_SYMBOL_GPL(nfs4_find_get_deviceid); | 144 | EXPORT_SYMBOL_GPL(nfs4_unhash_put_deviceid); |
145 | |||
146 | /* | ||
147 | * Delete a deviceid from cache | ||
148 | * | ||
149 | * @clp struct nfs_client qualifying the deviceid | ||
150 | * @id deviceid to delete | ||
151 | */ | ||
152 | void | ||
153 | nfs4_delete_deviceid(const struct nfs_client *clp, const struct nfs4_deviceid *id) | ||
154 | { | ||
155 | struct nfs4_deviceid_node *d; | ||
156 | |||
157 | d = nfs4_unhash_put_deviceid(clp, id); | ||
158 | if (!d) | ||
159 | return; | ||
160 | d->ld->free_deviceid_node(d); | ||
161 | } | ||
162 | EXPORT_SYMBOL_GPL(nfs4_delete_deviceid); | ||
96 | 163 | ||
97 | void | 164 | void |
98 | nfs4_init_deviceid_node(struct nfs4_deviceid_node *d, | 165 | nfs4_init_deviceid_node(struct nfs4_deviceid_node *d, |
@@ -126,13 +193,13 @@ nfs4_insert_deviceid_node(struct nfs4_deviceid_node *new) | |||
126 | long hash; | 193 | long hash; |
127 | 194 | ||
128 | spin_lock(&nfs4_deviceid_lock); | 195 | spin_lock(&nfs4_deviceid_lock); |
129 | d = nfs4_find_get_deviceid(new->nfs_client, &new->deviceid); | 196 | hash = nfs4_deviceid_hash(&new->deviceid); |
197 | d = _find_get_deviceid(new->nfs_client, &new->deviceid, hash); | ||
130 | if (d) { | 198 | if (d) { |
131 | spin_unlock(&nfs4_deviceid_lock); | 199 | spin_unlock(&nfs4_deviceid_lock); |
132 | return d; | 200 | return d; |
133 | } | 201 | } |
134 | 202 | ||
135 | hash = nfs4_deviceid_hash(&new->deviceid); | ||
136 | hlist_add_head_rcu(&new->node, &nfs4_deviceid_cache[hash]); | 203 | hlist_add_head_rcu(&new->node, &nfs4_deviceid_cache[hash]); |
137 | spin_unlock(&nfs4_deviceid_lock); | 204 | spin_unlock(&nfs4_deviceid_lock); |
138 | 205 | ||