File Index
Files and directories
Sign in
FILE

mapping.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/en/mapping.c C
  1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
  2/* Copyright (c) 2018 Mellanox Technologies */
  3
  4#include <linux/jhash.h>
  5#include <linux/slab.h>
  6#include <linux/xarray.h>
  7#include <linux/hashtable.h>
  8#include <linux/refcount.h>
  9#include <linux/mlx5/driver.h>
 10
 11#include "mapping.h"
 12
 13#define MAPPING_GRACE_PERIOD 2000
 14
 15static LIST_HEAD(shared_ctx_list);
 16static DEFINE_MUTEX(shared_ctx_lock);
 17
 18struct mapping_ctx {
 19	struct xarray xarray;
 20	DECLARE_HASHTABLE(ht, 8);
 21	struct mutex lock; /* Guards hashtable and xarray */
 22	unsigned long max_id;
 23	size_t data_size;
 24	bool delayed_removal;
 25	struct delayed_work dwork;
 26	struct list_head pending_list;
 27	spinlock_t pending_list_lock; /* Guards pending list */
 28	u8 id[MLX5_SW_IMAGE_GUID_MAX_BYTES];
 29	u8 id_len;
 30	u8 type;
 31	struct list_head list;
 32	refcount_t refcount;
 33};
 34
 35struct mapping_item {
 36	struct rcu_head rcu;
 37	struct list_head list;
 38	unsigned long timeout;
 39	struct hlist_node node;
 40	int cnt;
 41	u32 id;
 42	char data[];
 43};
 44
 45int mapping_add(struct mapping_ctx *ctx, void *data, u32 *id)
 46{
 47	struct mapping_item *mi;
 48	int err = -ENOMEM;
 49	u32 hash_key;
 50
 51	mutex_lock(&ctx->lock);
 52
 53	hash_key = jhash(data, ctx->data_size, 0);
 54	hash_for_each_possible(ctx->ht, mi, node, hash_key) {
 55		if (!memcmp(data, mi->data, ctx->data_size))
 56			goto attach;
 57	}
 58
 59	mi = kzalloc(sizeof(*mi) + ctx->data_size, GFP_KERNEL);
 60	if (!mi)
 61		goto err_alloc;
 62
 63	memcpy(mi->data, data, ctx->data_size);
 64	hash_add(ctx->ht, &mi->node, hash_key);
 65
 66	err = xa_alloc(&ctx->xarray, &mi->id, mi, XA_LIMIT(1, ctx->max_id),
 67		       GFP_KERNEL);
 68	if (err)
 69		goto err_assign;
 70attach:
 71	++mi->cnt;
 72	*id = mi->id;
 73
 74	mutex_unlock(&ctx->lock);
 75
 76	return 0;
 77
 78err_assign:
 79	hash_del(&mi->node);
 80	kfree(mi);
 81err_alloc:
 82	mutex_unlock(&ctx->lock);
 83
 84	return err;
 85}
 86
 87static void mapping_remove_and_free(struct mapping_ctx *ctx,
 88				    struct mapping_item *mi)
 89{
 90	xa_erase(&ctx->xarray, mi->id);
 91	kfree_rcu(mi, rcu);
 92}
 93
 94static void mapping_free_item(struct mapping_ctx *ctx,
 95			      struct mapping_item *mi)
 96{
 97	if (!ctx->delayed_removal) {
 98		mapping_remove_and_free(ctx, mi);
 99		return;
100	}
101
102	mi->timeout = jiffies + msecs_to_jiffies(MAPPING_GRACE_PERIOD);
103
104	spin_lock(&ctx->pending_list_lock);
105	list_add_tail(&mi->list, &ctx->pending_list);
106	spin_unlock(&ctx->pending_list_lock);
107
108	schedule_delayed_work(&ctx->dwork, MAPPING_GRACE_PERIOD);
109}
110
111int mapping_remove(struct mapping_ctx *ctx, u32 id)
112{
113	unsigned long index = id;
114	struct mapping_item *mi;
115	int err = -ENOENT;
116
117	mutex_lock(&ctx->lock);
118	mi = xa_load(&ctx->xarray, index);
119	if (!mi)
120		goto out;
121	err = 0;
122
123	if (--mi->cnt > 0)
124		goto out;
125
126	hash_del(&mi->node);
127	mapping_free_item(ctx, mi);
128out:
129	mutex_unlock(&ctx->lock);
130
131	return err;
132}
133
134int mapping_find(struct mapping_ctx *ctx, u32 id, void *data)
135{
136	unsigned long index = id;
137	struct mapping_item *mi;
138	int err = -ENOENT;
139
140	rcu_read_lock();
141	mi = xa_load(&ctx->xarray, index);
142	if (!mi)
143		goto err_find;
144
145	memcpy(data, mi->data, ctx->data_size);
146	err = 0;
147
148err_find:
149	rcu_read_unlock();
150	return err;
151}
152
153static void
154mapping_remove_and_free_list(struct mapping_ctx *ctx, struct list_head *list)
155{
156	struct mapping_item *mi;
157
158	list_for_each_entry(mi, list, list)
159		mapping_remove_and_free(ctx, mi);
160}
161
162static void mapping_work_handler(struct work_struct *work)
163{
164	unsigned long min_timeout = 0, now = jiffies;
165	struct mapping_item *mi, *next;
166	LIST_HEAD(pending_items);
167	struct mapping_ctx *ctx;
168
169	ctx = container_of(work, struct mapping_ctx, dwork.work);
170
171	spin_lock(&ctx->pending_list_lock);
172	list_for_each_entry_safe(mi, next, &ctx->pending_list, list) {
173		if (time_after(now, mi->timeout))
174			list_move(&mi->list, &pending_items);
175		else if (!min_timeout ||
176			 time_before(mi->timeout, min_timeout))
177			min_timeout = mi->timeout;
178	}
179	spin_unlock(&ctx->pending_list_lock);
180
181	mapping_remove_and_free_list(ctx, &pending_items);
182
183	if (min_timeout)
184		schedule_delayed_work(&ctx->dwork, abs(min_timeout - now));
185}
186
187static void mapping_flush_work(struct mapping_ctx *ctx)
188{
189	if (!ctx->delayed_removal)
190		return;
191
192	cancel_delayed_work_sync(&ctx->dwork);
193	mapping_remove_and_free_list(ctx, &ctx->pending_list);
194}
195
196struct mapping_ctx *
197mapping_create(size_t data_size, u32 max_id, bool delayed_removal)
198{
199	struct mapping_ctx *ctx;
200
201	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
202	if (!ctx)
203		return ERR_PTR(-ENOMEM);
204
205	ctx->max_id = max_id ? max_id : UINT_MAX;
206	ctx->data_size = data_size;
207
208	if (delayed_removal) {
209		INIT_DELAYED_WORK(&ctx->dwork, mapping_work_handler);
210		INIT_LIST_HEAD(&ctx->pending_list);
211		spin_lock_init(&ctx->pending_list_lock);
212		ctx->delayed_removal = true;
213	}
214
215	mutex_init(&ctx->lock);
216	xa_init_flags(&ctx->xarray, XA_FLAGS_ALLOC1);
217
218	refcount_set(&ctx->refcount, 1);
219	INIT_LIST_HEAD(&ctx->list);
220
221	return ctx;
222}
223
224struct mapping_ctx *
225mapping_create_for_id(u8 *id, u8 id_len, u8 type, size_t data_size, u32 max_id,
226		      bool delayed_removal)
227{
228	struct mapping_ctx *ctx;
229
230	mutex_lock(&shared_ctx_lock);
231	list_for_each_entry(ctx, &shared_ctx_list, list) {
232		if (ctx->type == type && ctx->id_len == id_len &&
233		    !memcmp(id, ctx->id, id_len)) {
234			if (refcount_inc_not_zero(&ctx->refcount))
235				goto unlock;
236			break;
237		}
238	}
239
240	ctx = mapping_create(data_size, max_id, delayed_removal);
241	if (IS_ERR(ctx))
242		goto unlock;
243
244	memcpy(ctx->id, id, id_len);
245	ctx->id_len = id_len;
246	ctx->type = type;
247	list_add(&ctx->list, &shared_ctx_list);
248
249unlock:
250	mutex_unlock(&shared_ctx_lock);
251	return ctx;
252}
253
254void mapping_destroy(struct mapping_ctx *ctx)
255{
256	if (!refcount_dec_and_test(&ctx->refcount))
257		return;
258
259	mutex_lock(&shared_ctx_lock);
260	list_del(&ctx->list);
261	mutex_unlock(&shared_ctx_lock);
262
263	mapping_flush_work(ctx);
264	xa_destroy(&ctx->xarray);
265	mutex_destroy(&ctx->lock);
266
267	kfree(ctx);
268}
~/ Read-only file preview.