int32_t dec_session_ref(nfs41_session_t *session) { int i; int32_t refcnt = atomic_dec_int32_t(&session->refcount); if (refcnt == 0) { /* Unlink the session from the client's list of sessions */ PTHREAD_MUTEX_lock(&session->clientid_record->cid_mutex); glist_del(&session->session_link); PTHREAD_MUTEX_unlock(&session->clientid_record->cid_mutex); /* Decrement our reference to the clientid record */ dec_client_id_ref(session->clientid_record); /* Destroy this session's mutexes and condition variable */ for (i = 0; i < NFS41_NB_SLOTS; i++) PTHREAD_MUTEX_destroy(&session->slots[i].lock); PTHREAD_COND_destroy(&session->cb_cond); PTHREAD_MUTEX_destroy(&session->cb_mutex); /* Destroy the session's back channel (if any) */ if (session->flags & session_bc_up) nfs_rpc_destroy_chan(&session->cb_chan); /* Free the memory for the session */ pool_free(nfs41_session_pool, session); } return refcnt; }
/** * @brief Deep-free a per-connection (TCP) duplicate request cache * * @param[in] drc The DRC to dispose * * Assumes that the DRC has been allocated from the tcp_drc_pool. */ static inline void free_tcp_drc(drc_t *drc) { if (drc->xt.tree[0].cache) gsh_free(drc->xt.tree[0].cache); PTHREAD_MUTEX_destroy(&drc->mtx); LogFullDebug(COMPONENT_DUPREQ, "free TCP drc %p", drc); pool_free(tcp_drc_pool, drc); }
/** * @brief Deep-free a duplicate request cache entry. * * If the entry has processed request data, the corresponding free * function is called on the result. The cache entry is then returned * to the dupreq_pool. */ static inline void nfs_dupreq_free_dupreq(dupreq_entry_t *dv) { const nfs_function_desc_t *func; if (dv->res) { func = nfs_dupreq_func(dv); func->free_function(dv->res); free_nfs_res(dv->res); } PTHREAD_MUTEX_destroy(&dv->mtx); pool_free(dupreq_pool, dv); }
/** * @brief Free an NSM client * * @param[in] client Client to free */ void free_nsm_client(state_nsm_client_t *client) { if (client->ssc_nlm_caller_name != NULL) gsh_free(client->ssc_nlm_caller_name); if (client->ssc_client != NULL) put_gsh_client(client->ssc_client); PTHREAD_MUTEX_destroy(&client->ssc_mutex); gsh_free(client); }
/** * @brief Get an NSM client * * @param[in] care Care status * @param[in] xprt RPC transport * @param[in] caller_name Caller name * * @return NSM client or NULL. */ state_nsm_client_t *get_nsm_client(care_t care, SVCXPRT *xprt, char *caller_name) { state_nsm_client_t key; state_nsm_client_t *pclient; char str[LOG_BUFF_LEN]; struct display_buffer dspbuf = {sizeof(str), str, str}; struct hash_latch latch; hash_error_t rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; if (caller_name == NULL) return NULL; memset(&key, 0, sizeof(key)); if (nfs_param.core_param.nsm_use_caller_name) { key.ssc_nlm_caller_name_len = strlen(caller_name); if (key.ssc_nlm_caller_name_len > LM_MAXSTRLEN) return NULL; key.ssc_nlm_caller_name = caller_name; } else if (op_ctx->client == NULL) { LogCrit(COMPONENT_STATE, "No gsh_client for caller_name %s", caller_name); return NULL; } else { key.ssc_nlm_caller_name = op_ctx->client->hostaddr_str; key.ssc_nlm_caller_name_len = strlen(key.ssc_nlm_caller_name); key.ssc_client = op_ctx->client; } if (isFullDebug(COMPONENT_STATE)) { display_nsm_client(&dspbuf, &key); LogFullDebug(COMPONENT_STATE, "Find {%s}", str); } buffkey.addr = &key; buffkey.len = sizeof(key); rc = hashtable_getlatch(ht_nsm_client, &buffkey, &buffval, true, &latch); /* If we found it, return it */ if (rc == HASHTABLE_SUCCESS) { pclient = buffval.addr; /* Return the found NSM Client */ if (isFullDebug(COMPONENT_STATE)) { display_nsm_client(&dspbuf, pclient); LogFullDebug(COMPONENT_STATE, "Found {%s}", str); } /* Increment refcount under hash latch. * This prevents dec ref from removing this entry from hash * if a race occurs. */ inc_nsm_client_ref(pclient); hashtable_releaselatched(ht_nsm_client, &latch); if (care == CARE_MONITOR && !nsm_monitor(pclient)) { dec_nsm_client_ref(pclient); pclient = NULL; } return pclient; } /* An error occurred, return NULL */ if (rc != HASHTABLE_ERROR_NO_SUCH_KEY) { display_nsm_client(&dspbuf, &key); LogCrit(COMPONENT_STATE, "Error %s, could not find {%s}", hash_table_err_to_str(rc), str); return NULL; } /* Not found, but we don't care, return NULL */ if (care == CARE_NOT) { /* Return the found NSM Client */ if (isFullDebug(COMPONENT_STATE)) { display_nsm_client(&dspbuf, &key); LogFullDebug(COMPONENT_STATE, "Ignoring {%s}", str); } hashtable_releaselatched(ht_nsm_client, &latch); return NULL; } pclient = gsh_malloc(sizeof(*pclient)); if (pclient == NULL) { display_nsm_client(&dspbuf, &key); LogCrit(COMPONENT_STATE, "No memory for {%s}", str); return NULL; } /* Copy everything over */ memcpy(pclient, &key, sizeof(key)); PTHREAD_MUTEX_init(&pclient->ssc_mutex, NULL); pclient->ssc_nlm_caller_name = gsh_strdup(key.ssc_nlm_caller_name); if (pclient->ssc_nlm_caller_name == NULL) { /* Discard the created client */ PTHREAD_MUTEX_destroy(&pclient->ssc_mutex); free_nsm_client(pclient); return NULL; } glist_init(&pclient->ssc_lock_list); glist_init(&pclient->ssc_share_list); pclient->ssc_refcount = 1; if (op_ctx->client != NULL) { pclient->ssc_client = op_ctx->client; inc_gsh_client_refcount(op_ctx->client); } if (isFullDebug(COMPONENT_STATE)) { display_nsm_client(&dspbuf, pclient); LogFullDebug(COMPONENT_STATE, "New {%s}", str); } buffkey.addr = pclient; buffkey.len = sizeof(*pclient); buffval.addr = pclient; buffval.len = sizeof(*pclient); rc = hashtable_setlatched(ht_nsm_client, &buffval, &buffval, &latch, false, NULL, NULL); /* An error occurred, return NULL */ if (rc != HASHTABLE_SUCCESS) { display_nsm_client(&dspbuf, pclient); LogCrit(COMPONENT_STATE, "Error %s, inserting {%s}", hash_table_err_to_str(rc), str); PTHREAD_MUTEX_destroy(&pclient->ssc_mutex); free_nsm_client(pclient); return NULL; } if (care != CARE_MONITOR || nsm_monitor(pclient)) return pclient; /* Failed to monitor, release client reference * and almost certainly remove it from the hash table. */ dec_nsm_client_ref(pclient); return NULL; }
/** * @brief adds a new state to a file * * This version of the function does not take the state lock on the * entry. It exists to allow callers to integrate state into a larger * operation. * * The caller may have already allocated a state, in which case state * need not be NULL. * * @note state_lock MUST be held for write * * @param[in,out] obj file to operate on * @param[in] state_type State to be defined * @param[in] state_data Data related to this state * @param[in] owner_input Related open_owner * @param[in,out] state The new state * @param[in] refer Reference to compound creating state * * @return Operation status */ state_status_t state_add_impl(struct fsal_obj_handle *obj, enum state_type state_type, union state_data *state_data, state_owner_t *owner_input, state_t **state, struct state_refer *refer) { state_t *pnew_state = *state; struct state_hdl *ostate = obj->state_hdl; char str[DISPLAY_STATEID_OTHER_SIZE]; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; bool got_export_ref = false; state_status_t status = 0; bool mutex_init = false; struct state_t *openstate = NULL; struct gsh_buffdesc fh_desc; if (isFullDebug(COMPONENT_STATE) && pnew_state != NULL) { display_stateid(&dspbuf, pnew_state); LogFullDebug(COMPONENT_STATE, "pnew_state=%s", str); display_reset_buffer(&dspbuf); } /* Attempt to get a reference to the export. */ if (!export_ready(op_ctx->ctx_export)) { /* If we could not get a reference, return stale. * Release attr_lock */ LogDebug(COMPONENT_STATE, "Stale export"); status = STATE_ESTALE; goto errout; } get_gsh_export_ref(op_ctx->ctx_export); got_export_ref = true; if (pnew_state == NULL) { if (state_type == STATE_TYPE_LOCK) openstate = state_data->lock.openstate; pnew_state = op_ctx->fsal_export->exp_ops.alloc_state( op_ctx->fsal_export, state_type, openstate); } PTHREAD_MUTEX_init(&pnew_state->state_mutex, NULL); mutex_init = true; /* Add the stateid.other, this will increment cid_stateid_counter */ nfs4_BuildStateId_Other(owner_input->so_owner.so_nfs4_owner. so_clientrec, pnew_state->stateid_other); /* Set the type and data for this state */ memcpy(&(pnew_state->state_data), state_data, sizeof(*state_data)); pnew_state->state_type = state_type; pnew_state->state_seqid = 0; /* will be incremented to 1 later */ pnew_state->state_refcount = 2; /* sentinel plus returned ref */ if (refer) pnew_state->state_refer = *refer; if (isFullDebug(COMPONENT_STATE)) { display_stateid_other(&dspbuf, pnew_state->stateid_other); str_valid = true; LogFullDebug(COMPONENT_STATE, "About to call nfs4_State_Set for %s", str); } glist_init(&pnew_state->state_list); /* We need to initialize state_owner, state_export, and state_obj now so * that the state can be indexed by owner/entry. We don't insert into * lists and take references yet since no one else can see this state * until we are completely done since we hold the state_lock. Might as * well grab export now also... */ pnew_state->state_export = op_ctx->ctx_export; pnew_state->state_owner = owner_input; fh_desc.addr = &pnew_state->state_obj.digest; fh_desc.len = sizeof(pnew_state->state_obj.digest); obj->obj_ops.handle_digest(obj, FSAL_DIGEST_NFSV4, &fh_desc); pnew_state->state_obj.len = fh_desc.len; /* Add the state to the related hashtable */ if (!nfs4_State_Set(pnew_state)) { if (!str_valid) display_stateid_other(&dspbuf, pnew_state->stateid_other); LogCrit(COMPONENT_STATE, "Can't create a new state id %s for the obj %p (F)", str, obj); /* Return STATE_MALLOC_ERROR since most likely the * nfs4_State_Set failed to allocate memory. */ status = STATE_MALLOC_ERROR; goto errout; } /* Each of the following blocks takes the state_mutex and releases it * because we always want state_mutex to be the last lock taken. * * NOTE: We don't have to worry about state_del/state_del_locked being * called in the midst of things because the state_lock is held. */ /* Attach this to an export */ PTHREAD_RWLOCK_wrlock(&op_ctx->ctx_export->lock); PTHREAD_MUTEX_lock(&pnew_state->state_mutex); glist_add_tail(&op_ctx->ctx_export->exp_state_list, &pnew_state->state_export_list); PTHREAD_MUTEX_unlock(&pnew_state->state_mutex); PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); /* Add state to list for file */ PTHREAD_MUTEX_lock(&pnew_state->state_mutex); glist_add_tail(&ostate->file.list_of_states, &pnew_state->state_list); PTHREAD_MUTEX_unlock(&pnew_state->state_mutex); /* Add state to list for owner */ PTHREAD_MUTEX_lock(&owner_input->so_mutex); PTHREAD_MUTEX_lock(&pnew_state->state_mutex); inc_state_owner_ref(owner_input); glist_add_tail(&owner_input->so_owner.so_nfs4_owner.so_state_list, &pnew_state->state_owner_list); PTHREAD_MUTEX_unlock(&pnew_state->state_mutex); PTHREAD_MUTEX_unlock(&owner_input->so_mutex); #ifdef DEBUG_SAL PTHREAD_MUTEX_lock(&all_state_v4_mutex); glist_add_tail(&state_v4_all, &pnew_state->state_list_all); PTHREAD_MUTEX_unlock(&all_state_v4_mutex); #endif if (pnew_state->state_type == STATE_TYPE_DELEG && pnew_state->state_data.deleg.sd_type == OPEN_DELEGATE_WRITE) ostate->file.write_delegated = true; /* Copy the result */ *state = pnew_state; if (str_valid) LogFullDebug(COMPONENT_STATE, "Add State: %p: %s", pnew_state, str); /* Regular exit */ status = STATE_SUCCESS; return status; errout: if (mutex_init) PTHREAD_MUTEX_destroy(&pnew_state->state_mutex); if (pnew_state != NULL) { /* Make sure the new state is closed (may have been passed in * with file open). */ (void) obj->obj_ops.close2(obj, pnew_state); pnew_state->state_exp->exp_ops.free_state(pnew_state); } if (got_export_ref) put_gsh_export(op_ctx->ctx_export); *state = NULL; return status; } /* state_add */