1/*
2 * Copyright (c) 2013, Cisco Systems, Inc. All rights reserved.
3 *
4 * This program is free software; you may redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
9 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
10 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
11 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
12 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
13 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
14 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
15 * SOFTWARE.
16 *
17 */
18#include <linux/errno.h>
19#include <linux/module.h>
20#include <linux/pci.h>
21
22#include "usnic_ib.h"
23#include "vnic_resource.h"
24#include "usnic_log.h"
25#include "usnic_vnic.h"
26
27struct usnic_vnic {
28	struct vnic_dev			*vdev;
29	struct vnic_dev_bar		bar[PCI_NUM_RESOURCES];
30	struct usnic_vnic_res_chunk	chunks[USNIC_VNIC_RES_TYPE_MAX];
31	spinlock_t			res_lock;
32};
33
34static enum vnic_res_type _to_vnic_res_type(enum usnic_vnic_res_type res_type)
35{
36#define DEFINE_USNIC_VNIC_RES_AT(usnic_vnic_res_t, vnic_res_type, desc, val) \
37		vnic_res_type,
38#define DEFINE_USNIC_VNIC_RES(usnic_vnic_res_t, vnic_res_type, desc) \
39		vnic_res_type,
40	static enum vnic_res_type usnic_vnic_type_2_vnic_type[] = {
41						USNIC_VNIC_RES_TYPES};
42#undef DEFINE_USNIC_VNIC_RES
43#undef DEFINE_USNIC_VNIC_RES_AT
44
45	if (res_type >= USNIC_VNIC_RES_TYPE_MAX)
46		return RES_TYPE_MAX;
47
48	return usnic_vnic_type_2_vnic_type[res_type];
49}
50
51const char *usnic_vnic_res_type_to_str(enum usnic_vnic_res_type res_type)
52{
53#define DEFINE_USNIC_VNIC_RES_AT(usnic_vnic_res_t, vnic_res_type, desc, val) \
54		desc,
55#define DEFINE_USNIC_VNIC_RES(usnic_vnic_res_t, vnic_res_type, desc) \
56		desc,
57	static const char * const usnic_vnic_res_type_desc[] = {
58						USNIC_VNIC_RES_TYPES};
59#undef DEFINE_USNIC_VNIC_RES
60#undef DEFINE_USNIC_VNIC_RES_AT
61
62	if (res_type >= USNIC_VNIC_RES_TYPE_MAX)
63		return "unknown";
64
65	return usnic_vnic_res_type_desc[res_type];
66
67}
68
69const char *usnic_vnic_pci_name(struct usnic_vnic *vnic)
70{
71	return pci_name(usnic_vnic_get_pdev(vnic));
72}
73
74int usnic_vnic_dump(struct usnic_vnic *vnic, char *buf,
75			int buf_sz,
76			void *hdr_obj,
77			int (*printtitle)(void *, char*, int),
78			int (*printcols)(char *, int),
79			int (*printrow)(void *, char *, int))
80{
81	struct usnic_vnic_res_chunk *chunk;
82	struct usnic_vnic_res *res;
83	struct vnic_dev_bar *bar0;
84	int i, j, offset;
85
86	offset = 0;
87	bar0 = usnic_vnic_get_bar(vnic, 0);
88	offset += scnprintf(buf + offset, buf_sz - offset,
89			"VF:%hu BAR0 bus_addr=%pa vaddr=0x%p size=%ld ",
90			usnic_vnic_get_index(vnic),
91			&bar0->bus_addr,
92			bar0->vaddr, bar0->len);
93	if (printtitle)
94		offset += printtitle(hdr_obj, buf + offset, buf_sz - offset);
95	offset += scnprintf(buf + offset, buf_sz - offset, "\n");
96	offset += scnprintf(buf + offset, buf_sz - offset,
97			"|RES\t|CTRL_PIN\t\t|IN_USE\t");
98	if (printcols)
99		offset += printcols(buf + offset, buf_sz - offset);
100	offset += scnprintf(buf + offset, buf_sz - offset, "\n");
101
102	spin_lock(&vnic->res_lock);
103	for (i = 0; i < ARRAY_SIZE(vnic->chunks); i++) {
104		chunk = &vnic->chunks[i];
105		for (j = 0; j < chunk->cnt; j++) {
106			res = chunk->res[j];
107			offset += scnprintf(buf + offset, buf_sz - offset,
108					"|%s[%u]\t|0x%p\t|%u\t",
109					usnic_vnic_res_type_to_str(res->type),
110					res->vnic_idx, res->ctrl, !!res->owner);
111			if (printrow) {
112				offset += printrow(res->owner, buf + offset,
113							buf_sz - offset);
114			}
115			offset += scnprintf(buf + offset, buf_sz - offset,
116						"\n");
117		}
118	}
119	spin_unlock(&vnic->res_lock);
120	return offset;
121}
122
123void usnic_vnic_res_spec_update(struct usnic_vnic_res_spec *spec,
124				enum usnic_vnic_res_type trgt_type,
125				u16 cnt)
126{
127	int i;
128
129	for (i = 0; i < USNIC_VNIC_RES_TYPE_MAX; i++) {
130		if (spec->resources[i].type == trgt_type) {
131			spec->resources[i].cnt = cnt;
132			return;
133		}
134	}
135
136	WARN_ON(1);
137}
138
139int usnic_vnic_res_spec_satisfied(const struct usnic_vnic_res_spec *min_spec,
140					struct usnic_vnic_res_spec *res_spec)
141{
142	int found, i, j;
143
144	for (i = 0; i < USNIC_VNIC_RES_TYPE_MAX; i++) {
145		found = 0;
146
147		for (j = 0; j < USNIC_VNIC_RES_TYPE_MAX; j++) {
148			if (res_spec->resources[i].type !=
149				min_spec->resources[i].type)
150				continue;
151			found = 1;
152			if (min_spec->resources[i].cnt >
153					res_spec->resources[i].cnt)
154				return -EINVAL;
155			break;
156		}
157
158		if (!found)
159			return -EINVAL;
160	}
161	return 0;
162}
163
164int usnic_vnic_spec_dump(char *buf, int buf_sz,
165				struct usnic_vnic_res_spec *res_spec)
166{
167	enum usnic_vnic_res_type res_type;
168	int res_cnt;
169	int i;
170	int offset = 0;
171
172	for (i = 0; i < USNIC_VNIC_RES_TYPE_MAX; i++) {
173		res_type = res_spec->resources[i].type;
174		res_cnt = res_spec->resources[i].cnt;
175		offset += scnprintf(buf + offset, buf_sz - offset,
176				"Res: %s Cnt: %d ",
177				usnic_vnic_res_type_to_str(res_type),
178				res_cnt);
179	}
180
181	return offset;
182}
183
184int usnic_vnic_check_room(struct usnic_vnic *vnic,
185				struct usnic_vnic_res_spec *res_spec)
186{
187	int i;
188	enum usnic_vnic_res_type res_type;
189	int res_cnt;
190
191	for (i = 0; i < USNIC_VNIC_RES_TYPE_MAX; i++) {
192		res_type = res_spec->resources[i].type;
193		res_cnt = res_spec->resources[i].cnt;
194
195		if (res_type == USNIC_VNIC_RES_TYPE_EOL)
196			break;
197
198		if (res_cnt > usnic_vnic_res_free_cnt(vnic, res_type))
199			return -EBUSY;
200	}
201
202	return 0;
203}
204
205int usnic_vnic_res_cnt(struct usnic_vnic *vnic,
206				enum usnic_vnic_res_type type)
207{
208	return vnic->chunks[type].cnt;
209}
210
211int usnic_vnic_res_free_cnt(struct usnic_vnic *vnic,
212				enum usnic_vnic_res_type type)
213{
214	return vnic->chunks[type].free_cnt;
215}
216
217struct usnic_vnic_res_chunk *
218usnic_vnic_get_resources(struct usnic_vnic *vnic, enum usnic_vnic_res_type type,
219				int cnt, void *owner)
220{
221	struct usnic_vnic_res_chunk *src, *ret;
222	struct usnic_vnic_res *res;
223	int i;
224
225	if (usnic_vnic_res_free_cnt(vnic, type) < cnt || cnt < 1 || !owner)
226		return ERR_PTR(-EINVAL);
227
228	ret = kzalloc(sizeof(*ret), GFP_ATOMIC);
229	if (!ret) {
230		usnic_err("Failed to allocate chunk for %s - Out of memory\n",
231				usnic_vnic_pci_name(vnic));
232		return ERR_PTR(-ENOMEM);
233	}
234
235	ret->res = kzalloc(sizeof(*(ret->res))*cnt, GFP_ATOMIC);
236	if (!ret->res) {
237		usnic_err("Failed to allocate resources for %s. Out of memory\n",
238				usnic_vnic_pci_name(vnic));
239		kfree(ret);
240		return ERR_PTR(-ENOMEM);
241	}
242
243	spin_lock(&vnic->res_lock);
244	src = &vnic->chunks[type];
245	for (i = 0; i < src->cnt && ret->cnt < cnt; i++) {
246		res = src->res[i];
247		if (!res->owner) {
248			src->free_cnt--;
249			res->owner = owner;
250			ret->res[ret->cnt++] = res;
251		}
252	}
253
254	spin_unlock(&vnic->res_lock);
255	ret->type = type;
256	ret->vnic = vnic;
257	WARN_ON(ret->cnt != cnt);
258
259	return ret;
260}
261
262void usnic_vnic_put_resources(struct usnic_vnic_res_chunk *chunk)
263{
264
265	struct usnic_vnic_res *res;
266	int i;
267	struct usnic_vnic *vnic = chunk->vnic;
268
269	spin_lock(&vnic->res_lock);
270	while ((i = --chunk->cnt) >= 0) {
271		res = chunk->res[i];
272		chunk->res[i] = NULL;
273		res->owner = NULL;
274		vnic->chunks[res->type].free_cnt++;
275	}
276	spin_unlock(&vnic->res_lock);
277
278	kfree(chunk->res);
279	kfree(chunk);
280}
281
282u16 usnic_vnic_get_index(struct usnic_vnic *vnic)
283{
284	return usnic_vnic_get_pdev(vnic)->devfn - 1;
285}
286
287static int usnic_vnic_alloc_res_chunk(struct usnic_vnic *vnic,
288					enum usnic_vnic_res_type type,
289					struct usnic_vnic_res_chunk *chunk)
290{
291	int cnt, err, i;
292	struct usnic_vnic_res *res;
293
294	cnt = vnic_dev_get_res_count(vnic->vdev, _to_vnic_res_type(type));
295	if (cnt < 1)
296		return -EINVAL;
297
298	chunk->cnt = chunk->free_cnt = cnt;
299	chunk->res = kzalloc(sizeof(*(chunk->res))*cnt, GFP_KERNEL);
300	if (!chunk->res)
301		return -ENOMEM;
302
303	for (i = 0; i < cnt; i++) {
304		res = kzalloc(sizeof(*res), GFP_KERNEL);
305		if (!res) {
306			err = -ENOMEM;
307			goto fail;
308		}
309		res->type = type;
310		res->vnic_idx = i;
311		res->vnic = vnic;
312		res->ctrl = vnic_dev_get_res(vnic->vdev,
313						_to_vnic_res_type(type), i);
314		chunk->res[i] = res;
315	}
316
317	chunk->vnic = vnic;
318	return 0;
319fail:
320	for (i--; i >= 0; i--)
321		kfree(chunk->res[i]);
322	kfree(chunk->res);
323	return err;
324}
325
326static void usnic_vnic_free_res_chunk(struct usnic_vnic_res_chunk *chunk)
327{
328	int i;
329	for (i = 0; i < chunk->cnt; i++)
330		kfree(chunk->res[i]);
331	kfree(chunk->res);
332}
333
334static int usnic_vnic_discover_resources(struct pci_dev *pdev,
335						struct usnic_vnic *vnic)
336{
337	enum usnic_vnic_res_type res_type;
338	int i;
339	int err = 0;
340
341	for (i = 0; i < ARRAY_SIZE(vnic->bar); i++) {
342		if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM))
343			continue;
344		vnic->bar[i].len = pci_resource_len(pdev, i);
345		vnic->bar[i].vaddr = pci_iomap(pdev, i, vnic->bar[i].len);
346		if (!vnic->bar[i].vaddr) {
347			usnic_err("Cannot memory-map BAR %d, aborting\n",
348					i);
349			err = -ENODEV;
350			goto out_clean_bar;
351		}
352		vnic->bar[i].bus_addr = pci_resource_start(pdev, i);
353	}
354
355	vnic->vdev = vnic_dev_register(NULL, pdev, pdev, vnic->bar,
356			ARRAY_SIZE(vnic->bar));
357	if (!vnic->vdev) {
358		usnic_err("Failed to register device %s\n",
359				pci_name(pdev));
360		err = -EINVAL;
361		goto out_clean_bar;
362	}
363
364	for (res_type = USNIC_VNIC_RES_TYPE_EOL + 1;
365			res_type < USNIC_VNIC_RES_TYPE_MAX; res_type++) {
366		err = usnic_vnic_alloc_res_chunk(vnic, res_type,
367						&vnic->chunks[res_type]);
368		if (err) {
369			usnic_err("Failed to alloc res %s with err %d\n",
370					usnic_vnic_res_type_to_str(res_type),
371					err);
372			goto out_clean_chunks;
373		}
374	}
375
376	return 0;
377
378out_clean_chunks:
379	for (res_type--; res_type > USNIC_VNIC_RES_TYPE_EOL; res_type--)
380		usnic_vnic_free_res_chunk(&vnic->chunks[res_type]);
381	vnic_dev_unregister(vnic->vdev);
382out_clean_bar:
383	for (i = 0; i < ARRAY_SIZE(vnic->bar); i++) {
384		if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM))
385			continue;
386		if (!vnic->bar[i].vaddr)
387			break;
388
389		iounmap(vnic->bar[i].vaddr);
390	}
391
392	return err;
393}
394
395struct pci_dev *usnic_vnic_get_pdev(struct usnic_vnic *vnic)
396{
397	return vnic_dev_get_pdev(vnic->vdev);
398}
399
400struct vnic_dev_bar *usnic_vnic_get_bar(struct usnic_vnic *vnic,
401				int bar_num)
402{
403	return (bar_num < ARRAY_SIZE(vnic->bar)) ? &vnic->bar[bar_num] : NULL;
404}
405
406static void usnic_vnic_release_resources(struct usnic_vnic *vnic)
407{
408	int i;
409	struct pci_dev *pdev;
410	enum usnic_vnic_res_type res_type;
411
412	pdev = usnic_vnic_get_pdev(vnic);
413
414	for (res_type = USNIC_VNIC_RES_TYPE_EOL + 1;
415			res_type < USNIC_VNIC_RES_TYPE_MAX; res_type++)
416		usnic_vnic_free_res_chunk(&vnic->chunks[res_type]);
417
418	vnic_dev_unregister(vnic->vdev);
419
420	for (i = 0; i < ARRAY_SIZE(vnic->bar); i++) {
421		if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM))
422			continue;
423		iounmap(vnic->bar[i].vaddr);
424	}
425}
426
427struct usnic_vnic *usnic_vnic_alloc(struct pci_dev *pdev)
428{
429	struct usnic_vnic *vnic;
430	int err = 0;
431
432	if (!pci_is_enabled(pdev)) {
433		usnic_err("PCI dev %s is disabled\n", pci_name(pdev));
434		return ERR_PTR(-EINVAL);
435	}
436
437	vnic = kzalloc(sizeof(*vnic), GFP_KERNEL);
438	if (!vnic) {
439		usnic_err("Failed to alloc vnic for %s - out of memory\n",
440				pci_name(pdev));
441		return ERR_PTR(-ENOMEM);
442	}
443
444	spin_lock_init(&vnic->res_lock);
445
446	err = usnic_vnic_discover_resources(pdev, vnic);
447	if (err) {
448		usnic_err("Failed to discover %s resources with err %d\n",
449				pci_name(pdev), err);
450		goto out_free_vnic;
451	}
452
453	usnic_dbg("Allocated vnic for %s\n", usnic_vnic_pci_name(vnic));
454
455	return vnic;
456
457out_free_vnic:
458	kfree(vnic);
459
460	return ERR_PTR(err);
461}
462
463void usnic_vnic_free(struct usnic_vnic *vnic)
464{
465	usnic_vnic_release_resources(vnic);
466	kfree(vnic);
467}
468