Commit c6cc89b0 authored by Mikhail Karpenko's avatar Mikhail Karpenko
Browse files

Split long PRDT enries

Each PRDT entry is limited to 4Mb and this was fixed. Tests look fine.
parent 33ec5861
Loading
Loading
Loading
Loading
+50 −22
Original line number Diff line number Diff line
@@ -71,6 +71,8 @@ static void finish_cmd(struct device *dev, struct elphel_ahci_priv *dpriv);
static int process_cmd(struct device *dev, struct elphel_ahci_priv *dpriv, struct ata_port *port);
//static void start_cmd(struct device *dev, struct elphel_ahci_priv *dpriv, struct ata_port *port);
static inline size_t get_size_from(const struct fvec *vects, int index, size_t offset, int all);
static inline void vectmov(struct fvec *vec, size_t len);
static inline void vectsplit(struct fvec *vect, struct fvec *parts, size_t *n_elem);

static ssize_t set_load_flag(struct device *dev, struct device_attribute *attr,
		const char *buff, size_t buff_sz)
@@ -141,8 +143,8 @@ static irqreturn_t elphel_irq_handler(int irq, void * dev_instance)
		writel(host_irq_stat, hpriv->mmio + HOST_IRQ_STAT);
		handled = IRQ_HANDLED;

//		if (process_cmd(host->dev, dpriv, host->ports[0]) == 0)
//			finish_cmd(host->dev, dpriv);
		if (process_cmd(host->dev, dpriv, host->ports[0]) == 0)
			finish_cmd(host->dev, dpriv);
	} else {
		/* pass handling to AHCI level */
		handled = ahci_single_irq_intr(irq, dev_instance);
@@ -256,7 +258,6 @@ static int elphel_drv_probe(struct platform_device *pdev)
	ret = init_buffers(dev, &dpriv->fbuffs);
	if (ret != 0)
		return ret;
//	sg_init_table(dpriv->sgl, MAX_DATA_CHUNKS);
	init_vectors(dpriv);

	match = of_match_device(ahci_elphel_of_match, &pdev->dev);
@@ -640,7 +641,7 @@ static int map_vectors(struct elphel_ahci_priv *dpriv)
			vect = chunks[i];
		}
		if (total_sz > dpriv->max_data_sz) {
			// truncate current buffer and finish mapping
			/* truncate current buffer and finish mapping */
			tail = total_sz - dpriv->max_data_sz;
			vect.iov_len -= tail;
			dpriv->curr_data_chunk = i;
@@ -652,7 +653,20 @@ static int map_vectors(struct elphel_ahci_priv *dpriv)
			finish = 1;
		}
		if (vect.iov_len != 0) {
			if (vect.iov_len < MAX_PRDT_LEN) {
				dpriv->sgl[index++] = vect;
			} else {
				/* current vector is too long and can not be mapped to a single PRDT entry, split it */
				vectsplit(&vect, dpriv->sgl, &index);
				if (vect.iov_len < MAX_PRDT_LEN) {
					dpriv->sgl[index++] = vect;
				} else {
					/* free slots in PRDT table have ended */
					dpriv->curr_data_chunk = i;
					dpriv->curr_data_offset = (unsigned char *)vect.iov_base - (unsigned char *)chunks[i].iov_base;
					finish = 1;
				}
			}
		}
		if (finish)
			break;
@@ -666,6 +680,21 @@ static int map_vectors(struct elphel_ahci_priv *dpriv)
	return index;
}

