1    	/*
2    	 * vim:noexpandtab:shiftwidth=8:tabstop=8:
3    	 *
4    	 * Copyright CEA/DAM/DIF  (2008)
5    	 * contributeur : Philippe DENIEL   philippe.deniel@cea.fr
6    	 *                Thomas LEIBOVICI  thomas.leibovici@cea.fr
7    	 *
8    	 *
9    	 * This program is free software; you can redistribute it and/or
10   	 * modify it under the terms of the GNU Lesser General Public License
11   	 * as published by the Free Software Foundation; either version 3 of
12   	 * the License, or (at your option) any later version.
13   	 *
14   	 * This program is distributed in the hope that it will be useful, but
15   	 * WITHOUT ANY WARRANTY; without even the implied warranty of
16   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   	 * Lesser General Public License for more details.
18   	 *
19   	 * You should have received a copy of the GNU Lesser General Public
20   	 * License along with this library; if not, write to the Free Software
21   	 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22   	 * 02110-1301 USA
23   	 *
24   	 * ---------------------------------------
25   	 */
26   	
27   	/**
28   	 * @file nfs4_op_sequence.c
29   	 * @brief Routines used for managing the NFS4_OP_SEQUENCE operation
30   	 */
31   	
32   	#include "config.h"
33   	#include "fsal.h"
34   	#include "sal_functions.h"
35   	#include "nfs_rpc_callback.h"
36   	#include "nfs_convert.h"
37   	#include "nfs_proto_tools.h"
38   	#include "nfs_proto_functions.h"
39   	
40   	/**
41   	 * @brief the NFS4_OP_SEQUENCE operation
42   	 *
43   	 * @param[in]     op   nfs4_op arguments
44   	 * @param[in,out] data Compound request's data
45   	 * @param[out]    resp nfs4_op results
46   	 *
47   	 * @return per RFC5661, p. 374
48   	 *
49   	 * @see nfs4_Compound
50   	 *
51   	 */
52   	enum nfs_req_result nfs4_op_sequence(struct nfs_argop4 *op,
53   					     compound_data_t *data,
54   					     struct nfs_resop4 *resp)
55   	{
56   		SEQUENCE4args * const arg_SEQUENCE4 = &op->nfs_argop4_u.opsequence;
57   		SEQUENCE4res * const res_SEQUENCE4 = &resp->nfs_resop4_u.opsequence;
58   		uint32_t slotid;
59   	
60   		nfs41_session_t *session;
61   		nfs41_session_slot_t *slot;
62   	
63   		resp->resop = NFS4_OP_SEQUENCE;
64   		res_SEQUENCE4->sr_status = NFS4_OK;
65   	
(1) Event cond_false: Condition "data->minorversion == 0", taking false branch.
66   		if (data->minorversion == 0) {
67   			res_SEQUENCE4->sr_status = NFS4ERR_INVAL;
68   			return NFS_REQ_ERROR;
(2) Event if_end: End of if statement.
69   		}
70   	
(3) Event cond_false: Condition "!nfs41_Session_Get_Pointer(arg_SEQUENCE4->sa_sessionid, &session)", taking false branch.
71   		if (!nfs41_Session_Get_Pointer(arg_SEQUENCE4->sa_sessionid, &session)) {
72   			res_SEQUENCE4->sr_status = NFS4ERR_BADSESSION;
73   			LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID,
74   				    "SEQUENCE returning status %s",
75   				    nfsstat4_to_str(res_SEQUENCE4->sr_status));
76   	
77   			return NFS_REQ_ERROR;
(4) Event if_end: End of if statement.
78   		}
79   	
80   		/* session->refcount +1 */
81   	
(5) Event cond_true: Condition "!!(component_log_level[COMPONENT_SESSIONS] >= NIV_DEBUG)", taking true branch.
(6) Event cond_true: Condition "!!(component_log_level[COMPONENT_SESSIONS] >= NIV_DEBUG)", taking true branch.
82   		LogDebug(COMPONENT_SESSIONS, "SEQUENCE session=%p", session);
83   	
84   		/* Check if lease is expired and reserve it */
(7) Event cond_true: Condition "rc == 0", taking true branch.
(8) Event cond_true: Condition "!!(component_log_level[COMPONENT_RW_LOCK] >= NIV_FULL_DEBUG)", taking true branch.
(9) Event cond_true: Condition "!!(component_log_level[COMPONENT_RW_LOCK] >= NIV_FULL_DEBUG)", taking true branch.
(10) Event if_fallthrough: Falling through to end of if statement.
(11) Event if_end: End of if statement.
85   		PTHREAD_MUTEX_lock(&session->clientid_record->cid_mutex);
86   	
(12) Event cond_false: Condition "!reserve_lease(session->clientid_record)", taking false branch.
87   		if (!reserve_lease(session->clientid_record)) {
88   			PTHREAD_MUTEX_unlock(&session->clientid_record->cid_mutex);
89   	
90   			dec_session_ref(session);
91   			res_SEQUENCE4->sr_status = NFS4ERR_EXPIRED;
92   			LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID,
93   				    "SEQUENCE returning status %s",
94   				    nfsstat4_to_str(res_SEQUENCE4->sr_status));
95   			return NFS_REQ_ERROR;
(13) Event if_end: End of if statement.
96   		}
97   	
98   		data->preserved_clientid = session->clientid_record;
99   	
(14) Event cond_true: Condition "rc == 0", taking true branch.
(15) Event cond_true: Condition "!!(component_log_level[COMPONENT_RW_LOCK] >= NIV_FULL_DEBUG)", taking true branch.
(16) Event cond_true: Condition "!!(component_log_level[COMPONENT_RW_LOCK] >= NIV_FULL_DEBUG)", taking true branch.
(17) Event if_fallthrough: Falling through to end of if statement.
(18) Event if_end: End of if statement.
100  		PTHREAD_MUTEX_unlock(&session->clientid_record->cid_mutex);
101  	
102  		slotid = arg_SEQUENCE4->sa_slotid;
103  	
104  		/* Check is slot is compliant with ca_maxrequests */
(19) Event cond_false: Condition "slotid >= session->fore_channel_attrs.ca_maxrequests", taking false branch.
105  		if (slotid >= session->fore_channel_attrs.ca_maxrequests) {
106  			dec_session_ref(session);
107  			res_SEQUENCE4->sr_status = NFS4ERR_BADSLOT;
108  			LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID,
109  				    "SEQUENCE returning status %s",
110  				    nfsstat4_to_str(res_SEQUENCE4->sr_status));
111  			return NFS_REQ_ERROR;
(20) Event if_end: End of if statement.
112  		}
113  	
114  		slot = &session->fc_slots[slotid];
115  	
116  		/* Serialize use of this slot. */
(21) Event lock: "pthread_mutex_lock" locks "slot->lock".
(22) Event cond_true: Condition "rc == 0", taking true branch.
(23) Event cond_true: Condition "!!(component_log_level[COMPONENT_RW_LOCK] >= NIV_FULL_DEBUG)", taking true branch.
(24) Event cond_true: Condition "!!(component_log_level[COMPONENT_RW_LOCK] >= NIV_FULL_DEBUG)", taking true branch.
(25) Event if_fallthrough: Falling through to end of if statement.
(26) Event if_end: End of if statement.
Also see events: [missing_unlock]
117  		PTHREAD_MUTEX_lock(&slot->lock);
118  	
(27) Event cond_false: Condition "slot->sequence + 1 != arg_SEQUENCE4->sa_sequenceid", taking false branch.
119  		if (slot->sequence + 1 != arg_SEQUENCE4->sa_sequenceid) {
120  			/* This sequence is NOT the next sequence */
121  			if (slot->sequence == arg_SEQUENCE4->sa_sequenceid) {
122  				/* But it is the previous sequence */
123  				if (slot->cached_result != NULL) {
124  					int32_t refcnt;
125  	
126  					/* And has a cached response.
127  					 * Replay operation through the DRC
128  					 * Take a reference to the slot cached response.
129  					 */
130  					data->slot = slot;
131  					refcnt = atomic_inc_int32_t(
132  						&slot->cached_result->res_refcnt);
133  	
134  					LogFullDebugAlt(COMPONENT_SESSIONS,
135  							COMPONENT_CLIENTID,
136  							"Use sesson slot %" PRIu32
137  							"=%p for replay refcnt=%"PRIi32,
138  							slotid,
139  							slot->cached_result,
140  							refcnt);
141  	
142  					PTHREAD_MUTEX_unlock(&slot->lock);
143  	
144  					dec_session_ref(session);
145  					res_SEQUENCE4->sr_status = NFS4_OK;
146  					return NFS_REQ_REPLAY;
147  				} else {
148  					/* Illegal replay */
149  					PTHREAD_MUTEX_unlock(&slot->lock);
150  	
151  					dec_session_ref(session);
152  					res_SEQUENCE4->sr_status =
153  					    NFS4ERR_RETRY_UNCACHED_REP;
154  					LogDebugAlt(COMPONENT_SESSIONS,
155  						    COMPONENT_CLIENTID,
156  						    "SEQUENCE returning status %s with slot seqid=%"
157  						    PRIu32" op seqid=%"PRIu32,
158  						    nfsstat4_to_str(
159  							res_SEQUENCE4->sr_status),
160  						    slot->sequence,
161  						    arg_SEQUENCE4->sa_sequenceid);
162  					return NFS_REQ_ERROR;
163  				}
164  			}
165  	
166  			PTHREAD_MUTEX_unlock(&slot->lock);
167  	
168  			dec_session_ref(session);
169  			res_SEQUENCE4->sr_status = NFS4ERR_SEQ_MISORDERED;
170  			LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID,
171  				    "SEQUENCE returning status %s",
172  				    nfsstat4_to_str(res_SEQUENCE4->sr_status));
173  			return NFS_REQ_ERROR;
(28) Event if_end: End of if statement.
174  		}
175  	
176  		/* Keep memory of the session in the COMPOUND's data */
177  		data->session = session;
178  	
179  		/* Record the sequenceid and slotid in the COMPOUND's data */
180  		data->sequence = arg_SEQUENCE4->sa_sequenceid;
181  		data->slotid = slotid;
182  	
183  		/* Update the sequence id within the slot */
184  		slot->sequence += 1;
185  	
186  		/* If the slot cache was in use, free it. */
187  		release_slot(slot);
188  	
189  		/* Set up the response */
190  		memcpy(res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_sessionid,
191  		       arg_SEQUENCE4->sa_sessionid, NFS4_SESSIONID_SIZE);
192  		res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_sequenceid = slot->sequence;
193  		res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_slotid = slotid;
194  		res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_highest_slotid =
195  		    session->nb_slots - 1;
196  		res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_target_highest_slotid =
197  		    session->fore_channel_attrs.ca_maxrequests - 1;
198  	
199  		res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_status_flags = 0;
200  	
(29) Event cond_true: Condition "nfs_rpc_get_chan(session->clientid_record, 0) == NULL", taking true branch.
201  		if (nfs_rpc_get_chan(session->clientid_record, 0) == NULL) {
202  			res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_status_flags |=
203  			    SEQ4_STATUS_CB_PATH_DOWN;
204  		}
205  	
206  		/* Remember if we are caching result and set position to cache. */
(30) Event cond_true: Condition "arg_SEQUENCE4->sa_cachethis", taking true branch.
207  		data->sa_cachethis = arg_SEQUENCE4->sa_cachethis;
208  		data->slot = slot;
209  	
(31) Event cond_true: Condition "!!(component_log_level[COMPONENT_SESSIONS] >= NIV_FULL_DEBUG)", taking true branch.
(32) Event cond_true: Condition "!!(component_log_level[COMPONENT_SESSIONS] >= NIV_FULL_DEBUG)", taking true branch.
(33) Event cond_true: Condition "arg_SEQUENCE4->sa_cachethis", taking true branch.
210  		LogFullDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID,
211  				"%s sesson slot %" PRIu32 "=%p for DRC",
212  				arg_SEQUENCE4->sa_cachethis
213  					? "Use"
214  					: "Don't use",
215  				slotid, data->slot);
216  	
217  		/* If we were successful, stash the clientid in the request
218  		 * context.
219  		 */
220  		op_ctx->clientid = &data->session->clientid;
221  	
222  		/* Now check the response size (we check here because we could't check
223  		 * in nfs4_Compound because the session wasn't established yet).
224  		 */
225  		res_SEQUENCE4->sr_status = check_resp_room(data, data->op_resp_size);
226  	
(34) Event cond_false: Condition "res_SEQUENCE4->sr_status != NFS4_OK", taking false branch.
227  		if (res_SEQUENCE4->sr_status != NFS4_OK) {
228  			/* Indicate the failed response size. */
229  			data->op_resp_size = sizeof(nfsstat4);
230  	
231  			PTHREAD_MUTEX_unlock(&slot->lock);
232  	
233  			dec_session_ref(session);
234  			data->session = NULL;
235  			return NFS_REQ_ERROR;
(35) Event if_end: End of if statement.
236  		}
237  	
238  		/* We keep the slot lock to serialize use of the slot. */
239  	
240  		(void) check_session_conn(session, data, true);
241  	
(36) Event missing_unlock: Returning without unlocking "slot->lock".
Also see events: [lock]
242  		return NFS_REQ_OK;
243  	}				/* nfs41_op_sequence */
244  	
245  	/**
246  	 * @brief Free memory allocated for SEQUENCE result
247  	 *
248  	 * This function frees any memory allocated for the result of the
249  	 * NFS4_OP_SEQUENCE operation.
250  	 *
251  	 * @param[in,out] resp nfs4_op results
252  	 */
253  	void nfs4_op_sequence_Free(nfs_resop4 *resp)
254  	{
255  		/* Nothing to be done */
256  	}
257