blob: a2700afc761481328dc3ed471083810f39c1f0db [file] [log] [blame]
Patrick Williams691668f2023-11-01 08:19:10 -05001/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
Rashmica Gupta36af84c2023-04-12 16:16:28 +10002// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
3#define _GNU_SOURCE
Andrew Jefferyb0c1d202023-11-07 22:08:44 +10304#include <libpldm/instance-id.h>
Andrew Jefferyd12dd362023-11-10 15:36:38 +10305#include <libpldm/pldm.h>
Andrew Jefferyb0c1d202023-11-07 22:08:44 +10306
Rashmica Gupta36af84c2023-04-12 16:16:28 +10007#include <errno.h>
8#include <fcntl.h>
9#include <stdlib.h>
Andrew Jefferya6f0cf32023-04-24 11:40:43 +093010#include <sys/stat.h>
Rashmica Gupta36af84c2023-04-12 16:16:28 +100011#include <unistd.h>
12
Andrew Jeffery0aea7072023-04-24 11:16:35 +093013#define BIT(i) (1UL << (i))
14
Andrew Jeffery37dd6a32023-05-12 16:04:06 +093015#define PLDM_TID_MAX 256
Rashmica Gupta36af84c2023-04-12 16:16:28 +100016#define PLDM_INST_ID_MAX 32
17
Andrew Jeffery0aea7072023-04-24 11:16:35 +093018/* We need to track our allocations explicitly due to OFD lock merging/splitting
19 */
20struct pldm_tid_state {
21 pldm_instance_id_t prev;
22 uint32_t allocations;
23};
24
Rashmica Gupta36af84c2023-04-12 16:16:28 +100025struct pldm_instance_db {
Andrew Jeffery0aea7072023-04-24 11:16:35 +093026 struct pldm_tid_state state[PLDM_TID_MAX];
Rashmica Gupta36af84c2023-04-12 16:16:28 +100027 int lock_db_fd;
28};
29
30static inline int iid_next(pldm_instance_id_t cur)
31{
32 return (cur + 1) % PLDM_INST_ID_MAX;
33}
34
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +093035LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +100036int pldm_instance_db_init(struct pldm_instance_db **ctx, const char *dbpath)
37{
38 struct pldm_instance_db *l_ctx;
Andrew Jefferya6f0cf32023-04-24 11:40:43 +093039 struct stat statbuf;
40 int rc;
Rashmica Gupta36af84c2023-04-12 16:16:28 +100041
42 /* Make sure the provided pointer was initialised to NULL. In the future
43 * if we stabilise the ABI and expose the struct definition the caller
44 * can potentially pass a valid pointer to a struct they've allocated
45 */
46 if (!ctx || *ctx) {
47 return -EINVAL;
48 }
49
Andrew Jefferya6f0cf32023-04-24 11:40:43 +093050 /* Ensure the underlying file is sized for properly managing allocations
51 */
52 rc = stat(dbpath, &statbuf);
53 if (rc < 0) {
54 return -EINVAL;
55 }
56
57 if (statbuf.st_size <
58 ((off_t)(PLDM_TID_MAX) * (off_t)(PLDM_INST_ID_MAX))) {
59 return -EINVAL;
60 }
61
Rashmica Gupta36af84c2023-04-12 16:16:28 +100062 l_ctx = calloc(1, sizeof(struct pldm_instance_db));
63 if (!l_ctx) {
64 return -ENOMEM;
65 }
66
67 /* Initialise previous ID values so the next one is zero */
68 for (int i = 0; i < PLDM_TID_MAX; i++) {
Andrew Jeffery0aea7072023-04-24 11:16:35 +093069 l_ctx->state[i].prev = 31;
Rashmica Gupta36af84c2023-04-12 16:16:28 +100070 }
71
72 /* Lock database may be read-only, either by permissions or mountpoint
73 */
74 l_ctx->lock_db_fd = open(dbpath, O_RDONLY | O_CLOEXEC);
75 if (l_ctx->lock_db_fd < 0) {
76 free(l_ctx);
77 return -errno;
78 }
79 *ctx = l_ctx;
80
81 return 0;
82}
83
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +093084LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +100085int pldm_instance_db_init_default(struct pldm_instance_db **ctx)
86{
87 return pldm_instance_db_init(ctx,
88 "/usr/share/libpldm/instance-db/default");
89}
90
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +093091LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +100092int pldm_instance_db_destroy(struct pldm_instance_db *ctx)
93{
94 if (!ctx) {
95 return 0;
96 }
97 close(ctx->lock_db_fd);
98 free(ctx);
99 return 0;
100}
101
Andrew Jefferye4240672024-06-28 14:52:55 +0930102static const struct flock pldm_instance_id_cfls = {
103 .l_type = F_RDLCK,
104 .l_whence = SEEK_SET,
105 .l_len = 1,
106};
107
108static const struct flock pldm_instance_id_cflx = {
109 .l_type = F_WRLCK,
110 .l_whence = SEEK_SET,
111 .l_len = 1,
112};
113
114static const struct flock pldm_instance_id_cflu = {
115 .l_type = F_UNLCK,
116 .l_whence = SEEK_SET,
117 .l_len = 1,
118};
119
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930120LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000121int pldm_instance_id_alloc(struct pldm_instance_db *ctx, pldm_tid_t tid,
122 pldm_instance_id_t *iid)
123{
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000124 uint8_t l_iid;
125
126 if (!iid) {
127 return -EINVAL;
128 }
129
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930130 l_iid = ctx->state[tid].prev;
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000131 if (l_iid >= PLDM_INST_ID_MAX) {
132 return -EPROTO;
133 }
134
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930135 while ((l_iid = iid_next(l_iid)) != ctx->state[tid].prev) {
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000136 struct flock flop;
137 off_t loff;
138 int rc;
139
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930140 /* Have we already allocated this instance ID? */
141 if (ctx->state[tid].allocations & BIT(l_iid)) {
142 continue;
143 }
144
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000145 /* Derive the instance ID offset in the lock database */
146 loff = tid * PLDM_INST_ID_MAX + l_iid;
147
148 /* Reserving the TID's IID. Done via a shared lock */
Andrew Jefferye4240672024-06-28 14:52:55 +0930149 flop = pldm_instance_id_cfls;
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000150 flop.l_start = loff;
151 rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
152 if (rc < 0) {
153 if (errno == EAGAIN || errno == EINTR) {
154 return -EAGAIN;
155 }
156 return -EPROTO;
157 }
158
159 /*
160 * If we *may* promote the lock to exclusive then this IID is
161 * only reserved by us. This is now our allocated IID.
162 *
163 * If we *may not* promote the lock to exclusive then this IID
164 * is also reserved on another file descriptor. Move on to the
165 * next IID index.
166 *
167 * Note that we cannot actually *perform* the promotion in
168 * practice because this is prevented by the lock database being
169 * opened O_RDONLY.
170 */
Andrew Jefferye4240672024-06-28 14:52:55 +0930171 flop = pldm_instance_id_cflx;
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000172 flop.l_start = loff;
173 rc = fcntl(ctx->lock_db_fd, F_OFD_GETLK, &flop);
174 if (rc < 0) {
175 if (errno == EAGAIN || errno == EINTR) {
Andrew Jefferye4240672024-06-28 14:52:55 +0930176 rc = -EAGAIN;
177 goto release_cfls;
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000178 }
Andrew Jefferye4240672024-06-28 14:52:55 +0930179 rc = -EPROTO;
180 goto release_cfls;
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000181 }
182
183 /* F_UNLCK is the type of the lock if we could successfully
184 * promote it to F_WRLCK */
185 if (flop.l_type == F_UNLCK) {
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930186 ctx->state[tid].prev = l_iid;
187 ctx->state[tid].allocations |= BIT(l_iid);
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000188 *iid = l_iid;
189 return 0;
190 }
Andrew Jefferye4240672024-06-28 14:52:55 +0930191
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000192 if (flop.l_type != F_RDLCK) {
Andrew Jefferye4240672024-06-28 14:52:55 +0930193 rc = -EPROTO;
194 }
195
196 release_cfls:
197 flop = pldm_instance_id_cflu;
198 flop.l_start = loff;
199 if (fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop) < 0) {
200 if (errno == EAGAIN || errno == EINTR) {
201 return -EAGAIN;
202 }
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000203 return -EPROTO;
204 }
Andrew Jefferye4240672024-06-28 14:52:55 +0930205
206 if (rc < 0) {
207 return rc;
208 }
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000209 }
210
211 /* Failed to allocate an IID after a full loop. Make the caller try
212 * again */
213 return -EAGAIN;
214}
215
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930216LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000217int pldm_instance_id_free(struct pldm_instance_db *ctx, pldm_tid_t tid,
218 pldm_instance_id_t iid)
219{
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000220 struct flock flop;
221 int rc;
222
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930223 /* Trying to free an instance ID that is not currently allocated */
224 if (!(ctx->state[tid].allocations & BIT(iid))) {
225 return -EINVAL;
226 }
227
Andrew Jefferye4240672024-06-28 14:52:55 +0930228 flop = pldm_instance_id_cflu;
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000229 flop.l_start = tid * PLDM_INST_ID_MAX + iid;
230 rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
231 if (rc < 0) {
232 if (errno == EAGAIN || errno == EINTR) {
233 return -EAGAIN;
234 }
235 return -EPROTO;
236 }
237
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930238 /* Mark the instance ID as no-longer allocated */
239 ctx->state[tid].allocations &= ~BIT(iid);
240
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000241 return 0;
242}