Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020 Project CHIP Authors
4 : * Copyright (c) 2016-2017 Nest Labs, Inc.
5 : * All rights reserved.
6 : *
7 : * Licensed under the Apache License, Version 2.0 (the "License");
8 : * you may not use this file except in compliance with the License.
9 : * You may obtain a copy of the License at
10 : *
11 : * http://www.apache.org/licenses/LICENSE-2.0
12 : *
13 : * Unless required by applicable law or agreed to in writing, software
14 : * distributed under the License is distributed on an "AS IS" BASIS,
15 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 : * See the License for the specific language governing permissions and
17 : * limitations under the License.
18 : */
19 :
20 : /**
21 : * @file
22 : *
23 : * @brief
24 : * Class declarations for a monotonically-increasing counter that is periodically
25 : * saved to the provided storage.
26 : */
27 :
28 : #pragma once
29 :
30 : #include <lib/core/CHIPEncoding.h>
31 : #include <lib/core/CHIPPersistentStorageDelegate.h>
32 : #include <lib/support/CHIPCounter.h>
33 : #include <lib/support/CodeUtils.h>
34 : #include <lib/support/DefaultStorageKeyAllocator.h>
35 :
36 : namespace chip {
37 :
38 : /**
39 : * @class PersistedCounter
40 : *
41 : * @brief
42 : * A class for managing a counter as an integer value intended to persist
43 : * across reboots.
44 : *
45 : * Counter values are always set to start at a multiple of a bootup value
46 : * "epoch".
47 : *
48 : * Example:
49 : *
50 : * - Assuming epoch is 100 via PersistedCounter::Init(_, 100) and GetValue +
51 : * AdvanceValue is called, we get the following outputs:
52 : *
53 : * - Output: 0, 1, 2, 3, 4 <reboot/reinit>
54 : * - Output: 100, 101, 102, 103, 104, 105 <reboot/reinit>
55 : * - Output: 200, 201, 202, ...., 299, 300, 301, 302 <reboot/reinit>
56 : * - Output: 400, 401 ...
57 : *
58 : */
59 : template <typename T>
60 : class PersistedCounter : public MonotonicallyIncreasingCounter<T>
61 : {
62 : public:
63 1 : PersistedCounter() : mKey(StorageKeyName::Uninitialized()) {}
64 1 : ~PersistedCounter() override {}
65 :
66 : /**
67 : * @brief
68 : * Initialize a PersistedCounter object.
69 : *
70 : * @param[in] aStorage the storage to use for the counter values.
71 : * @param[in] aKey the key to use for storing the counter values.
72 : * @param[in] aEpoch On bootup, values we vend will start at a
73 : * multiple of this parameter.
74 : *
75 : * @return CHIP_ERROR_INVALID_ARGUMENT if aStorageDelegate or aKey is NULL
76 : * CHIP_ERROR_INVALID_INTEGER_VALUE if aEpoch is 0.
77 : * CHIP_NO_ERROR otherwise
78 : */
79 1 : CHIP_ERROR Init(PersistentStorageDelegate * aStorage, StorageKeyName aKey, T aEpoch)
80 : {
81 1 : VerifyOrReturnError(aStorage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
82 1 : VerifyOrReturnError(aKey.IsInitialized(), CHIP_ERROR_INVALID_ARGUMENT);
83 1 : VerifyOrReturnError(aEpoch > 0, CHIP_ERROR_INVALID_INTEGER_VALUE);
84 :
85 1 : mStorage = aStorage;
86 1 : mKey = aKey;
87 1 : mEpoch = aEpoch;
88 :
89 : T startValue;
90 :
91 : // Read our previously-stored starting value.
92 1 : ReturnErrorOnFailure(ReadStartValue(startValue));
93 :
94 : #if CHIP_CONFIG_PERSISTED_COUNTER_DEBUG_LOGGING
95 : if constexpr (std::is_same_v<decltype(startValue), uint64_t>)
96 : {
97 : ChipLogDetail(EventLogging, "PersistedCounter::Init() aEpoch 0x" ChipLogFormatX64 " startValue 0x" ChipLogFormatX64,
98 : ChipLogValueX64(aEpoch), ChipLogValueX64(startValue));
99 : }
100 : else if (std::is_same_v<decltype(startValue), uint32_t>)
101 : {
102 : ChipLogDetail(EventLogging, "PersistedCounter::Init() aEpoch 0x%" PRIx32 " startValue 0x%" PRIx32,
103 : static_cast<uint32_t>(aEpoch), static_cast<uint32_t>(startValue));
104 : }
105 : #endif
106 :
107 1 : ReturnErrorOnFailure(PersistNextEpochStart(startValue + aEpoch));
108 :
109 : // This will set the starting value, after which we're ready.
110 1 : return MonotonicallyIncreasingCounter<T>::Init(startValue);
111 : }
112 :
113 : /**
114 : * @brief
115 : * Increment the counter and write to persisted storage if we've completed
116 : * the current epoch.
117 : *
118 : * @return Any error returned by a write to persisted storage.
119 : */
120 0 : CHIP_ERROR Advance() override
121 : {
122 0 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE);
123 0 : VerifyOrReturnError(mKey.IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
124 :
125 0 : ReturnErrorOnFailure(MonotonicallyIncreasingCounter<T>::Advance());
126 :
127 0 : if (MonotonicallyIncreasingCounter<T>::GetValue() >= mNextEpoch)
128 : {
129 : // Value advanced past the previously persisted "start point".
130 : // Ensure that a new starting point is persisted.
131 0 : ReturnErrorOnFailure(PersistNextEpochStart(mNextEpoch + mEpoch));
132 :
133 : // Advancing the epoch should have ensured that the current value
134 : // is valid
135 0 : VerifyOrReturnError(MonotonicallyIncreasingCounter<T>::GetValue() < mNextEpoch, CHIP_ERROR_INTERNAL);
136 : }
137 0 : return CHIP_NO_ERROR;
138 : }
139 :
140 : private:
141 : /**
142 : * @brief
143 : * Write out the counter value to persistent storage.
144 : *
145 : * @param[in] aStartValue The counter value to write out.
146 : *
147 : * @return Any error returned by a write to persistent storage.
148 : */
149 1 : CHIP_ERROR PersistNextEpochStart(T aStartValue)
150 : {
151 1 : mNextEpoch = aStartValue;
152 : #if CHIP_CONFIG_PERSISTED_COUNTER_DEBUG_LOGGING
153 : if constexpr (std::is_same_v<decltype(aStartValue), uint64_t>)
154 : {
155 : ChipLogDetail(EventLogging, "PersistedCounter::WriteStartValue() aStartValue 0x" ChipLogFormatX64,
156 : ChipLogValueX64(aStartValue));
157 : }
158 : else
159 : {
160 : ChipLogDetail(EventLogging, "PersistedCounter::WriteStartValue() aStartValue 0x%" PRIx32,
161 : static_cast<uint32_t>(aStartValue));
162 : }
163 : #endif
164 :
165 1 : T valueLE = Encoding::LittleEndian::HostSwap<T>(aStartValue);
166 1 : return mStorage->SyncSetKeyValue(mKey.KeyName(), &valueLE, sizeof(valueLE));
167 : }
168 :
169 : /**
170 : * @brief
171 : * Read our starting counter value (if we have one) in from persistent storage.
172 : *
173 : * @param[in,out] aStartValue The value read out.
174 : *
175 : * @return Any error returned by a read from persistent storage.
176 : */
177 1 : CHIP_ERROR ReadStartValue(T & aStartValue)
178 : {
179 1 : T valueLE = GetInitialCounterValue();
180 1 : uint16_t size = sizeof(valueLE);
181 :
182 1 : VerifyOrReturnError(mKey.IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
183 :
184 1 : CHIP_ERROR err = mStorage->SyncGetKeyValue(mKey.KeyName(), &valueLE, size);
185 1 : if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND)
186 : {
187 : // No previously-stored value, no worries, the counter is initialized to zero.
188 : // Suppress the error.
189 1 : err = CHIP_NO_ERROR;
190 : }
191 : else
192 : {
193 : // TODO: Figure out how to avoid a bootloop here. Maybe we should just
194 : // init to 0? Or a random value?
195 0 : ReturnErrorOnFailure(err);
196 : }
197 :
198 1 : if (size != sizeof(valueLE))
199 : {
200 : // TODO: Again, figure out whether this could lead to bootloops.
201 0 : return CHIP_ERROR_INCORRECT_STATE;
202 : }
203 :
204 1 : aStartValue = Encoding::LittleEndian::HostSwap<T>(valueLE);
205 :
206 : #if CHIP_CONFIG_PERSISTED_COUNTER_DEBUG_LOGGING
207 : if constexpr (std::is_same_v<decltype(aStartValue), uint64_t>)
208 : {
209 : ChipLogDetail(EventLogging, "PersistedCounter::ReadStartValue() aStartValue 0x" ChipLogFormatX64,
210 : ChipLogValueX64(aStartValue));
211 : }
212 : else
213 : {
214 : ChipLogDetail(EventLogging, "PersistedCounter::ReadStartValue() aStartValue 0x%" PRIx32,
215 : static_cast<uint32_t>(aStartValue));
216 : }
217 : #endif
218 :
219 1 : return CHIP_NO_ERROR;
220 : }
221 :
222 : /**
223 : * @brief Get the Initial Counter Value
224 : *
225 : * By default, persisted counters start off at 0.
226 : */
227 1 : virtual inline T GetInitialCounterValue() { return 0; }
228 :
229 : PersistentStorageDelegate * mStorage = nullptr; // start value is stored here
230 : StorageKeyName mKey;
231 : T mEpoch = 0; // epoch modulus value
232 : T mNextEpoch = 0; // next epoch start
233 : };
234 :
235 : } // namespace chip
|