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