root/drivers/infiniband/hw/usnic/usnic_vnic.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. _to_vnic_res_type
  2. usnic_vnic_res_type_to_str
  3. usnic_vnic_pci_name
  4. usnic_vnic_dump
  5. usnic_vnic_res_spec_update
  6. usnic_vnic_res_spec_satisfied
  7. usnic_vnic_spec_dump
  8. usnic_vnic_check_room
  9. usnic_vnic_res_cnt
  10. usnic_vnic_res_free_cnt
  11. usnic_vnic_get_resources
  12. usnic_vnic_put_resources
  13. usnic_vnic_get_index
  14. usnic_vnic_alloc_res_chunk
  15. usnic_vnic_free_res_chunk
  16. usnic_vnic_discover_resources
  17. usnic_vnic_get_pdev
  18. usnic_vnic_get_bar
  19. usnic_vnic_release_resources
  20. usnic_vnic_alloc
  21. usnic_vnic_free

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

/* [<][>][^][v][top][bottom][index][help] */