/* * FIFO queues, shared with hardware. * * If a queue has empty slots, an entry is added to the queue tail, * and that entry is marked as occupied. * Entries can be dequeued from the head of the list, when the device * has marked the entry as consumed. * * Returns true on successful queue/dequeue, false on failure.
*/ staticint fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry)
{ struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); unsignedlong flags; int ret = 0;
staticint fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry)
{ struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); unsignedlong flags; int ret = 0;
u64 c;
spin_lock_irqsave(&hw->fifo_lock, flags);
c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; if (c & ENTRY_MASK_C) { if (entry)
*entry = c & ENTRY_MASK_NOSTATE;
staticinlineint ctrl_set(int l2sz, int idxmask, int desclim)
{ int active = 0, go = 1; return l2sz << CTRL_BITPOS_L2SZ |
idxmask << CTRL_BITPOS_FIFOINDEXMASK |
desclim << CTRL_BITPOS_DESCLIMIT |
active << CTRL_BITPOS_A |
go << CTRL_BITPOS_G;
}
staticvoid ctrl_setup(struct ccb *ccb, int nr_desc, int l2desc_sz)
{ /* for simplicity, use the same parameters for send and recv ctrls */
ccb->send_ctrl = ctrl_set(l2desc_sz, nr_desc-1, nr_desc-1);
ccb->recv_ctrl = ctrl_set(l2desc_sz, nr_desc-1, nr_desc-1);
}
staticinlineint fifo_sz(int nr_entry)
{ /* size of a fifo is determined by the number of entries it contains */ return nr_entry * sizeof(u64) + FIFOHANDLESIZE;
}
staticvoid fifo_setup(void *base_addr, int nr_entry)
{ struct fifo *fifo_q = base_addr; int i;
/* set up an empty fifo */
fifo_q->head = 0;
fifo_q->tail = 0;
fifo_q->reset = 0;
fifo_q->nrents = nr_entry;
fifo_q->imask = nr_entry - 1;
fifo_q->merge = ENTRY_MASK_O;
for (i = 0; i < nr_entry; i++)
fifo_q->fifobar[i] = 0;
}
/* complicated dance to tell the hw we are stopping */
doorbell_clr(driver_ccb);
iowrite32(ioread32(&device_ccb->send_ctrl) & ~(1 << CTRL_BITPOS_G),
&device_ccb->send_ctrl);
iowrite32(ioread32(&device_ccb->recv_ctrl) & ~(1 << CTRL_BITPOS_G),
&device_ccb->recv_ctrl);
/* give iLO some time to process stop request */ for (retries = MAX_WAIT; retries > 0; retries--) {
doorbell_set(driver_ccb);
udelay(WAIT_TIME); if (!(ioread32(&device_ccb->send_ctrl) & (1 << CTRL_BITPOS_A))
&&
!(ioread32(&device_ccb->recv_ctrl) & (1 << CTRL_BITPOS_A))) break;
} if (retries == 0)
dev_err(&pdev->dev, "Closing, but controller still active\n");
/* clear the hw ccb */
memset_io(device_ccb, 0, sizeof(struct ccb));
/* free resources used to back send/recv queues */
dma_free_coherent(&pdev->dev, data->dma_size, data->dma_va,
data->dma_pa);
}
/* * Create two ccb's, one with virt addrs, one with phys addrs. * Copy the phys addr ccb to device shared mem.
*/
ctrl_setup(driver_ccb, NR_QENTRY, L2_QENTRY_SZ);
ctrl_setup(ilo_ccb, NR_QENTRY, L2_QENTRY_SZ);
/* make sure iLO is really handling requests */ for (i = MAX_WAIT; i > 0; i--) { if (ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, NULL, NULL)) break;
udelay(WAIT_TIME);
}
if (i == 0) {
dev_err(&hw->ilo_dev->dev, "Open could not dequeue a packet\n"); return -EBUSY;
}
staticinlineint is_channel_reset(struct ccb *ccb)
{ /* check for this particular channel needing a reset */ return FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset;
}
staticinlinevoid set_channel_reset(struct ccb *ccb)
{ /* set a flag indicating this channel needs a reset */
FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset = 1;
}
staticvoid ilo_set_reset(struct ilo_hwinfo *hw)
{ int slot;
/* * Mapped memory is zeroed on ilo reset, so set a per ccb flag * to indicate that this ccb needs to be closed and reopened.
*/ for (slot = 0; slot < max_ccb; slot++) { if (!hw->ccb_alloc[slot]) continue;
set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb);
}
}
if (is_channel_reset(driver_ccb)) { /* * If the device has been reset, applications * need to close and reopen all ccbs.
*/ return -ENODEV;
}
/* * This function is to be called when data is expected * in the channel, and will return an error if no packet is found * during the loop below. The sleep/retry logic is to allow * applications to call read() immediately post write(), * and give iLO some time to process the sent packet.
*/
cnt = 20; do { /* look for a received packet */
found = ilo_pkt_dequeue(hw, driver_ccb, RECVQ, &pkt_id,
&pkt_len, &pkt); if (found) break;
cnt--;
msleep(100);
} while (!found && cnt);
if (!found) return -EAGAIN;
/* only copy the length of the received packet */ if (pkt_len < len)
len = pkt_len;
err = copy_to_user(buf, pkt, len);
/* return the received packet to the queue */
ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, desc_mem_sz(1));
/* new ccb allocation */
data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM;
spin_lock(&hw->open_lock);
/* each fd private_data holds sw/hw view of ccb */ if (hw->ccb_alloc[slot] == NULL) { /* create a channel control block for this minor */
error = ilo_ccb_setup(hw, data, slot); if (error) {
kfree(data); goto out;
}
} else {
kfree(data); if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) { /* * The channel exists, and either this open * or a previous open of this channel wants * exclusive access.
*/
error = -EBUSY;
} else {
hw->ccb_alloc[slot]->ccb_cnt++;
error = 0;
}
}
out:
spin_unlock(&hw->open_lock);
if (!error)
fp->private_data = hw->ccb_alloc[slot];
staticvoid ilo_remove(struct pci_dev *pdev)
{ int i, minor; struct ilo_hwinfo *ilo_hw = pci_get_drvdata(pdev);
if (!ilo_hw) return;
clear_device(ilo_hw);
minor = MINOR(ilo_hw->cdev.dev); for (i = minor; i < minor + max_ccb; i++)
device_destroy(&ilo_class, MKDEV(ilo_major, i));
cdev_del(&ilo_hw->cdev);
ilo_disable_interrupts(ilo_hw);
free_irq(pdev->irq, ilo_hw);
ilo_unmap_device(pdev, ilo_hw);
pci_release_regions(pdev); /* * pci_disable_device(pdev) used to be here. But this PCI device has * two functions with interrupt lines connected to a single pin. The * other one is a USB host controller. So when we disable the PIN here * e.g. by rmmod hpilo, the controller stops working. It is because * the interrupt link is disabled in ACPI since it is not refcounted * yet. See acpi_pci_link_free_irq called from acpi_pci_irq_disable.
*/
kfree(ilo_hw);
ilo_hwdev[(minor / max_ccb)] = 0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.