162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright(c) 2016 Intel Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#ifndef DEF_RDMAVT_INCMR_H
762306a36Sopenharmony_ci#define DEF_RDMAVT_INCMR_H
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/*
1062306a36Sopenharmony_ci * For Memory Regions. This stuff should probably be moved into rdmavt/mr.h once
1162306a36Sopenharmony_ci * drivers no longer need access to the MR directly.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci#include <linux/percpu-refcount.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/*
1662306a36Sopenharmony_ci * A segment is a linear region of low physical memory.
1762306a36Sopenharmony_ci * Used by the verbs layer.
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_cistruct rvt_seg {
2062306a36Sopenharmony_ci	void *vaddr;
2162306a36Sopenharmony_ci	size_t length;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* The number of rvt_segs that fit in a page. */
2562306a36Sopenharmony_ci#define RVT_SEGSZ     (PAGE_SIZE / sizeof(struct rvt_seg))
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct rvt_segarray {
2862306a36Sopenharmony_ci	struct rvt_seg segs[RVT_SEGSZ];
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct rvt_mregion {
3262306a36Sopenharmony_ci	struct ib_pd *pd;       /* shares refcnt of ibmr.pd */
3362306a36Sopenharmony_ci	u64 user_base;          /* User's address for this region */
3462306a36Sopenharmony_ci	u64 iova;               /* IB start address of this region */
3562306a36Sopenharmony_ci	size_t length;
3662306a36Sopenharmony_ci	u32 lkey;
3762306a36Sopenharmony_ci	u32 offset;             /* offset (bytes) to start of region */
3862306a36Sopenharmony_ci	int access_flags;
3962306a36Sopenharmony_ci	u32 max_segs;           /* number of rvt_segs in all the arrays */
4062306a36Sopenharmony_ci	u32 mapsz;              /* size of the map array */
4162306a36Sopenharmony_ci	atomic_t lkey_invalid;	/* true if current lkey is invalid */
4262306a36Sopenharmony_ci	u8  page_shift;         /* 0 - non unform/non powerof2 sizes */
4362306a36Sopenharmony_ci	u8  lkey_published;     /* in global table */
4462306a36Sopenharmony_ci	struct percpu_ref refcount;
4562306a36Sopenharmony_ci	struct completion comp; /* complete when refcount goes to zero */
4662306a36Sopenharmony_ci	struct rvt_segarray *map[];    /* the segments */
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define RVT_MAX_LKEY_TABLE_BITS 23
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct rvt_lkey_table {
5262306a36Sopenharmony_ci	/* read mostly fields */
5362306a36Sopenharmony_ci	u32 max;                /* size of the table */
5462306a36Sopenharmony_ci	u32 shift;              /* lkey/rkey shift */
5562306a36Sopenharmony_ci	struct rvt_mregion __rcu **table;
5662306a36Sopenharmony_ci	/* writeable fields */
5762306a36Sopenharmony_ci	/* protect changes in this struct */
5862306a36Sopenharmony_ci	spinlock_t lock ____cacheline_aligned_in_smp;
5962306a36Sopenharmony_ci	u32 next;               /* next unused index (speeds search) */
6062306a36Sopenharmony_ci	u32 gen;                /* generation count */
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/*
6462306a36Sopenharmony_ci * These keep track of the copy progress within a memory region.
6562306a36Sopenharmony_ci * Used by the verbs layer.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistruct rvt_sge {
6862306a36Sopenharmony_ci	struct rvt_mregion *mr;
6962306a36Sopenharmony_ci	void *vaddr;            /* kernel virtual address of segment */
7062306a36Sopenharmony_ci	u32 sge_length;         /* length of the SGE */
7162306a36Sopenharmony_ci	u32 length;             /* remaining length of the segment */
7262306a36Sopenharmony_ci	u16 m;                  /* current index: mr->map[m] */
7362306a36Sopenharmony_ci	u16 n;                  /* current index: mr->map[m]->segs[n] */
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistruct rvt_sge_state {
7762306a36Sopenharmony_ci	struct rvt_sge *sg_list;      /* next SGE to be used if any */
7862306a36Sopenharmony_ci	struct rvt_sge sge;   /* progress state for the current SGE */
7962306a36Sopenharmony_ci	u32 total_len;
8062306a36Sopenharmony_ci	u8 num_sge;
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic inline void rvt_put_mr(struct rvt_mregion *mr)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	percpu_ref_put(&mr->refcount);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic inline void rvt_get_mr(struct rvt_mregion *mr)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	percpu_ref_get(&mr->refcount);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic inline void rvt_put_ss(struct rvt_sge_state *ss)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	while (ss->num_sge) {
9662306a36Sopenharmony_ci		rvt_put_mr(ss->sge.mr);
9762306a36Sopenharmony_ci		if (--ss->num_sge)
9862306a36Sopenharmony_ci			ss->sge = *ss->sg_list++;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic inline u32 rvt_get_sge_length(struct rvt_sge *sge, u32 length)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	u32 len = sge->length;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (len > length)
10762306a36Sopenharmony_ci		len = length;
10862306a36Sopenharmony_ci	if (len > sge->sge_length)
10962306a36Sopenharmony_ci		len = sge->sge_length;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return len;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic inline void rvt_update_sge(struct rvt_sge_state *ss, u32 length,
11562306a36Sopenharmony_ci				  bool release)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct rvt_sge *sge = &ss->sge;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	sge->vaddr += length;
12062306a36Sopenharmony_ci	sge->length -= length;
12162306a36Sopenharmony_ci	sge->sge_length -= length;
12262306a36Sopenharmony_ci	if (sge->sge_length == 0) {
12362306a36Sopenharmony_ci		if (release)
12462306a36Sopenharmony_ci			rvt_put_mr(sge->mr);
12562306a36Sopenharmony_ci		if (--ss->num_sge)
12662306a36Sopenharmony_ci			*sge = *ss->sg_list++;
12762306a36Sopenharmony_ci	} else if (sge->length == 0 && sge->mr->lkey) {
12862306a36Sopenharmony_ci		if (++sge->n >= RVT_SEGSZ) {
12962306a36Sopenharmony_ci			if (++sge->m >= sge->mr->mapsz)
13062306a36Sopenharmony_ci				return;
13162306a36Sopenharmony_ci			sge->n = 0;
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci		sge->vaddr = sge->mr->map[sge->m]->segs[sge->n].vaddr;
13462306a36Sopenharmony_ci		sge->length = sge->mr->map[sge->m]->segs[sge->n].length;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic inline void rvt_skip_sge(struct rvt_sge_state *ss, u32 length,
13962306a36Sopenharmony_ci				bool release)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct rvt_sge *sge = &ss->sge;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	while (length) {
14462306a36Sopenharmony_ci		u32 len = rvt_get_sge_length(sge, length);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		WARN_ON_ONCE(len == 0);
14762306a36Sopenharmony_ci		rvt_update_sge(ss, len, release);
14862306a36Sopenharmony_ci		length -= len;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cibool rvt_ss_has_lkey(struct rvt_sge_state *ss, u32 lkey);
15362306a36Sopenharmony_cibool rvt_mr_has_lkey(struct rvt_mregion *mr, u32 lkey);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci#endif          /* DEF_RDMAVT_INCMRH */
156