1691668feSPatrick Williams /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
236af84cdSRashmica Gupta // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
3b0c1d20aSAndrew Jeffery #include <libpldm/instance-id.h>
4d12dd36eSAndrew Jeffery #include <libpldm/pldm.h>
5b0c1d20aSAndrew Jeffery
636af84cdSRashmica Gupta #include <errno.h>
736af84cdSRashmica Gupta #include <fcntl.h>
836af84cdSRashmica Gupta #include <stdlib.h>
9a6f0cf3eSAndrew Jeffery #include <sys/stat.h>
1036af84cdSRashmica Gupta #include <unistd.h>
1136af84cdSRashmica Gupta
120aea707bSAndrew Jeffery #define BIT(i) (1UL << (i))
130aea707bSAndrew Jeffery
1436af84cdSRashmica Gupta #define PLDM_TID_MAX 256
1536af84cdSRashmica Gupta #define PLDM_INST_ID_MAX 32
1636af84cdSRashmica Gupta
170aea707bSAndrew Jeffery /* We need to track our allocations explicitly due to OFD lock merging/splitting
180aea707bSAndrew Jeffery */
190aea707bSAndrew Jeffery struct pldm_tid_state {
200aea707bSAndrew Jeffery pldm_instance_id_t prev;
210aea707bSAndrew Jeffery uint32_t allocations;
220aea707bSAndrew Jeffery };
230aea707bSAndrew Jeffery
2436af84cdSRashmica Gupta struct pldm_instance_db {
250aea707bSAndrew Jeffery struct pldm_tid_state state[PLDM_TID_MAX];
2636af84cdSRashmica Gupta int lock_db_fd;
2736af84cdSRashmica Gupta };
2836af84cdSRashmica Gupta
iid_next(pldm_instance_id_t cur)2936af84cdSRashmica Gupta static inline int iid_next(pldm_instance_id_t cur)
3036af84cdSRashmica Gupta {
3136af84cdSRashmica Gupta return (cur + 1) % PLDM_INST_ID_MAX;
3236af84cdSRashmica Gupta }
3336af84cdSRashmica Gupta
349d2a1c6aSAndrew Jeffery LIBPLDM_ABI_STABLE
pldm_instance_db_init(struct pldm_instance_db ** ctx,const char * dbpath)3536af84cdSRashmica Gupta int pldm_instance_db_init(struct pldm_instance_db **ctx, const char *dbpath)
3636af84cdSRashmica Gupta {
3736af84cdSRashmica Gupta struct pldm_instance_db *l_ctx;
38a6f0cf3eSAndrew Jeffery struct stat statbuf;
39a6f0cf3eSAndrew Jeffery int rc;
4036af84cdSRashmica Gupta
4136af84cdSRashmica Gupta /* Make sure the provided pointer was initialised to NULL. In the future
4236af84cdSRashmica Gupta * if we stabilise the ABI and expose the struct definition the caller
4336af84cdSRashmica Gupta * can potentially pass a valid pointer to a struct they've allocated
4436af84cdSRashmica Gupta */
4536af84cdSRashmica Gupta if (!ctx || *ctx) {
4636af84cdSRashmica Gupta return -EINVAL;
4736af84cdSRashmica Gupta }
4836af84cdSRashmica Gupta
49a6f0cf3eSAndrew Jeffery /* Ensure the underlying file is sized for properly managing allocations
50a6f0cf3eSAndrew Jeffery */
51a6f0cf3eSAndrew Jeffery rc = stat(dbpath, &statbuf);
52a6f0cf3eSAndrew Jeffery if (rc < 0) {
53a6f0cf3eSAndrew Jeffery return -EINVAL;
54a6f0cf3eSAndrew Jeffery }
55a6f0cf3eSAndrew Jeffery
56a6f0cf3eSAndrew Jeffery if (statbuf.st_size <
57a6f0cf3eSAndrew Jeffery ((off_t)(PLDM_TID_MAX) * (off_t)(PLDM_INST_ID_MAX))) {
58a6f0cf3eSAndrew Jeffery return -EINVAL;
59a6f0cf3eSAndrew Jeffery }
60a6f0cf3eSAndrew Jeffery
6136af84cdSRashmica Gupta l_ctx = calloc(1, sizeof(struct pldm_instance_db));
6236af84cdSRashmica Gupta if (!l_ctx) {
6336af84cdSRashmica Gupta return -ENOMEM;
6436af84cdSRashmica Gupta }
6536af84cdSRashmica Gupta
6636af84cdSRashmica Gupta /* Initialise previous ID values so the next one is zero */
6736af84cdSRashmica Gupta for (int i = 0; i < PLDM_TID_MAX; i++) {
680aea707bSAndrew Jeffery l_ctx->state[i].prev = 31;
6936af84cdSRashmica Gupta }
7036af84cdSRashmica Gupta
7136af84cdSRashmica Gupta /* Lock database may be read-only, either by permissions or mountpoint
7236af84cdSRashmica Gupta */
7336af84cdSRashmica Gupta l_ctx->lock_db_fd = open(dbpath, O_RDONLY | O_CLOEXEC);
7436af84cdSRashmica Gupta if (l_ctx->lock_db_fd < 0) {
7536af84cdSRashmica Gupta free(l_ctx);
7636af84cdSRashmica Gupta return -errno;
7736af84cdSRashmica Gupta }
7836af84cdSRashmica Gupta *ctx = l_ctx;
7936af84cdSRashmica Gupta
8036af84cdSRashmica Gupta return 0;
8136af84cdSRashmica Gupta }
8236af84cdSRashmica Gupta
839d2a1c6aSAndrew Jeffery LIBPLDM_ABI_STABLE
pldm_instance_db_init_default(struct pldm_instance_db ** ctx)8436af84cdSRashmica Gupta int pldm_instance_db_init_default(struct pldm_instance_db **ctx)
8536af84cdSRashmica Gupta {
8636af84cdSRashmica Gupta return pldm_instance_db_init(ctx,
8736af84cdSRashmica Gupta "/usr/share/libpldm/instance-db/default");
8836af84cdSRashmica Gupta }
8936af84cdSRashmica Gupta
909d2a1c6aSAndrew Jeffery LIBPLDM_ABI_STABLE
pldm_instance_db_destroy(struct pldm_instance_db * ctx)9136af84cdSRashmica Gupta int pldm_instance_db_destroy(struct pldm_instance_db *ctx)
9236af84cdSRashmica Gupta {
9336af84cdSRashmica Gupta if (!ctx) {
9436af84cdSRashmica Gupta return 0;
9536af84cdSRashmica Gupta }
9636af84cdSRashmica Gupta close(ctx->lock_db_fd);
9736af84cdSRashmica Gupta free(ctx);
9836af84cdSRashmica Gupta return 0;
9936af84cdSRashmica Gupta }
10036af84cdSRashmica Gupta
101*e4240679SAndrew Jeffery static const struct flock pldm_instance_id_cfls = {
10236af84cdSRashmica Gupta .l_type = F_RDLCK,
10336af84cdSRashmica Gupta .l_whence = SEEK_SET,
10436af84cdSRashmica Gupta .l_len = 1,
10536af84cdSRashmica Gupta };
106*e4240679SAndrew Jeffery
107*e4240679SAndrew Jeffery static const struct flock pldm_instance_id_cflx = {
10836af84cdSRashmica Gupta .l_type = F_WRLCK,
10936af84cdSRashmica Gupta .l_whence = SEEK_SET,
11036af84cdSRashmica Gupta .l_len = 1,
11136af84cdSRashmica Gupta };
112*e4240679SAndrew Jeffery
113*e4240679SAndrew Jeffery static const struct flock pldm_instance_id_cflu = {
114*e4240679SAndrew Jeffery .l_type = F_UNLCK,
115*e4240679SAndrew Jeffery .l_whence = SEEK_SET,
116*e4240679SAndrew Jeffery .l_len = 1,
117*e4240679SAndrew Jeffery };
118*e4240679SAndrew Jeffery
119*e4240679SAndrew Jeffery LIBPLDM_ABI_STABLE
pldm_instance_id_alloc(struct pldm_instance_db * ctx,pldm_tid_t tid,pldm_instance_id_t * iid)120*e4240679SAndrew Jeffery int pldm_instance_id_alloc(struct pldm_instance_db *ctx, pldm_tid_t tid,
121*e4240679SAndrew Jeffery pldm_instance_id_t *iid)
122*e4240679SAndrew Jeffery {
12336af84cdSRashmica Gupta uint8_t l_iid;
12436af84cdSRashmica Gupta
12536af84cdSRashmica Gupta if (!iid) {
12636af84cdSRashmica Gupta return -EINVAL;
12736af84cdSRashmica Gupta }
12836af84cdSRashmica Gupta
1290aea707bSAndrew Jeffery l_iid = ctx->state[tid].prev;
13036af84cdSRashmica Gupta if (l_iid >= PLDM_INST_ID_MAX) {
13136af84cdSRashmica Gupta return -EPROTO;
13236af84cdSRashmica Gupta }
13336af84cdSRashmica Gupta
1340aea707bSAndrew Jeffery while ((l_iid = iid_next(l_iid)) != ctx->state[tid].prev) {
13536af84cdSRashmica Gupta struct flock flop;
13636af84cdSRashmica Gupta off_t loff;
13736af84cdSRashmica Gupta int rc;
13836af84cdSRashmica Gupta
1390aea707bSAndrew Jeffery /* Have we already allocated this instance ID? */
1400aea707bSAndrew Jeffery if (ctx->state[tid].allocations & BIT(l_iid)) {
1410aea707bSAndrew Jeffery continue;
1420aea707bSAndrew Jeffery }
1430aea707bSAndrew Jeffery
14436af84cdSRashmica Gupta /* Derive the instance ID offset in the lock database */
14536af84cdSRashmica Gupta loff = tid * PLDM_INST_ID_MAX + l_iid;
14636af84cdSRashmica Gupta
14736af84cdSRashmica Gupta /* Reserving the TID's IID. Done via a shared lock */
148*e4240679SAndrew Jeffery flop = pldm_instance_id_cfls;
14936af84cdSRashmica Gupta flop.l_start = loff;
15036af84cdSRashmica Gupta rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
15136af84cdSRashmica Gupta if (rc < 0) {
15236af84cdSRashmica Gupta if (errno == EAGAIN || errno == EINTR) {
15336af84cdSRashmica Gupta return -EAGAIN;
15436af84cdSRashmica Gupta }
15536af84cdSRashmica Gupta return -EPROTO;
15636af84cdSRashmica Gupta }
15736af84cdSRashmica Gupta
15836af84cdSRashmica Gupta /*
15936af84cdSRashmica Gupta * If we *may* promote the lock to exclusive then this IID is
16036af84cdSRashmica Gupta * only reserved by us. This is now our allocated IID.
16136af84cdSRashmica Gupta *
16236af84cdSRashmica Gupta * If we *may not* promote the lock to exclusive then this IID
16336af84cdSRashmica Gupta * is also reserved on another file descriptor. Move on to the
16436af84cdSRashmica Gupta * next IID index.
16536af84cdSRashmica Gupta *
16636af84cdSRashmica Gupta * Note that we cannot actually *perform* the promotion in
16736af84cdSRashmica Gupta * practice because this is prevented by the lock database being
16836af84cdSRashmica Gupta * opened O_RDONLY.
16936af84cdSRashmica Gupta */
170*e4240679SAndrew Jeffery flop = pldm_instance_id_cflx;
17136af84cdSRashmica Gupta flop.l_start = loff;
17236af84cdSRashmica Gupta rc = fcntl(ctx->lock_db_fd, F_OFD_GETLK, &flop);
17336af84cdSRashmica Gupta if (rc < 0) {
17436af84cdSRashmica Gupta if (errno == EAGAIN || errno == EINTR) {
175*e4240679SAndrew Jeffery rc = -EAGAIN;
176*e4240679SAndrew Jeffery goto release_cfls;
17736af84cdSRashmica Gupta }
178*e4240679SAndrew Jeffery rc = -EPROTO;
179*e4240679SAndrew Jeffery goto release_cfls;
18036af84cdSRashmica Gupta }
18136af84cdSRashmica Gupta
18236af84cdSRashmica Gupta /* F_UNLCK is the type of the lock if we could successfully
18336af84cdSRashmica Gupta * promote it to F_WRLCK */
18436af84cdSRashmica Gupta if (flop.l_type == F_UNLCK) {
1850aea707bSAndrew Jeffery ctx->state[tid].prev = l_iid;
1860aea707bSAndrew Jeffery ctx->state[tid].allocations |= BIT(l_iid);
18736af84cdSRashmica Gupta *iid = l_iid;
18836af84cdSRashmica Gupta return 0;
18936af84cdSRashmica Gupta }
190*e4240679SAndrew Jeffery
19136af84cdSRashmica Gupta if (flop.l_type != F_RDLCK) {
192*e4240679SAndrew Jeffery rc = -EPROTO;
193*e4240679SAndrew Jeffery }
194*e4240679SAndrew Jeffery
195*e4240679SAndrew Jeffery release_cfls:
196*e4240679SAndrew Jeffery flop = pldm_instance_id_cflu;
197*e4240679SAndrew Jeffery flop.l_start = loff;
198*e4240679SAndrew Jeffery if (fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop) < 0) {
199*e4240679SAndrew Jeffery if (errno == EAGAIN || errno == EINTR) {
200*e4240679SAndrew Jeffery return -EAGAIN;
201*e4240679SAndrew Jeffery }
20236af84cdSRashmica Gupta return -EPROTO;
20336af84cdSRashmica Gupta }
204*e4240679SAndrew Jeffery
205*e4240679SAndrew Jeffery if (rc < 0) {
206*e4240679SAndrew Jeffery return rc;
207*e4240679SAndrew Jeffery }
20836af84cdSRashmica Gupta }
20936af84cdSRashmica Gupta
21036af84cdSRashmica Gupta /* Failed to allocate an IID after a full loop. Make the caller try
21136af84cdSRashmica Gupta * again */
21236af84cdSRashmica Gupta return -EAGAIN;
21336af84cdSRashmica Gupta }
21436af84cdSRashmica Gupta
2159d2a1c6aSAndrew Jeffery LIBPLDM_ABI_STABLE
pldm_instance_id_free(struct pldm_instance_db * ctx,pldm_tid_t tid,pldm_instance_id_t iid)21636af84cdSRashmica Gupta int pldm_instance_id_free(struct pldm_instance_db *ctx, pldm_tid_t tid,
21736af84cdSRashmica Gupta pldm_instance_id_t iid)
21836af84cdSRashmica Gupta {
21936af84cdSRashmica Gupta struct flock flop;
22036af84cdSRashmica Gupta int rc;
22136af84cdSRashmica Gupta
2220aea707bSAndrew Jeffery /* Trying to free an instance ID that is not currently allocated */
2230aea707bSAndrew Jeffery if (!(ctx->state[tid].allocations & BIT(iid))) {
2240aea707bSAndrew Jeffery return -EINVAL;
2250aea707bSAndrew Jeffery }
2260aea707bSAndrew Jeffery
227*e4240679SAndrew Jeffery flop = pldm_instance_id_cflu;
22836af84cdSRashmica Gupta flop.l_start = tid * PLDM_INST_ID_MAX + iid;
22936af84cdSRashmica Gupta rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
23036af84cdSRashmica Gupta if (rc < 0) {
23136af84cdSRashmica Gupta if (errno == EAGAIN || errno == EINTR) {
23236af84cdSRashmica Gupta return -EAGAIN;
23336af84cdSRashmica Gupta }
23436af84cdSRashmica Gupta return -EPROTO;
23536af84cdSRashmica Gupta }
23636af84cdSRashmica Gupta
2370aea707bSAndrew Jeffery /* Mark the instance ID as no-longer allocated */
2380aea707bSAndrew Jeffery ctx->state[tid].allocations &= ~BIT(iid);
2390aea707bSAndrew Jeffery
24036af84cdSRashmica Gupta return 0;
24136af84cdSRashmica Gupta }
242