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) 336af84cdSRashmica Gupta #define _GNU_SOURCE 4*b0c1d20aSAndrew Jeffery #include <libpldm/instance-id.h> 5*b0c1d20aSAndrew Jeffery #include <libpldm/requester/pldm.h> 6*b0c1d20aSAndrew Jeffery 736af84cdSRashmica Gupta #include <errno.h> 836af84cdSRashmica Gupta #include <fcntl.h> 936af84cdSRashmica Gupta #include <stdlib.h> 10a6f0cf3eSAndrew Jeffery #include <sys/stat.h> 1136af84cdSRashmica Gupta #include <unistd.h> 1236af84cdSRashmica Gupta 130aea707bSAndrew Jeffery #define BIT(i) (1UL << (i)) 140aea707bSAndrew Jeffery 1536af84cdSRashmica Gupta #define PLDM_TID_MAX 256 1636af84cdSRashmica Gupta #define PLDM_INST_ID_MAX 32 1736af84cdSRashmica Gupta 180aea707bSAndrew Jeffery /* We need to track our allocations explicitly due to OFD lock merging/splitting 190aea707bSAndrew Jeffery */ 200aea707bSAndrew Jeffery struct pldm_tid_state { 210aea707bSAndrew Jeffery pldm_instance_id_t prev; 220aea707bSAndrew Jeffery uint32_t allocations; 230aea707bSAndrew Jeffery }; 240aea707bSAndrew Jeffery 2536af84cdSRashmica Gupta struct pldm_instance_db { 260aea707bSAndrew Jeffery struct pldm_tid_state state[PLDM_TID_MAX]; 2736af84cdSRashmica Gupta int lock_db_fd; 2836af84cdSRashmica Gupta }; 2936af84cdSRashmica Gupta 3036af84cdSRashmica Gupta static inline int iid_next(pldm_instance_id_t cur) 3136af84cdSRashmica Gupta { 3236af84cdSRashmica Gupta return (cur + 1) % PLDM_INST_ID_MAX; 3336af84cdSRashmica Gupta } 3436af84cdSRashmica Gupta 359d2a1c6aSAndrew Jeffery LIBPLDM_ABI_STABLE 3636af84cdSRashmica Gupta int pldm_instance_db_init(struct pldm_instance_db **ctx, const char *dbpath) 3736af84cdSRashmica Gupta { 3836af84cdSRashmica Gupta struct pldm_instance_db *l_ctx; 39a6f0cf3eSAndrew Jeffery struct stat statbuf; 40a6f0cf3eSAndrew Jeffery int rc; 4136af84cdSRashmica Gupta 4236af84cdSRashmica Gupta /* Make sure the provided pointer was initialised to NULL. In the future 4336af84cdSRashmica Gupta * if we stabilise the ABI and expose the struct definition the caller 4436af84cdSRashmica Gupta * can potentially pass a valid pointer to a struct they've allocated 4536af84cdSRashmica Gupta */ 4636af84cdSRashmica Gupta if (!ctx || *ctx) { 4736af84cdSRashmica Gupta return -EINVAL; 4836af84cdSRashmica Gupta } 4936af84cdSRashmica Gupta 50a6f0cf3eSAndrew Jeffery /* Ensure the underlying file is sized for properly managing allocations 51a6f0cf3eSAndrew Jeffery */ 52a6f0cf3eSAndrew Jeffery rc = stat(dbpath, &statbuf); 53a6f0cf3eSAndrew Jeffery if (rc < 0) { 54a6f0cf3eSAndrew Jeffery return -EINVAL; 55a6f0cf3eSAndrew Jeffery } 56a6f0cf3eSAndrew Jeffery 57a6f0cf3eSAndrew Jeffery if (statbuf.st_size < 58a6f0cf3eSAndrew Jeffery ((off_t)(PLDM_TID_MAX) * (off_t)(PLDM_INST_ID_MAX))) { 59a6f0cf3eSAndrew Jeffery return -EINVAL; 60a6f0cf3eSAndrew Jeffery } 61a6f0cf3eSAndrew Jeffery 6236af84cdSRashmica Gupta l_ctx = calloc(1, sizeof(struct pldm_instance_db)); 6336af84cdSRashmica Gupta if (!l_ctx) { 6436af84cdSRashmica Gupta return -ENOMEM; 6536af84cdSRashmica Gupta } 6636af84cdSRashmica Gupta 6736af84cdSRashmica Gupta /* Initialise previous ID values so the next one is zero */ 6836af84cdSRashmica Gupta for (int i = 0; i < PLDM_TID_MAX; i++) { 690aea707bSAndrew Jeffery l_ctx->state[i].prev = 31; 7036af84cdSRashmica Gupta } 7136af84cdSRashmica Gupta 7236af84cdSRashmica Gupta /* Lock database may be read-only, either by permissions or mountpoint 7336af84cdSRashmica Gupta */ 7436af84cdSRashmica Gupta l_ctx->lock_db_fd = open(dbpath, O_RDONLY | O_CLOEXEC); 7536af84cdSRashmica Gupta if (l_ctx->lock_db_fd < 0) { 7636af84cdSRashmica Gupta free(l_ctx); 7736af84cdSRashmica Gupta return -errno; 7836af84cdSRashmica Gupta } 7936af84cdSRashmica Gupta *ctx = l_ctx; 8036af84cdSRashmica Gupta 8136af84cdSRashmica Gupta return 0; 8236af84cdSRashmica Gupta } 8336af84cdSRashmica Gupta 849d2a1c6aSAndrew Jeffery LIBPLDM_ABI_STABLE 8536af84cdSRashmica Gupta int pldm_instance_db_init_default(struct pldm_instance_db **ctx) 8636af84cdSRashmica Gupta { 8736af84cdSRashmica Gupta return pldm_instance_db_init(ctx, 8836af84cdSRashmica Gupta "/usr/share/libpldm/instance-db/default"); 8936af84cdSRashmica Gupta } 9036af84cdSRashmica Gupta 919d2a1c6aSAndrew Jeffery LIBPLDM_ABI_STABLE 9236af84cdSRashmica Gupta int pldm_instance_db_destroy(struct pldm_instance_db *ctx) 9336af84cdSRashmica Gupta { 9436af84cdSRashmica Gupta if (!ctx) { 9536af84cdSRashmica Gupta return 0; 9636af84cdSRashmica Gupta } 9736af84cdSRashmica Gupta close(ctx->lock_db_fd); 9836af84cdSRashmica Gupta free(ctx); 9936af84cdSRashmica Gupta return 0; 10036af84cdSRashmica Gupta } 10136af84cdSRashmica Gupta 1029d2a1c6aSAndrew Jeffery LIBPLDM_ABI_STABLE 10336af84cdSRashmica Gupta int pldm_instance_id_alloc(struct pldm_instance_db *ctx, pldm_tid_t tid, 10436af84cdSRashmica Gupta pldm_instance_id_t *iid) 10536af84cdSRashmica Gupta { 10636af84cdSRashmica Gupta static const struct flock cfls = { 10736af84cdSRashmica Gupta .l_type = F_RDLCK, 10836af84cdSRashmica Gupta .l_whence = SEEK_SET, 10936af84cdSRashmica Gupta .l_len = 1, 11036af84cdSRashmica Gupta }; 11136af84cdSRashmica Gupta static const struct flock cflx = { 11236af84cdSRashmica Gupta .l_type = F_WRLCK, 11336af84cdSRashmica Gupta .l_whence = SEEK_SET, 11436af84cdSRashmica Gupta .l_len = 1, 11536af84cdSRashmica Gupta }; 11636af84cdSRashmica Gupta uint8_t l_iid; 11736af84cdSRashmica Gupta 11836af84cdSRashmica Gupta if (!iid) { 11936af84cdSRashmica Gupta return -EINVAL; 12036af84cdSRashmica Gupta } 12136af84cdSRashmica Gupta 1220aea707bSAndrew Jeffery l_iid = ctx->state[tid].prev; 12336af84cdSRashmica Gupta if (l_iid >= PLDM_INST_ID_MAX) { 12436af84cdSRashmica Gupta return -EPROTO; 12536af84cdSRashmica Gupta } 12636af84cdSRashmica Gupta 1270aea707bSAndrew Jeffery while ((l_iid = iid_next(l_iid)) != ctx->state[tid].prev) { 12836af84cdSRashmica Gupta struct flock flop; 12936af84cdSRashmica Gupta off_t loff; 13036af84cdSRashmica Gupta int rc; 13136af84cdSRashmica Gupta 1320aea707bSAndrew Jeffery /* Have we already allocated this instance ID? */ 1330aea707bSAndrew Jeffery if (ctx->state[tid].allocations & BIT(l_iid)) { 1340aea707bSAndrew Jeffery continue; 1350aea707bSAndrew Jeffery } 1360aea707bSAndrew Jeffery 13736af84cdSRashmica Gupta /* Derive the instance ID offset in the lock database */ 13836af84cdSRashmica Gupta loff = tid * PLDM_INST_ID_MAX + l_iid; 13936af84cdSRashmica Gupta 14036af84cdSRashmica Gupta /* Reserving the TID's IID. Done via a shared lock */ 14136af84cdSRashmica Gupta flop = cfls; 14236af84cdSRashmica Gupta flop.l_start = loff; 14336af84cdSRashmica Gupta rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop); 14436af84cdSRashmica Gupta if (rc < 0) { 14536af84cdSRashmica Gupta if (errno == EAGAIN || errno == EINTR) { 14636af84cdSRashmica Gupta return -EAGAIN; 14736af84cdSRashmica Gupta } 14836af84cdSRashmica Gupta return -EPROTO; 14936af84cdSRashmica Gupta } 15036af84cdSRashmica Gupta 15136af84cdSRashmica Gupta /* 15236af84cdSRashmica Gupta * If we *may* promote the lock to exclusive then this IID is 15336af84cdSRashmica Gupta * only reserved by us. This is now our allocated IID. 15436af84cdSRashmica Gupta * 15536af84cdSRashmica Gupta * If we *may not* promote the lock to exclusive then this IID 15636af84cdSRashmica Gupta * is also reserved on another file descriptor. Move on to the 15736af84cdSRashmica Gupta * next IID index. 15836af84cdSRashmica Gupta * 15936af84cdSRashmica Gupta * Note that we cannot actually *perform* the promotion in 16036af84cdSRashmica Gupta * practice because this is prevented by the lock database being 16136af84cdSRashmica Gupta * opened O_RDONLY. 16236af84cdSRashmica Gupta */ 16336af84cdSRashmica Gupta flop = cflx; 16436af84cdSRashmica Gupta flop.l_start = loff; 16536af84cdSRashmica Gupta rc = fcntl(ctx->lock_db_fd, F_OFD_GETLK, &flop); 16636af84cdSRashmica Gupta if (rc < 0) { 16736af84cdSRashmica Gupta if (errno == EAGAIN || errno == EINTR) { 16836af84cdSRashmica Gupta return -EAGAIN; 16936af84cdSRashmica Gupta } 17036af84cdSRashmica Gupta return -EPROTO; 17136af84cdSRashmica Gupta } 17236af84cdSRashmica Gupta 17336af84cdSRashmica Gupta /* F_UNLCK is the type of the lock if we could successfully 17436af84cdSRashmica Gupta * promote it to F_WRLCK */ 17536af84cdSRashmica Gupta if (flop.l_type == F_UNLCK) { 1760aea707bSAndrew Jeffery ctx->state[tid].prev = l_iid; 1770aea707bSAndrew Jeffery ctx->state[tid].allocations |= BIT(l_iid); 17836af84cdSRashmica Gupta *iid = l_iid; 17936af84cdSRashmica Gupta return 0; 18036af84cdSRashmica Gupta } 18136af84cdSRashmica Gupta if (flop.l_type != F_RDLCK) { 18236af84cdSRashmica Gupta return -EPROTO; 18336af84cdSRashmica Gupta } 18436af84cdSRashmica Gupta } 18536af84cdSRashmica Gupta 18636af84cdSRashmica Gupta /* Failed to allocate an IID after a full loop. Make the caller try 18736af84cdSRashmica Gupta * again */ 18836af84cdSRashmica Gupta return -EAGAIN; 18936af84cdSRashmica Gupta } 19036af84cdSRashmica Gupta 1919d2a1c6aSAndrew Jeffery LIBPLDM_ABI_STABLE 19236af84cdSRashmica Gupta int pldm_instance_id_free(struct pldm_instance_db *ctx, pldm_tid_t tid, 19336af84cdSRashmica Gupta pldm_instance_id_t iid) 19436af84cdSRashmica Gupta { 19536af84cdSRashmica Gupta static const struct flock cflu = { 19636af84cdSRashmica Gupta .l_type = F_UNLCK, 19736af84cdSRashmica Gupta .l_whence = SEEK_SET, 19836af84cdSRashmica Gupta .l_len = 1, 19936af84cdSRashmica Gupta }; 20036af84cdSRashmica Gupta struct flock flop; 20136af84cdSRashmica Gupta int rc; 20236af84cdSRashmica Gupta 2030aea707bSAndrew Jeffery /* Trying to free an instance ID that is not currently allocated */ 2040aea707bSAndrew Jeffery if (!(ctx->state[tid].allocations & BIT(iid))) { 2050aea707bSAndrew Jeffery return -EINVAL; 2060aea707bSAndrew Jeffery } 2070aea707bSAndrew Jeffery 20836af84cdSRashmica Gupta flop = cflu; 20936af84cdSRashmica Gupta flop.l_start = tid * PLDM_INST_ID_MAX + iid; 21036af84cdSRashmica Gupta rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop); 21136af84cdSRashmica Gupta if (rc < 0) { 21236af84cdSRashmica Gupta if (errno == EAGAIN || errno == EINTR) { 21336af84cdSRashmica Gupta return -EAGAIN; 21436af84cdSRashmica Gupta } 21536af84cdSRashmica Gupta return -EPROTO; 21636af84cdSRashmica Gupta } 21736af84cdSRashmica Gupta 2180aea707bSAndrew Jeffery /* Mark the instance ID as no-longer allocated */ 2190aea707bSAndrew Jeffery ctx->state[tid].allocations &= ~BIT(iid); 2200aea707bSAndrew Jeffery 22136af84cdSRashmica Gupta return 0; 22236af84cdSRashmica Gupta } 223