xref: /openbmc/libpldm/src/requester/instance-id.c (revision 36af84cdbb660953d73804a12dfda056c3ac9b08)
1  // NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
2  #define _GNU_SOURCE
3  #include "libpldm/requester/instance-id.h"
4  #include "libpldm/pldm.h"
5  #include <errno.h>
6  #include <fcntl.h>
7  #include <stdlib.h>
8  #include <unistd.h>
9  
10  #define PLDM_TID_MAX 256
11  #define PLDM_INST_ID_MAX 32
12  
13  struct pldm_instance_db {
14  	pldm_instance_id_t prev[PLDM_TID_MAX];
15  	int lock_db_fd;
16  };
17  
18  static inline int iid_next(pldm_instance_id_t cur)
19  {
20  	return (cur + 1) % PLDM_INST_ID_MAX;
21  }
22  
23  int pldm_instance_db_init(struct pldm_instance_db **ctx, const char *dbpath)
24  {
25  	struct pldm_instance_db *l_ctx;
26  
27  	/* Make sure the provided pointer was initialised to NULL. In the future
28  	 * if we stabilise the ABI and expose the struct definition the caller
29  	 * can potentially pass a valid pointer to a struct they've allocated
30  	 */
31  	if (!ctx || *ctx) {
32  		return -EINVAL;
33  	}
34  
35  	l_ctx = calloc(1, sizeof(struct pldm_instance_db));
36  	if (!l_ctx) {
37  		return -ENOMEM;
38  	}
39  
40  	/* Initialise previous ID values so the next one is zero */
41  	for (int i = 0; i < PLDM_TID_MAX; i++) {
42  		l_ctx->prev[i] = 31;
43  	}
44  
45  	/* Lock database may be read-only, either by permissions or mountpoint
46  	 */
47  	l_ctx->lock_db_fd = open(dbpath, O_RDONLY | O_CLOEXEC);
48  	if (l_ctx->lock_db_fd < 0) {
49  		free(l_ctx);
50  		return -errno;
51  	}
52  	*ctx = l_ctx;
53  
54  	return 0;
55  }
56  
57  int pldm_instance_db_init_default(struct pldm_instance_db **ctx)
58  {
59  	return pldm_instance_db_init(ctx,
60  				     "/usr/share/libpldm/instance-db/default");
61  }
62  
63  int pldm_instance_db_destroy(struct pldm_instance_db *ctx)
64  {
65  	if (!ctx) {
66  		return 0;
67  	}
68  	close(ctx->lock_db_fd);
69  	free(ctx);
70  	return 0;
71  }
72  
73  int pldm_instance_id_alloc(struct pldm_instance_db *ctx, pldm_tid_t tid,
74  			   pldm_instance_id_t *iid)
75  {
76  	static const struct flock cfls = {
77  	    .l_type = F_RDLCK,
78  	    .l_whence = SEEK_SET,
79  	    .l_len = 1,
80  	};
81  	static const struct flock cflx = {
82  	    .l_type = F_WRLCK,
83  	    .l_whence = SEEK_SET,
84  	    .l_len = 1,
85  	};
86  	uint8_t l_iid;
87  
88  	if (!iid) {
89  		return -EINVAL;
90  	}
91  
92  	l_iid = ctx->prev[tid];
93  	if (l_iid >= PLDM_INST_ID_MAX) {
94  		return -EPROTO;
95  	}
96  
97  	while ((l_iid = iid_next(l_iid)) != ctx->prev[tid]) {
98  		struct flock flop;
99  		off_t loff;
100  		int rc;
101  
102  		/* Derive the instance ID offset in the lock database */
103  		loff = tid * PLDM_INST_ID_MAX + l_iid;
104  
105  		/* Reserving the TID's IID. Done via a shared lock */
106  		flop = cfls;
107  		flop.l_start = loff;
108  		rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
109  		if (rc < 0) {
110  			if (errno == EAGAIN || errno == EINTR) {
111  				return -EAGAIN;
112  			}
113  			return -EPROTO;
114  		}
115  
116  		/*
117  		 * If we *may* promote the lock to exclusive then this IID is
118  		 * only reserved by us. This is now our allocated IID.
119  		 *
120  		 * If we *may not* promote the lock to exclusive then this IID
121  		 * is also reserved on another file descriptor. Move on to the
122  		 * next IID index.
123  		 *
124  		 * Note that we cannot actually *perform* the promotion in
125  		 * practice because this is prevented by the lock database being
126  		 * opened O_RDONLY.
127  		 */
128  		flop = cflx;
129  		flop.l_start = loff;
130  		rc = fcntl(ctx->lock_db_fd, F_OFD_GETLK, &flop);
131  		if (rc < 0) {
132  			if (errno == EAGAIN || errno == EINTR) {
133  				return -EAGAIN;
134  			}
135  			return -EPROTO;
136  		}
137  
138  		/* F_UNLCK is the type of the lock if we could successfully
139  		 * promote it to F_WRLCK */
140  		if (flop.l_type == F_UNLCK) {
141  			ctx->prev[tid] = l_iid;
142  			*iid = l_iid;
143  			return 0;
144  		}
145  		if (flop.l_type != F_RDLCK) {
146  			return -EPROTO;
147  		}
148  	}
149  
150  	/* Failed to allocate an IID after a full loop. Make the caller try
151  	 * again */
152  	return -EAGAIN;
153  }
154  
155  int pldm_instance_id_free(struct pldm_instance_db *ctx, pldm_tid_t tid,
156  			  pldm_instance_id_t iid)
157  {
158  	static const struct flock cflu = {
159  	    .l_type = F_UNLCK,
160  	    .l_whence = SEEK_SET,
161  	    .l_len = 1,
162  	};
163  	struct flock flop;
164  	int rc;
165  
166  	flop = cflu;
167  	flop.l_start = tid * PLDM_INST_ID_MAX + iid;
168  	rc = fcntl(ctx->lock_db_fd, F_OFD_SETLK, &flop);
169  	if (rc < 0) {
170  		if (errno == EAGAIN || errno == EINTR) {
171  			return -EAGAIN;
172  		}
173  		return -EPROTO;
174  	}
175  
176  	return 0;
177  }
178