1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 // Copyright (c) 2019 Mellanox Technologies
3 
4 #include <linux/mlx5/driver.h>
5 #include <linux/mlx5/device.h>
6 
7 #include "mlx5_core.h"
8 #include "lib/mlx5.h"
9 
10 struct mlx5_dm {
11 	/* protect access to icm bitmask */
12 	spinlock_t lock;
13 	unsigned long *steering_sw_icm_alloc_blocks;
14 	unsigned long *header_modify_sw_icm_alloc_blocks;
15 };
16 
mlx5_dm_create(struct mlx5_core_dev * dev)17 struct mlx5_dm *mlx5_dm_create(struct mlx5_core_dev *dev)
18 {
19 	u64 header_modify_icm_blocks = 0;
20 	u64 steering_icm_blocks = 0;
21 	struct mlx5_dm *dm;
22 
23 	if (!(MLX5_CAP_GEN_64(dev, general_obj_types) & MLX5_GENERAL_OBJ_TYPES_CAP_SW_ICM))
24 		return 0;
25 
26 	dm = kzalloc(sizeof(*dm), GFP_KERNEL);
27 	if (!dm)
28 		return ERR_PTR(-ENOMEM);
29 
30 	spin_lock_init(&dm->lock);
31 
32 	if (MLX5_CAP64_DEV_MEM(dev, steering_sw_icm_start_address)) {
33 		steering_icm_blocks =
34 			BIT(MLX5_CAP_DEV_MEM(dev, log_steering_sw_icm_size) -
35 			    MLX5_LOG_SW_ICM_BLOCK_SIZE(dev));
36 
37 		dm->steering_sw_icm_alloc_blocks =
38 			kcalloc(BITS_TO_LONGS(steering_icm_blocks),
39 				sizeof(unsigned long), GFP_KERNEL);
40 		if (!dm->steering_sw_icm_alloc_blocks)
41 			goto err_steering;
42 	}
43 
44 	if (MLX5_CAP64_DEV_MEM(dev, header_modify_sw_icm_start_address)) {
45 		header_modify_icm_blocks =
46 			BIT(MLX5_CAP_DEV_MEM(dev, log_header_modify_sw_icm_size) -
47 			    MLX5_LOG_SW_ICM_BLOCK_SIZE(dev));
48 
49 		dm->header_modify_sw_icm_alloc_blocks =
50 			kcalloc(BITS_TO_LONGS(header_modify_icm_blocks),
51 				sizeof(unsigned long), GFP_KERNEL);
52 		if (!dm->header_modify_sw_icm_alloc_blocks)
53 			goto err_modify_hdr;
54 	}
55 
56 	return dm;
57 
58 err_modify_hdr:
59 	kfree(dm->steering_sw_icm_alloc_blocks);
60 
61 err_steering:
62 	kfree(dm);
63 
64 	return ERR_PTR(-ENOMEM);
65 }
66 
mlx5_dm_cleanup(struct mlx5_core_dev * dev)67 void mlx5_dm_cleanup(struct mlx5_core_dev *dev)
68 {
69 	struct mlx5_dm *dm = dev->dm;
70 
71 	if (!dev->dm)
72 		return;
73 
74 	if (dm->steering_sw_icm_alloc_blocks) {
75 		WARN_ON(!bitmap_empty(dm->steering_sw_icm_alloc_blocks,
76 				      BIT(MLX5_CAP_DEV_MEM(dev, log_steering_sw_icm_size) -
77 					  MLX5_LOG_SW_ICM_BLOCK_SIZE(dev))));
78 		kfree(dm->steering_sw_icm_alloc_blocks);
79 	}
80 
81 	if (dm->header_modify_sw_icm_alloc_blocks) {
82 		WARN_ON(!bitmap_empty(dm->header_modify_sw_icm_alloc_blocks,
83 				      BIT(MLX5_CAP_DEV_MEM(dev,
84 							   log_header_modify_sw_icm_size) -
85 				      MLX5_LOG_SW_ICM_BLOCK_SIZE(dev))));
86 		kfree(dm->header_modify_sw_icm_alloc_blocks);
87 	}
88 
89 	kfree(dm);
90 }
91 
mlx5_dm_sw_icm_alloc(struct mlx5_core_dev * dev,enum mlx5_sw_icm_type type,u64 length,u16 uid,phys_addr_t * addr,u32 * obj_id)92 int mlx5_dm_sw_icm_alloc(struct mlx5_core_dev *dev, enum mlx5_sw_icm_type type,
93 			 u64 length, u16 uid, phys_addr_t *addr, u32 *obj_id)
94 {
95 	u32 num_blocks = DIV_ROUND_UP_ULL(length, MLX5_SW_ICM_BLOCK_SIZE(dev));
96 	u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {};
97 	u32 in[MLX5_ST_SZ_DW(create_sw_icm_in)] = {};
98 	struct mlx5_dm *dm = dev->dm;
99 	unsigned long *block_map;
100 	u64 icm_start_addr;
101 	u32 log_icm_size;
102 	u32 max_blocks;
103 	u64 block_idx;
104 	void *sw_icm;
105 	int ret;
106 
107 	if (!dev->dm)
108 		return -EOPNOTSUPP;
109 
110 	if (!length || (length & (length - 1)) ||
111 	    length & (MLX5_SW_ICM_BLOCK_SIZE(dev) - 1))
112 		return -EINVAL;
113 
114 	MLX5_SET(general_obj_in_cmd_hdr, in, opcode,
115 		 MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
116 	MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_OBJ_TYPE_SW_ICM);
117 	MLX5_SET(general_obj_in_cmd_hdr, in, uid, uid);
118 
119 	switch (type) {
120 	case MLX5_SW_ICM_TYPE_STEERING:
121 		icm_start_addr = MLX5_CAP64_DEV_MEM(dev, steering_sw_icm_start_address);
122 		log_icm_size = MLX5_CAP_DEV_MEM(dev, log_steering_sw_icm_size);
123 		block_map = dm->steering_sw_icm_alloc_blocks;
124 		break;
125 	case MLX5_SW_ICM_TYPE_HEADER_MODIFY:
126 		icm_start_addr = MLX5_CAP64_DEV_MEM(dev, header_modify_sw_icm_start_address);
127 		log_icm_size = MLX5_CAP_DEV_MEM(dev,
128 						log_header_modify_sw_icm_size);
129 		block_map = dm->header_modify_sw_icm_alloc_blocks;
130 		break;
131 	default:
132 		return -EINVAL;
133 	}
134 
135 	if (!block_map)
136 		return -EOPNOTSUPP;
137 
138 	max_blocks = BIT(log_icm_size - MLX5_LOG_SW_ICM_BLOCK_SIZE(dev));
139 	spin_lock(&dm->lock);
140 	block_idx = bitmap_find_next_zero_area(block_map,
141 					       max_blocks,
142 					       0,
143 					       num_blocks, 0);
144 
145 	if (block_idx < max_blocks)
146 		bitmap_set(block_map,
147 			   block_idx, num_blocks);
148 
149 	spin_unlock(&dm->lock);
150 
151 	if (block_idx >= max_blocks)
152 		return -ENOMEM;
153 
154 	sw_icm = MLX5_ADDR_OF(create_sw_icm_in, in, sw_icm);
155 	icm_start_addr += block_idx << MLX5_LOG_SW_ICM_BLOCK_SIZE(dev);
156 	MLX5_SET64(sw_icm, sw_icm, sw_icm_start_addr,
157 		   icm_start_addr);
158 	MLX5_SET(sw_icm, sw_icm, log_sw_icm_size, ilog2(length));
159 
160 	ret = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
161 	if (ret) {
162 		spin_lock(&dm->lock);
163 		bitmap_clear(block_map,
164 			     block_idx, num_blocks);
165 		spin_unlock(&dm->lock);
166 
167 		return ret;
168 	}
169 
170 	*addr = icm_start_addr;
171 	*obj_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
172 
173 	return 0;
174 }
175 EXPORT_SYMBOL_GPL(mlx5_dm_sw_icm_alloc);
176 
mlx5_dm_sw_icm_dealloc(struct mlx5_core_dev * dev,enum mlx5_sw_icm_type type,u64 length,u16 uid,phys_addr_t addr,u32 obj_id)177 int mlx5_dm_sw_icm_dealloc(struct mlx5_core_dev *dev, enum mlx5_sw_icm_type type,
178 			   u64 length, u16 uid, phys_addr_t addr, u32 obj_id)
179 {
180 	u32 num_blocks = DIV_ROUND_UP_ULL(length, MLX5_SW_ICM_BLOCK_SIZE(dev));
181 	u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)] = {};
182 	u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {};
183 	struct mlx5_dm *dm = dev->dm;
184 	unsigned long *block_map;
185 	u64 icm_start_addr;
186 	u64 start_idx;
187 	int err;
188 
189 	if (!dev->dm)
190 		return -EOPNOTSUPP;
191 
192 	switch (type) {
193 	case MLX5_SW_ICM_TYPE_STEERING:
194 		icm_start_addr = MLX5_CAP64_DEV_MEM(dev, steering_sw_icm_start_address);
195 		block_map = dm->steering_sw_icm_alloc_blocks;
196 		break;
197 	case MLX5_SW_ICM_TYPE_HEADER_MODIFY:
198 		icm_start_addr = MLX5_CAP64_DEV_MEM(dev, header_modify_sw_icm_start_address);
199 		block_map = dm->header_modify_sw_icm_alloc_blocks;
200 		break;
201 	default:
202 		return -EINVAL;
203 	}
204 
205 	MLX5_SET(general_obj_in_cmd_hdr, in, opcode,
206 		 MLX5_CMD_OP_DESTROY_GENERAL_OBJECT);
207 	MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_OBJ_TYPE_SW_ICM);
208 	MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, obj_id);
209 	MLX5_SET(general_obj_in_cmd_hdr, in, uid, uid);
210 
211 	err =  mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
212 	if (err)
213 		return err;
214 
215 	start_idx = (addr - icm_start_addr) >> MLX5_LOG_SW_ICM_BLOCK_SIZE(dev);
216 	spin_lock(&dm->lock);
217 	bitmap_clear(block_map,
218 		     start_idx, num_blocks);
219 	spin_unlock(&dm->lock);
220 
221 	return 0;
222 }
223 EXPORT_SYMBOL_GPL(mlx5_dm_sw_icm_dealloc);
224