blob: a079f7bd3465ddad7a8bb4f870353a9179fac8ba [file] [log] [blame]
Rashmica Gupta36af84c2023-04-12 16:16:28 +10001// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
2#define _GNU_SOURCE
Andrew Jefferya918a622023-04-21 15:14:47 +09303#include "libpldm/instance-id.h"
Rashmica Gupta36af84c2023-04-12 16:16:28 +10004#include "libpldm/pldm.h"
5#include <errno.h>
6#include <fcntl.h>
7#include <stdlib.h>
Andrew Jefferya6f0cf32023-04-24 11:40:43 +09308#include <sys/stat.h>
Rashmica Gupta36af84c2023-04-12 16:16:28 +10009#include <unistd.h>
10
Andrew Jeffery0aea7072023-04-24 11:16:35 +093011#define BIT(i) (1UL << (i))
12
Andrew Jeffery37dd6a32023-05-12 16:04:06 +093013#define PLDM_TID_MAX 256
Rashmica Gupta36af84c2023-04-12 16:16:28 +100014#define PLDM_INST_ID_MAX 32
15
Andrew Jeffery0aea7072023-04-24 11:16:35 +093016/* We need to track our allocations explicitly due to OFD lock merging/splitting
17 */
18struct pldm_tid_state {
19 pldm_instance_id_t prev;
20 uint32_t allocations;
21};
22
Rashmica Gupta36af84c2023-04-12 16:16:28 +100023struct pldm_instance_db {
Andrew Jeffery0aea7072023-04-24 11:16:35 +093024 struct pldm_tid_state state[PLDM_TID_MAX];
Rashmica Gupta36af84c2023-04-12 16:16:28 +100025 int lock_db_fd;
26};
27
28static inline int iid_next(pldm_instance_id_t cur)
29{
30 return (cur + 1) % PLDM_INST_ID_MAX;
31}
32
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +093033LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +100034int pldm_instance_db_init(struct pldm_instance_db **ctx, const char *dbpath)
35{
36 struct pldm_instance_db *l_ctx;
Andrew Jefferya6f0cf32023-04-24 11:40:43 +093037 struct stat statbuf;
38 int rc;
Rashmica Gupta36af84c2023-04-12 16:16:28 +100039
40 /* Make sure the provided pointer was initialised to NULL. In the future
41 * if we stabilise the ABI and expose the struct definition the caller
42 * can potentially pass a valid pointer to a struct they've allocated
43 */
44 if (!ctx || *ctx) {
45 return -EINVAL;
46 }
47
Andrew Jefferya6f0cf32023-04-24 11:40:43 +093048 /* Ensure the underlying file is sized for properly managing allocations
49 */
50 rc = stat(dbpath, &statbuf);
51 if (rc < 0) {
52 return -EINVAL;
53 }
54
55 if (statbuf.st_size <
56 ((off_t)(PLDM_TID_MAX) * (off_t)(PLDM_INST_ID_MAX))) {
57 return -EINVAL;
58 }
59
Rashmica Gupta36af84c2023-04-12 16:16:28 +100060 l_ctx = calloc(1, sizeof(struct pldm_instance_db));
61 if (!l_ctx) {
62 return -ENOMEM;
63 }
64
65 /* Initialise previous ID values so the next one is zero */
66 for (int i = 0; i < PLDM_TID_MAX; i++) {
Andrew Jeffery0aea7072023-04-24 11:16:35 +093067 l_ctx->state[i].prev = 31;
Rashmica Gupta36af84c2023-04-12 16:16:28 +100068 }
69
70 /* Lock database may be read-only, either by permissions or mountpoint
71 */
72 l_ctx->lock_db_fd = open(dbpath, O_RDONLY | O_CLOEXEC);
73 if (l_ctx->lock_db_fd < 0) {
74 free(l_ctx);
75 return -errno;
76 }
77 *ctx = l_ctx;
78
79 return 0;
80}
81
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +093082LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +100083int pldm_instance_db_init_default(struct pldm_instance_db **ctx)
84{
85 return pldm_instance_db_init(ctx,
86 "/usr/share/libpldm/instance-db/default");
87}
88
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +093089LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +100090int pldm_instance_db_destroy(struct pldm_instance_db *ctx)
91{
92 if (!ctx) {
93 return 0;
94 }
95 close(ctx->lock_db_fd);
96 free(ctx);
97 return 0;
98}
99
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930100LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000101int pldm_instance_id_alloc(struct pldm_instance_db *ctx, pldm_tid_t tid,
102 pldm_instance_id_t *iid)
103{
104 static const struct flock cfls = {
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930105 .l_type = F_RDLCK,
106 .l_whence = SEEK_SET,
107 .l_len = 1,
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000108 };
109 static const struct flock cflx = {
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930110 .l_type = F_WRLCK,
111 .l_whence = SEEK_SET,
112 .l_len = 1,
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000113 };
114 uint8_t l_iid;
115
116 if (!iid) {
117 return -EINVAL;
118 }
119
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930120 l_iid = ctx->state[tid].prev;
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000121 if (l_iid >= PLDM_INST_ID_MAX) {
122 return -EPROTO;
123 }
124
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930125 while ((l_iid = iid_next(l_iid)) != ctx->state[tid].prev) {
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000126 struct flock flop;
127 off_t loff;
128 int rc;
129
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930130 /* Have we already allocated this instance ID? */
131 if (ctx->state[tid].allocations & BIT(l_iid)) {
132 continue;
133 }
134
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000135 /* Derive the instance ID offset in the lock database */
136 loff = tid * PLDM_INST_ID_MAX + l_iid;
137
138 /* Reserving the TID's IID. Done via a shared lock */
139 flop = cfls;
140 flop.l_start = loff;
141 rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
142 if (rc < 0) {
143 if (errno == EAGAIN || errno == EINTR) {
144 return -EAGAIN;
145 }
146 return -EPROTO;
147 }
148
149 /*
150 * If we *may* promote the lock to exclusive then this IID is
151 * only reserved by us. This is now our allocated IID.
152 *
153 * If we *may not* promote the lock to exclusive then this IID
154 * is also reserved on another file descriptor. Move on to the
155 * next IID index.
156 *
157 * Note that we cannot actually *perform* the promotion in
158 * practice because this is prevented by the lock database being
159 * opened O_RDONLY.
160 */
161 flop = cflx;
162 flop.l_start = loff;
163 rc = fcntl(ctx->lock_db_fd, F_OFD_GETLK, &flop);
164 if (rc < 0) {
165 if (errno == EAGAIN || errno == EINTR) {
166 return -EAGAIN;
167 }
168 return -EPROTO;
169 }
170
171 /* F_UNLCK is the type of the lock if we could successfully
172 * promote it to F_WRLCK */
173 if (flop.l_type == F_UNLCK) {
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930174 ctx->state[tid].prev = l_iid;
175 ctx->state[tid].allocations |= BIT(l_iid);
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000176 *iid = l_iid;
177 return 0;
178 }
179 if (flop.l_type != F_RDLCK) {
180 return -EPROTO;
181 }
182 }
183
184 /* Failed to allocate an IID after a full loop. Make the caller try
185 * again */
186 return -EAGAIN;
187}
188
Andrew Jeffery9d2a1c62023-06-05 13:02:16 +0930189LIBPLDM_ABI_STABLE
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000190int pldm_instance_id_free(struct pldm_instance_db *ctx, pldm_tid_t tid,
191 pldm_instance_id_t iid)
192{
193 static const struct flock cflu = {
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930194 .l_type = F_UNLCK,
195 .l_whence = SEEK_SET,
196 .l_len = 1,
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000197 };
198 struct flock flop;
199 int rc;
200
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930201 /* Trying to free an instance ID that is not currently allocated */
202 if (!(ctx->state[tid].allocations & BIT(iid))) {
203 return -EINVAL;
204 }
205
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000206 flop = cflu;
207 flop.l_start = tid * PLDM_INST_ID_MAX + iid;
208 rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
209 if (rc < 0) {
210 if (errno == EAGAIN || errno == EINTR) {
211 return -EAGAIN;
212 }
213 return -EPROTO;
214 }
215
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930216 /* Mark the instance ID as no-longer allocated */
217 ctx->state[tid].allocations &= ~BIT(iid);
218
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000219 return 0;
220}