diff options
Diffstat (limited to 'fs/nfs/callback_proc.c')
-rw-r--r-- | fs/nfs/callback_proc.c | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index f7e83e23cf9f..b7da1f54da68 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c | |||
@@ -101,3 +101,130 @@ out: | |||
101 | dprintk("%s: exit with status = %d\n", __func__, ntohl(res)); | 101 | dprintk("%s: exit with status = %d\n", __func__, ntohl(res)); |
102 | return res; | 102 | return res; |
103 | } | 103 | } |
104 | |||
105 | #if defined(CONFIG_NFS_V4_1) | ||
106 | |||
107 | /* | ||
108 | * Validate the sequenceID sent by the server. | ||
109 | * Return success if the sequenceID is one more than what we last saw on | ||
110 | * this slot, accounting for wraparound. Increments the slot's sequence. | ||
111 | * | ||
112 | * We don't yet implement a duplicate request cache, so at this time | ||
113 | * we will log replays, and process them as if we had not seen them before, | ||
114 | * but we don't bump the sequence in the slot. Not too worried about it, | ||
115 | * since we only currently implement idempotent callbacks anyway. | ||
116 | * | ||
117 | * We have a single slot backchannel at this time, so we don't bother | ||
118 | * checking the used_slots bit array on the table. The lower layer guarantees | ||
119 | * a single outstanding callback request at a time. | ||
120 | */ | ||
121 | static int | ||
122 | validate_seqid(struct nfs4_slot_table *tbl, u32 slotid, u32 seqid) | ||
123 | { | ||
124 | struct nfs4_slot *slot; | ||
125 | |||
126 | dprintk("%s enter. slotid %d seqid %d\n", | ||
127 | __func__, slotid, seqid); | ||
128 | |||
129 | if (slotid > NFS41_BC_MAX_CALLBACKS) | ||
130 | return htonl(NFS4ERR_BADSLOT); | ||
131 | |||
132 | slot = tbl->slots + slotid; | ||
133 | dprintk("%s slot table seqid: %d\n", __func__, slot->seq_nr); | ||
134 | |||
135 | /* Normal */ | ||
136 | if (likely(seqid == slot->seq_nr + 1)) { | ||
137 | slot->seq_nr++; | ||
138 | return htonl(NFS4_OK); | ||
139 | } | ||
140 | |||
141 | /* Replay */ | ||
142 | if (seqid == slot->seq_nr) { | ||
143 | dprintk("%s seqid %d is a replay - no DRC available\n", | ||
144 | __func__, seqid); | ||
145 | return htonl(NFS4_OK); | ||
146 | } | ||
147 | |||
148 | /* Wraparound */ | ||
149 | if (seqid == 1 && (slot->seq_nr + 1) == 0) { | ||
150 | slot->seq_nr = 1; | ||
151 | return htonl(NFS4_OK); | ||
152 | } | ||
153 | |||
154 | /* Misordered request */ | ||
155 | return htonl(NFS4ERR_SEQ_MISORDERED); | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * Returns a pointer to a held 'struct nfs_client' that matches the server's | ||
160 | * address, major version number, and session ID. It is the caller's | ||
161 | * responsibility to release the returned reference. | ||
162 | * | ||
163 | * Returns NULL if there are no connections with sessions, or if no session | ||
164 | * matches the one of interest. | ||
165 | */ | ||
166 | static struct nfs_client *find_client_with_session( | ||
167 | const struct sockaddr *addr, u32 nfsversion, | ||
168 | struct nfs4_sessionid *sessionid) | ||
169 | { | ||
170 | struct nfs_client *clp; | ||
171 | |||
172 | clp = nfs_find_client(addr, 4); | ||
173 | if (clp == NULL) | ||
174 | return NULL; | ||
175 | |||
176 | do { | ||
177 | struct nfs_client *prev = clp; | ||
178 | |||
179 | if (clp->cl_session != NULL) { | ||
180 | if (memcmp(clp->cl_session->sess_id.data, | ||
181 | sessionid->data, | ||
182 | NFS4_MAX_SESSIONID_LEN) == 0) { | ||
183 | /* Returns a held reference to clp */ | ||
184 | return clp; | ||
185 | } | ||
186 | } | ||
187 | clp = nfs_find_client_next(prev); | ||
188 | nfs_put_client(prev); | ||
189 | } while (clp != NULL); | ||
190 | |||
191 | return NULL; | ||
192 | } | ||
193 | |||
194 | /* FIXME: referring calls should be processed */ | ||
195 | unsigned nfs4_callback_sequence(struct cb_sequenceargs *args, | ||
196 | struct cb_sequenceres *res) | ||
197 | { | ||
198 | struct nfs_client *clp; | ||
199 | int i, status; | ||
200 | |||
201 | for (i = 0; i < args->csa_nrclists; i++) | ||
202 | kfree(args->csa_rclists[i].rcl_refcalls); | ||
203 | kfree(args->csa_rclists); | ||
204 | |||
205 | status = htonl(NFS4ERR_BADSESSION); | ||
206 | clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid); | ||
207 | if (clp == NULL) | ||
208 | goto out; | ||
209 | |||
210 | status = validate_seqid(&clp->cl_session->bc_slot_table, | ||
211 | args->csa_slotid, args->csa_sequenceid); | ||
212 | if (status) | ||
213 | goto out_putclient; | ||
214 | |||
215 | memcpy(&res->csr_sessionid, &args->csa_sessionid, | ||
216 | sizeof(res->csr_sessionid)); | ||
217 | res->csr_sequenceid = args->csa_sequenceid; | ||
218 | res->csr_slotid = args->csa_slotid; | ||
219 | res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; | ||
220 | res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; | ||
221 | |||
222 | out_putclient: | ||
223 | nfs_put_client(clp); | ||
224 | out: | ||
225 | dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); | ||
226 | res->csr_status = status; | ||
227 | return res->csr_status; | ||
228 | } | ||
229 | |||
230 | #endif /* CONFIG_NFS_V4_1 */ | ||