blob: 108e4515252e87b9551076cf992e33f0d3309cc5 [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 Jefferya918a622023-04-21 15:14:47 +09304#include "libpldm/instance-id.h"
Rashmica Gupta36af84c2023-04-12 16:16:28 +10005#include "libpldm/pldm.h"
6#include <errno.h>
7#include <fcntl.h>
8#include <stdlib.h>
Andrew Jefferya6f0cf32023-04-24 11:40:43 +09309#include <sys/stat.h>
Rashmica Gupta36af84c2023-04-12 16:16:28 +100010#include <unistd.h>
11
Andrew Jeffery0aea7072023-04-24 11:16:35 +093012#define BIT(i) (1UL << (i))
13
Andrew Jeffery37dd6a32023-05-12 16:04:06 +093014#define PLDM_TID_MAX 256
Rashmica Gupta36af84c2023-04-12 16:16:28 +100015#define PLDM_INST_ID_MAX 32
16
Andrew Jeffery0aea7072023-04-24 11:16:35 +093017/* We need to track our allocations explicitly due to OFD lock merging/splitting
18 */
19struct pldm_tid_state {
20 pldm_instance_id_t prev;
21 uint32_t allocations;
22};
23
Rashmica Gupta36af84c2023-04-12 16:16:28 +100024struct pldm_instance_db {
Andrew Jeffery0aea7072023-04-24 11:16:35 +093025 struct pldm_tid_state state[PLDM_TID_MAX];
Rashmica Gupta36af84c2023-04-12 16:16:28 +100026 int lock_db_fd;
27};
28
29static inline int iid_next(pldm_instance_id_t cur)
30{
31 return (cur + 1) % PLDM_INST_ID_MAX;
32}
33
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +093034LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +100035int pldm_instance_db_init(struct pldm_instance_db **ctx, const char *dbpath)
36{
37 struct pldm_instance_db *l_ctx;
Andrew Jefferya6f0cf32023-04-24 11:40:43 +093038 struct stat statbuf;
39 int rc;
Rashmica Gupta36af84c2023-04-12 16:16:28 +100040
41 /* Make sure the provided pointer was initialised to NULL. In the future
42 * if we stabilise the ABI and expose the struct definition the caller
43 * can potentially pass a valid pointer to a struct they've allocated
44 */
45 if (!ctx || *ctx) {
46 return -EINVAL;
47 }
48
Andrew Jefferya6f0cf32023-04-24 11:40:43 +093049 /* Ensure the underlying file is sized for properly managing allocations
50 */
51 rc = stat(dbpath, &statbuf);
52 if (rc < 0) {
53 return -EINVAL;
54 }
55
56 if (statbuf.st_size <
57 ((off_t)(PLDM_TID_MAX) * (off_t)(PLDM_INST_ID_MAX))) {
58 return -EINVAL;
59 }
60
Rashmica Gupta36af84c2023-04-12 16:16:28 +100061 l_ctx = calloc(1, sizeof(struct pldm_instance_db));
62 if (!l_ctx) {
63 return -ENOMEM;
64 }
65
66 /* Initialise previous ID values so the next one is zero */
67 for (int i = 0; i < PLDM_TID_MAX; i++) {
Andrew Jeffery0aea7072023-04-24 11:16:35 +093068 l_ctx->state[i].prev = 31;
Rashmica Gupta36af84c2023-04-12 16:16:28 +100069 }
70
71 /* Lock database may be read-only, either by permissions or mountpoint
72 */
73 l_ctx->lock_db_fd = open(dbpath, O_RDONLY | O_CLOEXEC);
74 if (l_ctx->lock_db_fd < 0) {
75 free(l_ctx);
76 return -errno;
77 }
78 *ctx = l_ctx;
79
80 return 0;
81}
82
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +093083LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +100084int pldm_instance_db_init_default(struct pldm_instance_db **ctx)
85{
86 return pldm_instance_db_init(ctx,
87 "/usr/share/libpldm/instance-db/default");
88}
89
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +093090LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +100091int pldm_instance_db_destroy(struct pldm_instance_db *ctx)
92{
93 if (!ctx) {
94 return 0;
95 }
96 close(ctx->lock_db_fd);
97 free(ctx);
98 return 0;
99}
100
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930101LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000102int pldm_instance_id_alloc(struct pldm_instance_db *ctx, pldm_tid_t tid,
103 pldm_instance_id_t *iid)
104{
105 static const struct flock cfls = {
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930106 .l_type = F_RDLCK,
107 .l_whence = SEEK_SET,
108 .l_len = 1,
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000109 };
110 static const struct flock cflx = {
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930111 .l_type = F_WRLCK,
112 .l_whence = SEEK_SET,
113 .l_len = 1,
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000114 };
115 uint8_t l_iid;
116
117 if (!iid) {
118 return -EINVAL;
119 }
120
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930121 l_iid = ctx->state[tid].prev;
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000122 if (l_iid >= PLDM_INST_ID_MAX) {
123 return -EPROTO;
124 }
125
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930126 while ((l_iid = iid_next(l_iid)) != ctx->state[tid].prev) {
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000127 struct flock flop;
128 off_t loff;
129 int rc;
130
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930131 /* Have we already allocated this instance ID? */
132 if (ctx->state[tid].allocations & BIT(l_iid)) {
133 continue;
134 }
135
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000136 /* Derive the instance ID offset in the lock database */
137 loff = tid * PLDM_INST_ID_MAX + l_iid;
138
139 /* Reserving the TID's IID. Done via a shared lock */
140 flop = cfls;
141 flop.l_start = loff;
142 rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
143 if (rc < 0) {
144 if (errno == EAGAIN || errno == EINTR) {
145 return -EAGAIN;
146 }
147 return -EPROTO;
148 }
149
150 /*
151 * If we *may* promote the lock to exclusive then this IID is
152 * only reserved by us. This is now our allocated IID.
153 *
154 * If we *may not* promote the lock to exclusive then this IID
155 * is also reserved on another file descriptor. Move on to the
156 * next IID index.
157 *
158 * Note that we cannot actually *perform* the promotion in
159 * practice because this is prevented by the lock database being
160 * opened O_RDONLY.
161 */
162 flop = cflx;
163 flop.l_start = loff;
164 rc = fcntl(ctx->lock_db_fd, F_OFD_GETLK, &flop);
165 if (rc < 0) {
166 if (errno == EAGAIN || errno == EINTR) {
167 return -EAGAIN;
168 }
169 return -EPROTO;
170 }
171
172 /* F_UNLCK is the type of the lock if we could successfully
173 * promote it to F_WRLCK */
174 if (flop.l_type == F_UNLCK) {
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930175 ctx->state[tid].prev = l_iid;
176 ctx->state[tid].allocations |= BIT(l_iid);
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000177 *iid = l_iid;
178 return 0;
179 }
180 if (flop.l_type != F_RDLCK) {
181 return -EPROTO;
182 }
183 }
184
185 /* Failed to allocate an IID after a full loop. Make the caller try
186 * again */
187 return -EAGAIN;
188}
189
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930190LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000191int pldm_instance_id_free(struct pldm_instance_db *ctx, pldm_tid_t tid,
192 pldm_instance_id_t iid)
193{
194 static const struct flock cflu = {
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930195 .l_type = F_UNLCK,
196 .l_whence = SEEK_SET,
197 .l_len = 1,
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000198 };
199 struct flock flop;
200 int rc;
201
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930202 /* Trying to free an instance ID that is not currently allocated */
203 if (!(ctx->state[tid].allocations & BIT(iid))) {
204 return -EINVAL;
205 }
206
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000207 flop = cflu;
208 flop.l_start = tid * PLDM_INST_ID_MAX + iid;
209 rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
210 if (rc < 0) {
211 if (errno == EAGAIN || errno == EINTR) {
212 return -EAGAIN;
213 }
214 return -EPROTO;
215 }
216
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930217 /* Mark the instance ID as no-longer allocated */
218 ctx->state[tid].allocations &= ~BIT(iid);
219
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000220 return 0;
221}