linux/drivers/infiniband/hw/ipath/ipath_diag.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2006, 2007, 2008 QLogic Corporation. All rights reserved.
   3 * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved.
   4 *
   5 * This software is available to you under a choice of one of two
   6 * licenses.  You may choose to be licensed under the terms of the GNU
   7 * General Public License (GPL) Version 2, available from the file
   8 * COPYING in the main directory of this source tree, or the
   9 * OpenIB.org BSD license below:
  10 *
  11 *     Redistribution and use in source and binary forms, with or
  12 *     without modification, are permitted provided that the following
  13 *     conditions are met:
  14 *
  15 *      - Redistributions of source code must retain the above
  16 *        copyright notice, this list of conditions and the following
  17 *        disclaimer.
  18 *
  19 *      - Redistributions in binary form must reproduce the above
  20 *        copyright notice, this list of conditions and the following
  21 *        disclaimer in the documentation and/or other materials
  22 *        provided with the distribution.
  23 *
  24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31 * SOFTWARE.
  32 */
  33
  34/*
  35 * This file contains support for diagnostic functions.  It is accessed by
  36 * opening the ipath_diag device, normally minor number 129.  Diagnostic use
  37 * of the InfiniPath chip may render the chip or board unusable until the
  38 * driver is unloaded, or in some cases, until the system is rebooted.
  39 *
  40 * Accesses to the chip through this interface are not similar to going
  41 * through the /sys/bus/pci resource mmap interface.
  42 */
  43
  44#include <linux/io.h>
  45#include <linux/pci.h>
  46#include <linux/vmalloc.h>
  47#include <linux/fs.h>
  48#include <asm/uaccess.h>
  49
  50#include "ipath_kernel.h"
  51#include "ipath_common.h"
  52
  53int ipath_diag_inuse;
  54static int diag_set_link;
  55
  56static int ipath_diag_open(struct inode *in, struct file *fp);
  57static int ipath_diag_release(struct inode *in, struct file *fp);
  58static ssize_t ipath_diag_read(struct file *fp, char __user *data,
  59                               size_t count, loff_t *off);
  60static ssize_t ipath_diag_write(struct file *fp, const char __user *data,
  61                                size_t count, loff_t *off);
  62
  63static const struct file_operations diag_file_ops = {
  64        .owner = THIS_MODULE,
  65        .write = ipath_diag_write,
  66        .read = ipath_diag_read,
  67        .open = ipath_diag_open,
  68        .release = ipath_diag_release
  69};
  70
  71static ssize_t ipath_diagpkt_write(struct file *fp,
  72                                   const char __user *data,
  73                                   size_t count, loff_t *off);
  74
  75static const struct file_operations diagpkt_file_ops = {
  76        .owner = THIS_MODULE,
  77        .write = ipath_diagpkt_write,
  78};
  79
  80static atomic_t diagpkt_count = ATOMIC_INIT(0);
  81static struct cdev *diagpkt_cdev;
  82static struct device *diagpkt_dev;
  83
  84int ipath_diag_add(struct ipath_devdata *dd)
  85{
  86        char name[16];
  87        int ret = 0;
  88
  89        if (atomic_inc_return(&diagpkt_count) == 1) {
  90                ret = ipath_cdev_init(IPATH_DIAGPKT_MINOR,
  91                                      "ipath_diagpkt", &diagpkt_file_ops,
  92                                      &diagpkt_cdev, &diagpkt_dev);
  93
  94                if (ret) {
  95                        ipath_dev_err(dd, "Couldn't create ipath_diagpkt "
  96                                      "device: %d", ret);
  97                        goto done;
  98                }
  99        }
 100
 101        snprintf(name, sizeof(name), "ipath_diag%d", dd->ipath_unit);
 102
 103        ret = ipath_cdev_init(IPATH_DIAG_MINOR_BASE + dd->ipath_unit, name,
 104                              &diag_file_ops, &dd->diag_cdev,
 105                              &dd->diag_dev);
 106        if (ret)
 107                ipath_dev_err(dd, "Couldn't create %s device: %d",
 108                              name, ret);
 109
 110done:
 111        return ret;
 112}
 113
 114void ipath_diag_remove(struct ipath_devdata *dd)
 115{
 116        if (atomic_dec_and_test(&diagpkt_count))
 117                ipath_cdev_cleanup(&diagpkt_cdev, &diagpkt_dev);
 118
 119        ipath_cdev_cleanup(&dd->diag_cdev, &dd->diag_dev);
 120}
 121
 122/**
 123 * ipath_read_umem64 - read a 64-bit quantity from the chip into user space
 124 * @dd: the infinipath device
 125 * @uaddr: the location to store the data in user memory
 126 * @caddr: the source chip address (full pointer, not offset)
 127 * @count: number of bytes to copy (multiple of 32 bits)
 128 *
 129 * This function also localizes all chip memory accesses.
 130 * The copy should be written such that we read full cacheline packets
 131 * from the chip.  This is usually used for a single qword
 132 *
 133 * NOTE:  This assumes the chip address is 64-bit aligned.
 134 */
 135static int ipath_read_umem64(struct ipath_devdata *dd, void __user *uaddr,
 136                             const void __iomem *caddr, size_t count)
 137{
 138        const u64 __iomem *reg_addr = caddr;
 139        const u64 __iomem *reg_end = reg_addr + (count / sizeof(u64));
 140        int ret;
 141
 142        /* not very efficient, but it works for now */
 143        if (reg_addr < dd->ipath_kregbase || reg_end > dd->ipath_kregend) {
 144                ret = -EINVAL;
 145                goto bail;
 146        }
 147        while (reg_addr < reg_end) {
 148                u64 data = readq(reg_addr);
 149                if (copy_to_user(uaddr, &data, sizeof(u64))) {
 150                        ret = -EFAULT;
 151                        goto bail;
 152                }
 153                reg_addr++;
 154                uaddr += sizeof(u64);
 155        }
 156        ret = 0;
 157bail:
 158        return ret;
 159}
 160
 161/**
 162 * ipath_write_umem64 - write a 64-bit quantity to the chip from user space
 163 * @dd: the infinipath device
 164 * @caddr: the destination chip address (full pointer, not offset)
 165 * @uaddr: the source of the data in user memory
 166 * @count: the number of bytes to copy (multiple of 32 bits)
 167 *
 168 * This is usually used for a single qword
 169 * NOTE:  This assumes the chip address is 64-bit aligned.
 170 */
 171
 172static int ipath_write_umem64(struct ipath_devdata *dd, void __iomem *caddr,
 173                              const void __user *uaddr, size_t count)
 174{
 175        u64 __iomem *reg_addr = caddr;
 176        const u64 __iomem *reg_end = reg_addr + (count / sizeof(u64));
 177        int ret;
 178
 179        /* not very efficient, but it works for now */
 180        if (reg_addr < dd->ipath_kregbase || reg_end > dd->ipath_kregend) {
 181                ret = -EINVAL;
 182                goto bail;
 183        }
 184        while (reg_addr < reg_end) {
 185                u64 data;
 186                if (copy_from_user(&data, uaddr, sizeof(data))) {
 187                        ret = -EFAULT;
 188                        goto bail;
 189                }
 190                writeq(data, reg_addr);
 191
 192                reg_addr++;
 193                uaddr += sizeof(u64);
 194        }
 195        ret = 0;
 196bail:
 197        return ret;
 198}
 199
 200/**
 201 * ipath_read_umem32 - read a 32-bit quantity from the chip into user space
 202 * @dd: the infinipath device
 203 * @uaddr: the location to store the data in user memory
 204 * @caddr: the source chip address (full pointer, not offset)
 205 * @count: number of bytes to copy
 206 *
 207 * read 32 bit values, not 64 bit; for memories that only
 208 * support 32 bit reads; usually a single dword.
 209 */
 210static int ipath_read_umem32(struct ipath_devdata *dd, void __user *uaddr,
 211                             const void __iomem *caddr, size_t count)
 212{
 213        const u32 __iomem *reg_addr = caddr;
 214        const u32 __iomem *reg_end = reg_addr + (count / sizeof(u32));
 215        int ret;
 216
 217        if (reg_addr < (u32 __iomem *) dd->ipath_kregbase ||
 218            reg_end > (u32 __iomem *) dd->ipath_kregend) {
 219                ret = -EINVAL;
 220                goto bail;
 221        }
 222        /* not very efficient, but it works for now */
 223        while (reg_addr < reg_end) {
 224                u32 data = readl(reg_addr);
 225                if (copy_to_user(uaddr, &data, sizeof(data))) {
 226                        ret = -EFAULT;
 227                        goto bail;
 228                }
 229
 230                reg_addr++;
 231                uaddr += sizeof(u32);
 232
 233        }
 234        ret = 0;
 235bail:
 236        return ret;
 237}
 238
 239/**
 240 * ipath_write_umem32 - write a 32-bit quantity to the chip from user space
 241 * @dd: the infinipath device
 242 * @caddr: the destination chip address (full pointer, not offset)
 243 * @uaddr: the source of the data in user memory
 244 * @count: number of bytes to copy
 245 *
 246 * write 32 bit values, not 64 bit; for memories that only
 247 * support 32 bit write; usually a single dword.
 248 */
 249
 250static int ipath_write_umem32(struct ipath_devdata *dd, void __iomem *caddr,
 251                              const void __user *uaddr, size_t count)
 252{
 253        u32 __iomem *reg_addr = caddr;
 254        const u32 __iomem *reg_end = reg_addr + (count / sizeof(u32));
 255        int ret;
 256
 257        if (reg_addr < (u32 __iomem *) dd->ipath_kregbase ||
 258            reg_end > (u32 __iomem *) dd->ipath_kregend) {
 259                ret = -EINVAL;
 260                goto bail;
 261        }
 262        while (reg_addr < reg_end) {
 263                u32 data;
 264                if (copy_from_user(&data, uaddr, sizeof(data))) {
 265                        ret = -EFAULT;
 266                        goto bail;
 267                }
 268                writel(data, reg_addr);
 269
 270                reg_addr++;
 271                uaddr += sizeof(u32);
 272        }
 273        ret = 0;
 274bail:
 275        return ret;
 276}
 277
 278static int ipath_diag_open(struct inode *in, struct file *fp)
 279{
 280        int unit = iminor(in) - IPATH_DIAG_MINOR_BASE;
 281        struct ipath_devdata *dd;
 282        int ret;
 283
 284        mutex_lock(&ipath_mutex);
 285
 286        if (ipath_diag_inuse) {
 287                ret = -EBUSY;
 288                goto bail;
 289        }
 290
 291        dd = ipath_lookup(unit);
 292
 293        if (dd == NULL || !(dd->ipath_flags & IPATH_PRESENT) ||
 294            !dd->ipath_kregbase) {
 295                ret = -ENODEV;
 296                goto bail;
 297        }
 298
 299        fp->private_data = dd;
 300        ipath_diag_inuse = -2;
 301        diag_set_link = 0;
 302        ret = 0;
 303
 304        /* Only expose a way to reset the device if we
 305           make it into diag mode. */
 306        ipath_expose_reset(&dd->pcidev->dev);
 307
 308bail:
 309        mutex_unlock(&ipath_mutex);
 310
 311        return ret;
 312}
 313
 314/**
 315 * ipath_diagpkt_write - write an IB packet
 316 * @fp: the diag data device file pointer
 317 * @data: ipath_diag_pkt structure saying where to get the packet
 318 * @count: size of data to write
 319 * @off: unused by this code
 320 */
 321static ssize_t ipath_diagpkt_write(struct file *fp,
 322                                   const char __user *data,
 323                                   size_t count, loff_t *off)
 324{
 325        u32 __iomem *piobuf;
 326        u32 plen, clen, pbufn;
 327        struct ipath_diag_pkt odp;
 328        struct ipath_diag_xpkt dp;
 329        u32 *tmpbuf = NULL;
 330        struct ipath_devdata *dd;
 331        ssize_t ret = 0;
 332        u64 val;
 333        u32 l_state, lt_state; /* LinkState, LinkTrainingState */
 334
 335        if (count < sizeof(odp)) {
 336                ret = -EINVAL;
 337                goto bail;
 338        }
 339
 340        if (count == sizeof(dp)) {
 341                if (copy_from_user(&dp, data, sizeof(dp))) {
 342                        ret = -EFAULT;
 343                        goto bail;
 344                }
 345        } else if (copy_from_user(&odp, data, sizeof(odp))) {
 346                ret = -EFAULT;
 347                goto bail;
 348        }
 349
 350        /*
 351         * Due to padding/alignment issues (lessened with new struct)
 352         * the old and new structs are the same length. We need to
 353         * disambiguate them, which we can do because odp.len has never
 354         * been less than the total of LRH+BTH+DETH so far, while
 355         * dp.unit (same offset) unit is unlikely to get that high.
 356         * Similarly, dp.data, the pointer to user at the same offset
 357         * as odp.unit, is almost certainly at least one (512byte)page
 358         * "above" NULL. The if-block below can be omitted if compatibility
 359         * between a new driver and older diagnostic code is unimportant.
 360         * compatibility the other direction (new diags, old driver) is
 361         * handled in the diagnostic code, with a warning.
 362         */
 363        if (dp.unit >= 20 && dp.data < 512) {
 364                /* very probable version mismatch. Fix it up */
 365                memcpy(&odp, &dp, sizeof(odp));
 366                /* We got a legacy dp, copy elements to dp */
 367                dp.unit = odp.unit;
 368                dp.data = odp.data;
 369                dp.len = odp.len;
 370                dp.pbc_wd = 0; /* Indicate we need to compute PBC wd */
 371        }
 372
 373        /* send count must be an exact number of dwords */
 374        if (dp.len & 3) {
 375                ret = -EINVAL;
 376                goto bail;
 377        }
 378
 379        clen = dp.len >> 2;
 380
 381        dd = ipath_lookup(dp.unit);
 382        if (!dd || !(dd->ipath_flags & IPATH_PRESENT) ||
 383            !dd->ipath_kregbase) {
 384                ipath_cdbg(VERBOSE, "illegal unit %u for diag data send\n",
 385                           dp.unit);
 386                ret = -ENODEV;
 387                goto bail;
 388        }
 389
 390        if (ipath_diag_inuse && !diag_set_link &&
 391            !(dd->ipath_flags & IPATH_LINKACTIVE)) {
 392                diag_set_link = 1;
 393                ipath_cdbg(VERBOSE, "Trying to set to set link active for "
 394                           "diag pkt\n");
 395                ipath_set_linkstate(dd, IPATH_IB_LINKARM);
 396                ipath_set_linkstate(dd, IPATH_IB_LINKACTIVE);
 397        }
 398
 399        if (!(dd->ipath_flags & IPATH_INITTED)) {
 400                /* no hardware, freeze, etc. */
 401                ipath_cdbg(VERBOSE, "unit %u not usable\n", dd->ipath_unit);
 402                ret = -ENODEV;
 403                goto bail;
 404        }
 405        /*
 406         * Want to skip check for l_state if using custom PBC,
 407         * because we might be trying to force an SM packet out.
 408         * first-cut, skip _all_ state checking in that case.
 409         */
 410        val = ipath_ib_state(dd, dd->ipath_lastibcstat);
 411        lt_state = ipath_ib_linktrstate(dd, dd->ipath_lastibcstat);
 412        l_state = ipath_ib_linkstate(dd, dd->ipath_lastibcstat);
 413        if (!dp.pbc_wd && (lt_state != INFINIPATH_IBCS_LT_STATE_LINKUP ||
 414            (val != dd->ib_init && val != dd->ib_arm &&
 415            val != dd->ib_active))) {
 416                ipath_cdbg(VERBOSE, "unit %u not ready (state %llx)\n",
 417                           dd->ipath_unit, (unsigned long long) val);
 418                ret = -EINVAL;
 419                goto bail;
 420        }
 421
 422        /* need total length before first word written */
 423        /* +1 word is for the qword padding */
 424        plen = sizeof(u32) + dp.len;
 425
 426        if ((plen + 4) > dd->ipath_ibmaxlen) {
 427                ipath_dbg("Pkt len 0x%x > ibmaxlen %x\n",
 428                          plen - 4, dd->ipath_ibmaxlen);
 429                ret = -EINVAL;
 430                goto bail;      /* before writing pbc */
 431        }
 432        tmpbuf = vmalloc(plen);
 433        if (!tmpbuf) {
 434                dev_info(&dd->pcidev->dev, "Unable to allocate tmp buffer, "
 435                         "failing\n");
 436                ret = -ENOMEM;
 437                goto bail;
 438        }
 439
 440        if (copy_from_user(tmpbuf,
 441                           (const void __user *) (unsigned long) dp.data,
 442                           dp.len)) {
 443                ret = -EFAULT;
 444                goto bail;
 445        }
 446
 447        plen >>= 2;             /* in dwords */
 448
 449        piobuf = ipath_getpiobuf(dd, plen, &pbufn);
 450        if (!piobuf) {
 451                ipath_cdbg(VERBOSE, "No PIO buffers avail unit for %u\n",
 452                           dd->ipath_unit);
 453                ret = -EBUSY;
 454                goto bail;
 455        }
 456        /* disarm it just to be extra sure */
 457        ipath_disarm_piobufs(dd, pbufn, 1);
 458
 459        if (ipath_debug & __IPATH_PKTDBG)
 460                ipath_cdbg(VERBOSE, "unit %u 0x%x+1w pio%d\n",
 461                           dd->ipath_unit, plen - 1, pbufn);
 462
 463        if (dp.pbc_wd == 0)
 464                dp.pbc_wd = plen;
 465        writeq(dp.pbc_wd, piobuf);
 466        /*
 467         * Copy all by the trigger word, then flush, so it's written
 468         * to chip before trigger word, then write trigger word, then
 469         * flush again, so packet is sent.
 470         */
 471        if (dd->ipath_flags & IPATH_PIO_FLUSH_WC) {
 472                ipath_flush_wc();
 473                __iowrite32_copy(piobuf + 2, tmpbuf, clen - 1);
 474                ipath_flush_wc();
 475                __raw_writel(tmpbuf[clen - 1], piobuf + clen + 1);
 476        } else
 477                __iowrite32_copy(piobuf + 2, tmpbuf, clen);
 478
 479        ipath_flush_wc();
 480
 481        ret = sizeof(dp);
 482
 483bail:
 484        vfree(tmpbuf);
 485        return ret;
 486}
 487
 488static int ipath_diag_release(struct inode *in, struct file *fp)
 489{
 490        mutex_lock(&ipath_mutex);
 491        ipath_diag_inuse = 0;
 492        fp->private_data = NULL;
 493        mutex_unlock(&ipath_mutex);
 494        return 0;
 495}
 496
 497static ssize_t ipath_diag_read(struct file *fp, char __user *data,
 498                               size_t count, loff_t *off)
 499{
 500        struct ipath_devdata *dd = fp->private_data;
 501        void __iomem *kreg_base;
 502        ssize_t ret;
 503
 504        kreg_base = dd->ipath_kregbase;
 505
 506        if (count == 0)
 507                ret = 0;
 508        else if ((count % 4) || (*off % 4))
 509                /* address or length is not 32-bit aligned, hence invalid */
 510                ret = -EINVAL;
 511        else if (ipath_diag_inuse < 1 && (*off || count != 8))
 512                ret = -EINVAL;  /* prevent cat /dev/ipath_diag* */
 513        else if ((count % 8) || (*off % 8))
 514                /* address or length not 64-bit aligned; do 32-bit reads */
 515                ret = ipath_read_umem32(dd, data, kreg_base + *off, count);
 516        else
 517                ret = ipath_read_umem64(dd, data, kreg_base + *off, count);
 518
 519        if (ret >= 0) {
 520                *off += count;
 521                ret = count;
 522                if (ipath_diag_inuse == -2)
 523                        ipath_diag_inuse++;
 524        }
 525
 526        return ret;
 527}
 528
 529static ssize_t ipath_diag_write(struct file *fp, const char __user *data,
 530                                size_t count, loff_t *off)
 531{
 532        struct ipath_devdata *dd = fp->private_data;
 533        void __iomem *kreg_base;
 534        ssize_t ret;
 535
 536        kreg_base = dd->ipath_kregbase;
 537
 538        if (count == 0)
 539                ret = 0;
 540        else if ((count % 4) || (*off % 4))
 541                /* address or length is not 32-bit aligned, hence invalid */
 542                ret = -EINVAL;
 543        else if ((ipath_diag_inuse == -1 && (*off || count != 8)) ||
 544                 ipath_diag_inuse == -2)  /* read qw off 0, write qw off 0 */
 545                ret = -EINVAL;  /* before any other write allowed */
 546        else if ((count % 8) || (*off % 8))
 547                /* address or length not 64-bit aligned; do 32-bit writes */
 548                ret = ipath_write_umem32(dd, kreg_base + *off, data, count);
 549        else
 550                ret = ipath_write_umem64(dd, kreg_base + *off, data, count);
 551
 552        if (ret >= 0) {
 553                *off += count;
 554                ret = count;
 555                if (ipath_diag_inuse == -1)
 556                        ipath_diag_inuse = 1; /* all read/write OK now */
 557        }
 558
 559        return ret;
 560}
 561
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.