blob: 82e3ed1294942cb73d20d697be448c43dc65a8e0 [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
33int pldm_instance_db_init(struct pldm_instance_db **ctx, const char *dbpath)
34{
35 struct pldm_instance_db *l_ctx;
Andrew Jefferya6f0cf32023-04-24 11:40:43 +093036 struct stat statbuf;
37 int rc;
Rashmica Gupta36af84c2023-04-12 16:16:28 +100038
39 /* Make sure the provided pointer was initialised to NULL. In the future
40 * if we stabilise the ABI and expose the struct definition the caller
41 * can potentially pass a valid pointer to a struct they've allocated
42 */
43 if (!ctx || *ctx) {
44 return -EINVAL;
45 }
46
Andrew Jefferya6f0cf32023-04-24 11:40:43 +093047 /* Ensure the underlying file is sized for properly managing allocations
48 */
49 rc = stat(dbpath, &statbuf);
50 if (rc < 0) {
51 return -EINVAL;
52 }
53
54 if (statbuf.st_size <
55 ((off_t)(PLDM_TID_MAX) * (off_t)(PLDM_INST_ID_MAX))) {
56 return -EINVAL;
57 }
58
Rashmica Gupta36af84c2023-04-12 16:16:28 +100059 l_ctx = calloc(1, sizeof(struct pldm_instance_db));
60 if (!l_ctx) {
61 return -ENOMEM;
62 }
63
64 /* Initialise previous ID values so the next one is zero */
65 for (int i = 0; i < PLDM_TID_MAX; i++) {
Andrew Jeffery0aea7072023-04-24 11:16:35 +093066 l_ctx->state[i].prev = 31;
Rashmica Gupta36af84c2023-04-12 16:16:28 +100067 }
68
69 /* Lock database may be read-only, either by permissions or mountpoint
70 */
71 l_ctx->lock_db_fd = open(dbpath, O_RDONLY | O_CLOEXEC);
72 if (l_ctx->lock_db_fd < 0) {
73 free(l_ctx);
74 return -errno;
75 }
76 *ctx = l_ctx;
77
78 return 0;
79}
80
81int pldm_instance_db_init_default(struct pldm_instance_db **ctx)
82{
83 return pldm_instance_db_init(ctx,
84 "/usr/share/libpldm/instance-db/default");
85}
86
87int pldm_instance_db_destroy(struct pldm_instance_db *ctx)
88{
89 if (!ctx) {
90 return 0;
91 }
92 close(ctx->lock_db_fd);
93 free(ctx);
94 return 0;
95}
96
97int pldm_instance_id_alloc(struct pldm_instance_db *ctx, pldm_tid_t tid,
98 pldm_instance_id_t *iid)
99{
100 static const struct flock cfls = {
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930101 .l_type = F_RDLCK,
102 .l_whence = SEEK_SET,
103 .l_len = 1,
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000104 };
105 static const struct flock cflx = {
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930106 .l_type = F_WRLCK,
107 .l_whence = SEEK_SET,
108 .l_len = 1,
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000109 };
110 uint8_t l_iid;
111
112 if (!iid) {
113 return -EINVAL;
114 }
115
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930116 l_iid = ctx->state[tid].prev;
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000117 if (l_iid >= PLDM_INST_ID_MAX) {
118 return -EPROTO;
119 }
120
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930121 while ((l_iid = iid_next(l_iid)) != ctx->state[tid].prev) {
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000122 struct flock flop;
123 off_t loff;
124 int rc;
125
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930126 /* Have we already allocated this instance ID? */
127 if (ctx->state[tid].allocations & BIT(l_iid)) {
128 continue;
129 }
130
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000131 /* Derive the instance ID offset in the lock database */
132 loff = tid * PLDM_INST_ID_MAX + l_iid;
133
134 /* Reserving the TID's IID. Done via a shared lock */
135 flop = cfls;
136 flop.l_start = loff;
137 rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
138 if (rc < 0) {
139 if (errno == EAGAIN || errno == EINTR) {
140 return -EAGAIN;
141 }
142 return -EPROTO;
143 }
144
145 /*
146 * If we *may* promote the lock to exclusive then this IID is
147 * only reserved by us. This is now our allocated IID.
148 *
149 * If we *may not* promote the lock to exclusive then this IID
150 * is also reserved on another file descriptor. Move on to the
151 * next IID index.
152 *
153 * Note that we cannot actually *perform* the promotion in
154 * practice because this is prevented by the lock database being
155 * opened O_RDONLY.
156 */
157 flop = cflx;
158 flop.l_start = loff;
159 rc = fcntl(ctx->lock_db_fd, F_OFD_GETLK, &flop);
160 if (rc < 0) {
161 if (errno == EAGAIN || errno == EINTR) {
162 return -EAGAIN;
163 }
164 return -EPROTO;
165 }
166
167 /* F_UNLCK is the type of the lock if we could successfully
168 * promote it to F_WRLCK */
169 if (flop.l_type == F_UNLCK) {
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930170 ctx->state[tid].prev = l_iid;
171 ctx->state[tid].allocations |= BIT(l_iid);
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000172 *iid = l_iid;
173 return 0;
174 }
175 if (flop.l_type != F_RDLCK) {
176 return -EPROTO;
177 }
178 }
179
180 /* Failed to allocate an IID after a full loop. Make the caller try
181 * again */
182 return -EAGAIN;
183}
184
185int pldm_instance_id_free(struct pldm_instance_db *ctx, pldm_tid_t tid,
186 pldm_instance_id_t iid)
187{
188 static const struct flock cflu = {
Andrew Jeffery37dd6a32023-05-12 16:04:06 +0930189 .l_type = F_UNLCK,
190 .l_whence = SEEK_SET,
191 .l_len = 1,
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000192 };
193 struct flock flop;
194 int rc;
195
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930196 /* Trying to free an instance ID that is not currently allocated */
197 if (!(ctx->state[tid].allocations & BIT(iid))) {
198 return -EINVAL;
199 }
200
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000201 flop = cflu;
202 flop.l_start = tid * PLDM_INST_ID_MAX + iid;
203 rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
204 if (rc < 0) {
205 if (errno == EAGAIN || errno == EINTR) {
206 return -EAGAIN;
207 }
208 return -EPROTO;
209 }
210
Andrew Jeffery0aea7072023-04-24 11:16:35 +0930211 /* Mark the instance ID as no-longer allocated */
212 ctx->state[tid].allocations &= ~BIT(iid);
213
Rashmica Gupta36af84c2023-04-12 16:16:28 +1000214 return 0;
215}