Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 57a62fed authored by Markus Lidel's avatar Markus Lidel Committed by Linus Torvalds
Browse files

[PATCH] I2O: Bugfixes to get I2O working again

From: Markus Lidel <Markus.Lidel@shadowconnect.com>

- Fixed locking of struct i2o_exec_wait in Executive-OSM

- Removed LCT Notify in i2o_exec_probe() which caused freeing memory and
  accessing freed memory during first enumeration of I2O devices

- Added missing locking in i2o_exec_lct_notify()

- removed put_device() of I2O controller in i2o_iop_remove() which caused
  the controller structure get freed to early

- Fixed size of mempool in i2o_iop_alloc()

- Fixed access to freed memory in i2o_msg_get()

See http://bugzilla.kernel.org/show_bug.cgi?id=6561



Signed-off-by: default avatarMarkus Lidel <Markus.Lidel@shadowconnect.com>
Cc: <stable@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent a913f507
Loading
Loading
Loading
Loading
+37 −35
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ struct i2o_exec_wait {
	u32 m;			/* message id */
	struct i2o_message *msg;	/* pointer to the reply message */
	struct list_head list;	/* node in global wait list */
	spinlock_t lock;	/* lock before modifying */
};

/* Work struct needed to handle LCT NOTIFY replies */
@@ -87,6 +88,7 @@ static struct i2o_exec_wait *i2o_exec_wait_alloc(void)
		return NULL;

	INIT_LIST_HEAD(&wait->list);
	spin_lock_init(&wait->lock);

	return wait;
};
@@ -125,6 +127,7 @@ int i2o_msg_post_wait_mem(struct i2o_controller *c, struct i2o_message *msg,
	DECLARE_WAIT_QUEUE_HEAD(wq);
	struct i2o_exec_wait *wait;
	static u32 tcntxt = 0x80000000;
	long flags;
	int rc = 0;

	wait = i2o_exec_wait_alloc();
@@ -146,33 +149,28 @@ int i2o_msg_post_wait_mem(struct i2o_controller *c, struct i2o_message *msg,
	wait->tcntxt = tcntxt++;
	msg->u.s.tcntxt = cpu_to_le32(wait->tcntxt);

	wait->wq = &wq;
	/*
	 * we add elements to the head, because if a entry in the list will
	 * never be removed, we have to iterate over it every time
	 */
	list_add(&wait->list, &i2o_exec_wait_list);

	/*
	 * Post the message to the controller. At some point later it will
	 * return. If we time out before it returns then complete will be zero.
	 */
	i2o_msg_post(c, msg);

	if (!wait->complete) {
		wait->wq = &wq;
		/*
		 * we add elements add the head, because if a entry in the list
		 * will never be removed, we have to iterate over it every time
		 */
		list_add(&wait->list, &i2o_exec_wait_list);
	wait_event_interruptible_timeout(wq, wait->complete, timeout * HZ);

		wait_event_interruptible_timeout(wq, wait->complete,
						 timeout * HZ);
	spin_lock_irqsave(&wait->lock, flags);

	wait->wq = NULL;
	}

	barrier();

	if (wait->complete) {
	if (wait->complete)
		rc = le32_to_cpu(wait->msg->body[0]) >> 24;
		i2o_flush_reply(c, wait->m);
		i2o_exec_wait_free(wait);
	} else {
	else {
		/*
		 * We cannot remove it now. This is important. When it does
		 * terminate (which it must do if the controller has not
@@ -186,6 +184,13 @@ int i2o_msg_post_wait_mem(struct i2o_controller *c, struct i2o_message *msg,
		rc = -ETIMEDOUT;
	}

	spin_unlock_irqrestore(&wait->lock, flags);

	if (rc != -ETIMEDOUT) {
		i2o_flush_reply(c, wait->m);
		i2o_exec_wait_free(wait);
	}

	return rc;
};

@@ -213,7 +218,6 @@ static int i2o_msg_post_wait_complete(struct i2o_controller *c, u32 m,
{
	struct i2o_exec_wait *wait, *tmp;
	unsigned long flags;
	static spinlock_t lock = SPIN_LOCK_UNLOCKED;
	int rc = 1;

	/*
@@ -223,23 +227,24 @@ static int i2o_msg_post_wait_complete(struct i2o_controller *c, u32 m,
	 * already expired. Not much we can do about that except log it for
	 * debug purposes, increase timeout, and recompile.
	 */
	spin_lock_irqsave(&lock, flags);
	list_for_each_entry_safe(wait, tmp, &i2o_exec_wait_list, list) {
		if (wait->tcntxt == context) {
			list_del(&wait->list);
			spin_lock_irqsave(&wait->lock, flags);

			spin_unlock_irqrestore(&lock, flags);
			list_del(&wait->list);

			wait->m = m;
			wait->msg = msg;
			wait->complete = 1;

			barrier();

			if (wait->wq) {
				wake_up_interruptible(wait->wq);
			if (wait->wq)
				rc = 0;
			} else {
			else
				rc = -1;

			spin_unlock_irqrestore(&wait->lock, flags);

			if (rc) {
				struct device *dev;

				dev = &c->pdev->dev;
@@ -248,15 +253,13 @@ static int i2o_msg_post_wait_complete(struct i2o_controller *c, u32 m,
					 c->name);
				i2o_dma_free(dev, &wait->dma);
				i2o_exec_wait_free(wait);
				rc = -1;
			}
			} else
				wake_up_interruptible(wait->wq);

			return rc;
		}
	}

	spin_unlock_irqrestore(&lock, flags);

	osm_warn("%s: Bogus reply in POST WAIT (tr-context: %08x)!\n", c->name,
		 context);

@@ -322,14 +325,9 @@ static DEVICE_ATTR(product_id, S_IRUGO, i2o_exec_show_product_id, NULL);
static int i2o_exec_probe(struct device *dev)
{
	struct i2o_device *i2o_dev = to_i2o_device(dev);
	struct i2o_controller *c = i2o_dev->iop;

	i2o_event_register(i2o_dev, &i2o_exec_driver, 0, 0xffffffff);

	c->exec = i2o_dev;

	i2o_exec_lct_notify(c, c->lct->change_ind + 1);

	device_create_file(dev, &dev_attr_vendor_id);
	device_create_file(dev, &dev_attr_product_id);

@@ -523,6 +521,8 @@ static int i2o_exec_lct_notify(struct i2o_controller *c, u32 change_ind)
	struct device *dev;
	struct i2o_message *msg;

	down(&c->lct_lock);

	dev = &c->pdev->dev;

	if (i2o_dma_realloc
@@ -545,6 +545,8 @@ static int i2o_exec_lct_notify(struct i2o_controller *c, u32 change_ind)

	i2o_msg_post(c, msg);

	up(&c->lct_lock);

	return 0;
};

+1 −3
Original line number Diff line number Diff line
@@ -804,8 +804,6 @@ void i2o_iop_remove(struct i2o_controller *c)

	/* Ask the IOP to switch to RESET state */
	i2o_iop_reset(c);

	put_device(&c->device);
}

/**
@@ -1059,7 +1057,7 @@ struct i2o_controller *i2o_iop_alloc(void)

	snprintf(poolname, sizeof(poolname), "i2o_%s_msg_inpool", c->name);
	if (i2o_pool_alloc
	    (&c->in_msg, poolname, I2O_INBOUND_MSG_FRAME_SIZE * 4,
	    (&c->in_msg, poolname, I2O_INBOUND_MSG_FRAME_SIZE * 4 + sizeof(u32),
	     I2O_MSG_INPOOL_MIN)) {
		kfree(c);
		return ERR_PTR(-ENOMEM);
+4 −1
Original line number Diff line number Diff line
@@ -1114,8 +1114,11 @@ static inline struct i2o_message *i2o_msg_get(struct i2o_controller *c)

	mmsg->mfa = readl(c->in_port);
	if (unlikely(mmsg->mfa >= c->in_queue.len)) {
		u32 mfa = mmsg->mfa;

		mempool_free(mmsg, c->in_msg.mempool);
		if(mmsg->mfa == I2O_QUEUE_EMPTY)

		if (mfa == I2O_QUEUE_EMPTY)
			return ERR_PTR(-EBUSY);
		return ERR_PTR(-EFAULT);
	}