136af84cdSRashmica Gupta // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) 236af84cdSRashmica Gupta #define _GNU_SOURCE 3a918a628SAndrew Jeffery #include "libpldm/instance-id.h" 436af84cdSRashmica Gupta #include "libpldm/pldm.h" 536af84cdSRashmica Gupta #include <errno.h> 636af84cdSRashmica Gupta #include <fcntl.h> 736af84cdSRashmica Gupta #include <stdlib.h> 836af84cdSRashmica Gupta #include <unistd.h> 936af84cdSRashmica Gupta 10*0aea707bSAndrew Jeffery #define BIT(i) (1UL << (i)) 11*0aea707bSAndrew Jeffery 1236af84cdSRashmica Gupta #define PLDM_TID_MAX 256 1336af84cdSRashmica Gupta #define PLDM_INST_ID_MAX 32 1436af84cdSRashmica Gupta 15*0aea707bSAndrew Jeffery /* We need to track our allocations explicitly due to OFD lock merging/splitting 16*0aea707bSAndrew Jeffery */ 17*0aea707bSAndrew Jeffery struct pldm_tid_state { 18*0aea707bSAndrew Jeffery pldm_instance_id_t prev; 19*0aea707bSAndrew Jeffery uint32_t allocations; 20*0aea707bSAndrew Jeffery }; 21*0aea707bSAndrew Jeffery 2236af84cdSRashmica Gupta struct pldm_instance_db { 23*0aea707bSAndrew Jeffery struct pldm_tid_state state[PLDM_TID_MAX]; 2436af84cdSRashmica Gupta int lock_db_fd; 2536af84cdSRashmica Gupta }; 2636af84cdSRashmica Gupta 2736af84cdSRashmica Gupta static inline int iid_next(pldm_instance_id_t cur) 2836af84cdSRashmica Gupta { 2936af84cdSRashmica Gupta return (cur + 1) % PLDM_INST_ID_MAX; 3036af84cdSRashmica Gupta } 3136af84cdSRashmica Gupta 3236af84cdSRashmica Gupta int pldm_instance_db_init(struct pldm_instance_db **ctx, const char *dbpath) 3336af84cdSRashmica Gupta { 3436af84cdSRashmica Gupta struct pldm_instance_db *l_ctx; 3536af84cdSRashmica Gupta 3636af84cdSRashmica Gupta /* Make sure the provided pointer was initialised to NULL. In the future 3736af84cdSRashmica Gupta * if we stabilise the ABI and expose the struct definition the caller 3836af84cdSRashmica Gupta * can potentially pass a valid pointer to a struct they've allocated 3936af84cdSRashmica Gupta */ 4036af84cdSRashmica Gupta if (!ctx || *ctx) { 4136af84cdSRashmica Gupta return -EINVAL; 4236af84cdSRashmica Gupta } 4336af84cdSRashmica Gupta 4436af84cdSRashmica Gupta l_ctx = calloc(1, sizeof(struct pldm_instance_db)); 4536af84cdSRashmica Gupta if (!l_ctx) { 4636af84cdSRashmica Gupta return -ENOMEM; 4736af84cdSRashmica Gupta } 4836af84cdSRashmica Gupta 4936af84cdSRashmica Gupta /* Initialise previous ID values so the next one is zero */ 5036af84cdSRashmica Gupta for (int i = 0; i < PLDM_TID_MAX; i++) { 51*0aea707bSAndrew Jeffery l_ctx->state[i].prev = 31; 5236af84cdSRashmica Gupta } 5336af84cdSRashmica Gupta 5436af84cdSRashmica Gupta /* Lock database may be read-only, either by permissions or mountpoint 5536af84cdSRashmica Gupta */ 5636af84cdSRashmica Gupta l_ctx->lock_db_fd = open(dbpath, O_RDONLY | O_CLOEXEC); 5736af84cdSRashmica Gupta if (l_ctx->lock_db_fd < 0) { 5836af84cdSRashmica Gupta free(l_ctx); 5936af84cdSRashmica Gupta return -errno; 6036af84cdSRashmica Gupta } 6136af84cdSRashmica Gupta *ctx = l_ctx; 6236af84cdSRashmica Gupta 6336af84cdSRashmica Gupta return 0; 6436af84cdSRashmica Gupta } 6536af84cdSRashmica Gupta 6636af84cdSRashmica Gupta int pldm_instance_db_init_default(struct pldm_instance_db **ctx) 6736af84cdSRashmica Gupta { 6836af84cdSRashmica Gupta return pldm_instance_db_init(ctx, 6936af84cdSRashmica Gupta "/usr/share/libpldm/instance-db/default"); 7036af84cdSRashmica Gupta } 7136af84cdSRashmica Gupta 7236af84cdSRashmica Gupta int pldm_instance_db_destroy(struct pldm_instance_db *ctx) 7336af84cdSRashmica Gupta { 7436af84cdSRashmica Gupta if (!ctx) { 7536af84cdSRashmica Gupta return 0; 7636af84cdSRashmica Gupta } 7736af84cdSRashmica Gupta close(ctx->lock_db_fd); 7836af84cdSRashmica Gupta free(ctx); 7936af84cdSRashmica Gupta return 0; 8036af84cdSRashmica Gupta } 8136af84cdSRashmica Gupta 8236af84cdSRashmica Gupta int pldm_instance_id_alloc(struct pldm_instance_db *ctx, pldm_tid_t tid, 8336af84cdSRashmica Gupta pldm_instance_id_t *iid) 8436af84cdSRashmica Gupta { 8536af84cdSRashmica Gupta static const struct flock cfls = { 8636af84cdSRashmica Gupta .l_type = F_RDLCK, 8736af84cdSRashmica Gupta .l_whence = SEEK_SET, 8836af84cdSRashmica Gupta .l_len = 1, 8936af84cdSRashmica Gupta }; 9036af84cdSRashmica Gupta static const struct flock cflx = { 9136af84cdSRashmica Gupta .l_type = F_WRLCK, 9236af84cdSRashmica Gupta .l_whence = SEEK_SET, 9336af84cdSRashmica Gupta .l_len = 1, 9436af84cdSRashmica Gupta }; 9536af84cdSRashmica Gupta uint8_t l_iid; 9636af84cdSRashmica Gupta 9736af84cdSRashmica Gupta if (!iid) { 9836af84cdSRashmica Gupta return -EINVAL; 9936af84cdSRashmica Gupta } 10036af84cdSRashmica Gupta 101*0aea707bSAndrew Jeffery l_iid = ctx->state[tid].prev; 10236af84cdSRashmica Gupta if (l_iid >= PLDM_INST_ID_MAX) { 10336af84cdSRashmica Gupta return -EPROTO; 10436af84cdSRashmica Gupta } 10536af84cdSRashmica Gupta 106*0aea707bSAndrew Jeffery while ((l_iid = iid_next(l_iid)) != ctx->state[tid].prev) { 10736af84cdSRashmica Gupta struct flock flop; 10836af84cdSRashmica Gupta off_t loff; 10936af84cdSRashmica Gupta int rc; 11036af84cdSRashmica Gupta 111*0aea707bSAndrew Jeffery /* Have we already allocated this instance ID? */ 112*0aea707bSAndrew Jeffery if (ctx->state[tid].allocations & BIT(l_iid)) { 113*0aea707bSAndrew Jeffery continue; 114*0aea707bSAndrew Jeffery } 115*0aea707bSAndrew Jeffery 11636af84cdSRashmica Gupta /* Derive the instance ID offset in the lock database */ 11736af84cdSRashmica Gupta loff = tid * PLDM_INST_ID_MAX + l_iid; 11836af84cdSRashmica Gupta 11936af84cdSRashmica Gupta /* Reserving the TID's IID. Done via a shared lock */ 12036af84cdSRashmica Gupta flop = cfls; 12136af84cdSRashmica Gupta flop.l_start = loff; 12236af84cdSRashmica Gupta rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop); 12336af84cdSRashmica Gupta if (rc < 0) { 12436af84cdSRashmica Gupta if (errno == EAGAIN || errno == EINTR) { 12536af84cdSRashmica Gupta return -EAGAIN; 12636af84cdSRashmica Gupta } 12736af84cdSRashmica Gupta return -EPROTO; 12836af84cdSRashmica Gupta } 12936af84cdSRashmica Gupta 13036af84cdSRashmica Gupta /* 13136af84cdSRashmica Gupta * If we *may* promote the lock to exclusive then this IID is 13236af84cdSRashmica Gupta * only reserved by us. This is now our allocated IID. 13336af84cdSRashmica Gupta * 13436af84cdSRashmica Gupta * If we *may not* promote the lock to exclusive then this IID 13536af84cdSRashmica Gupta * is also reserved on another file descriptor. Move on to the 13636af84cdSRashmica Gupta * next IID index. 13736af84cdSRashmica Gupta * 13836af84cdSRashmica Gupta * Note that we cannot actually *perform* the promotion in 13936af84cdSRashmica Gupta * practice because this is prevented by the lock database being 14036af84cdSRashmica Gupta * opened O_RDONLY. 14136af84cdSRashmica Gupta */ 14236af84cdSRashmica Gupta flop = cflx; 14336af84cdSRashmica Gupta flop.l_start = loff; 14436af84cdSRashmica Gupta rc = fcntl(ctx->lock_db_fd, F_OFD_GETLK, &flop); 14536af84cdSRashmica Gupta if (rc < 0) { 14636af84cdSRashmica Gupta if (errno == EAGAIN || errno == EINTR) { 14736af84cdSRashmica Gupta return -EAGAIN; 14836af84cdSRashmica Gupta } 14936af84cdSRashmica Gupta return -EPROTO; 15036af84cdSRashmica Gupta } 15136af84cdSRashmica Gupta 15236af84cdSRashmica Gupta /* F_UNLCK is the type of the lock if we could successfully 15336af84cdSRashmica Gupta * promote it to F_WRLCK */ 15436af84cdSRashmica Gupta if (flop.l_type == F_UNLCK) { 155*0aea707bSAndrew Jeffery ctx->state[tid].prev = l_iid; 156*0aea707bSAndrew Jeffery ctx->state[tid].allocations |= BIT(l_iid); 15736af84cdSRashmica Gupta *iid = l_iid; 15836af84cdSRashmica Gupta return 0; 15936af84cdSRashmica Gupta } 16036af84cdSRashmica Gupta if (flop.l_type != F_RDLCK) { 16136af84cdSRashmica Gupta return -EPROTO; 16236af84cdSRashmica Gupta } 16336af84cdSRashmica Gupta } 16436af84cdSRashmica Gupta 16536af84cdSRashmica Gupta /* Failed to allocate an IID after a full loop. Make the caller try 16636af84cdSRashmica Gupta * again */ 16736af84cdSRashmica Gupta return -EAGAIN; 16836af84cdSRashmica Gupta } 16936af84cdSRashmica Gupta 17036af84cdSRashmica Gupta int pldm_instance_id_free(struct pldm_instance_db *ctx, pldm_tid_t tid, 17136af84cdSRashmica Gupta pldm_instance_id_t iid) 17236af84cdSRashmica Gupta { 17336af84cdSRashmica Gupta static const struct flock cflu = { 17436af84cdSRashmica Gupta .l_type = F_UNLCK, 17536af84cdSRashmica Gupta .l_whence = SEEK_SET, 17636af84cdSRashmica Gupta .l_len = 1, 17736af84cdSRashmica Gupta }; 17836af84cdSRashmica Gupta struct flock flop; 17936af84cdSRashmica Gupta int rc; 18036af84cdSRashmica Gupta 181*0aea707bSAndrew Jeffery /* Trying to free an instance ID that is not currently allocated */ 182*0aea707bSAndrew Jeffery if (!(ctx->state[tid].allocations & BIT(iid))) { 183*0aea707bSAndrew Jeffery return -EINVAL; 184*0aea707bSAndrew Jeffery } 185*0aea707bSAndrew Jeffery 18636af84cdSRashmica Gupta flop = cflu; 18736af84cdSRashmica Gupta flop.l_start = tid * PLDM_INST_ID_MAX + iid; 18836af84cdSRashmica Gupta rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop); 18936af84cdSRashmica Gupta if (rc < 0) { 19036af84cdSRashmica Gupta if (errno == EAGAIN || errno == EINTR) { 19136af84cdSRashmica Gupta return -EAGAIN; 19236af84cdSRashmica Gupta } 19336af84cdSRashmica Gupta return -EPROTO; 19436af84cdSRashmica Gupta } 19536af84cdSRashmica Gupta 196*0aea707bSAndrew Jeffery /* Mark the instance ID as no-longer allocated */ 197*0aea707bSAndrew Jeffery ctx->state[tid].allocations &= ~BIT(iid); 198*0aea707bSAndrew Jeffery 19936af84cdSRashmica Gupta return 0; 20036af84cdSRashmica Gupta } 201