net/mlx5: sparse: warning: Using plain integer as NULL pointer
[linux-block.git] / drivers / net / ethernet / mellanox / mlx5 / core / lib / dm.c
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
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 NULL;
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
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
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
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);