static inline void vectsplit(struct fvec *vect, struct fvec *parts, size_t *n_elem)
{
	size_t len;
	struct fvec split;

	while (vect->iov_len > MAX_PRDT_LEN && *n_elem < MAX_SGL_LEN) {
		len = MAX_PRDT_LEN - MAX_PRDT_LEN % PHY_BLOCK_SIZE;
		split.iov_base = vect->iov_base;
		split.iov_dma = vect->iov_dma;
		split.iov_len = len;
		vectmov(vect, len);
		parts[*n_elem] = split;
		*n_elem = *n_elem + 1;
	}
}
static inline void vectcpy(struct fvec *dest, void *src, size_t len)
{
	unsigned char *d = (unsigned char *)dest->iov_base;
@@ -1236,7 +1265,6 @@ static ssize_t rawdev_write(struct device *dev, ///<

	if ((dpriv->flags & PROC_CMD) || dont_process) {
		// we are not ready yet
		printk(KERN_DEBUG ">>> not ready: flags = %u\n", dpriv->flags);
		return -EAGAIN;
	}

@@ -1315,24 +1343,24 @@ static ssize_t rawdev_write(struct device *dev, ///<
	for (i = 0; i < MAX_DATA_CHUNKS; i++) {
		printk(KERN_DEBUG ">>>\tslot: %i; len: %u\n", i, dpriv->data_chunks[i].iov_len);
	}
	if (check_chunks(dpriv->data_chunks) != 0) {
		dont_process = 1;
		return -EINVAL;
	}
//	if (check_chunks(dpriv->data_chunks) != 0) {
//		dont_process = 1;
//		return -EINVAL;
//	}

	process_cmd(dev, dpriv, port);
	while (dpriv->flags & PROC_CMD) {
#ifndef DEBUG_DONT_WRITE
		while (dpriv->flags & IRQ_SIMPLE) {
			printk_once(KERN_DEBUG ">>> waiting for interrupt\n");
			msleep_interruptible(1);
		}
#endif
		printk(KERN_DEBUG ">>> proceeding to next cmd chunk\n");
		sg_elems = process_cmd(dev, dpriv, port);
		if (sg_elems == 0)
			finish_cmd(dev, dpriv);
	}
//	while (dpriv->flags & PROC_CMD) {
//#ifndef DEBUG_DONT_WRITE
//		while (dpriv->flags & IRQ_SIMPLE) {
//			printk_once(KERN_DEBUG ">>> waiting for interrupt\n");
//			msleep_interruptible(1);
//		}
//#endif
//		printk(KERN_DEBUG ">>> proceeding to next cmd chunk\n");
//		sg_elems = process_cmd(dev, dpriv, port);
//		if (sg_elems == 0)
//			finish_cmd(dev, dpriv);
//	}
//	if (chunks[CHUNK_DATA_0].iov_len != 0)
//		dma_unmap_single(dev, chunks[CHUNK_DATA_0].iov_dma, chunks[CHUNK_DATA_0].iov_len, DMA_TO_DEVICE);
//	if (chunks[CHUNK_DATA_1].iov_len != 0)
+6 −1
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@
#define CMD_FIS_LEN               5
/** This is used to get 28-bit address from 64-bit value */
#define ADDR_MASK_28_BIT          ((u64)0xfffffff)
/** A maximum of length of 4MB may exist for PRDT entry */
#define MAX_PRDT_LEN              0x3fffff
/** An array or JPEG frame chunks contains pointers to JPEG leading marker,
 * JPEG header, Exif data if present, stuffing bytes chunk which aligns
 * the frame size to disk sector boundary, JPEG data which
@@ -46,6 +48,9 @@
#define DEFAULT_PORT_NUM          0
/** Align buffers length to this amount of bytes */
#define ALIGNMENT_SIZE            32
/** Maximum number of entries in PRDT table. HW max is 64k.
 * Set this value the same as AHCI_MAX_SG in ahci.h */
#define MAX_SGL_LEN               168

/** This structure holds raw device buffer pointers */
struct drv_pointers {
@@ -102,7 +107,7 @@ struct elphel_ahci_priv {
	struct drv_pointers lba_ptr;
	struct frame_buffers fbuffs;
	struct fvec data_chunks[MAX_DATA_CHUNKS];
	struct fvec sgl[MAX_DATA_CHUNKS];
	struct fvec sgl[MAX_SGL_LEN];
	int sg_elems;
	int curr_data_chunk;                         ///< index of a data chunk used during last transaction
	size_t curr_data_offset;                     ///< offset of the last byte in a data chunk pointed to by @e curr_data_chunk