netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Michael Chan" <mchan@broadcom.com>
To: paulmck@linux.vnet.ibm.com
Cc: "David Miller" <davem@davemloft.net>,
	michaelc@cs.wisc.edu, anilgv@broadcom.com,
	netdev <netdev@vger.kernel.org>,
	linux-scsi@vger.kernel.org, open-iscsi@googlegroups.com
Subject: Re: [PATCH 2/3] cnic: Add CNIC driver.
Date: Thu, 22 May 2008 12:46:11 -0700	[thread overview]
Message-ID: <1211485571.18326.41.camel@dell> (raw)
In-Reply-To: <20080522074422.GB11933@linux.vnet.ibm.com>

On Thu, 2008-05-22 at 00:44 -0700, Paul E. McKenney wrote:

> > +
> > +int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops)
> > +{
> > +	struct cnic_dev *dev;
> > +
> > +	if (ulp_type >= MAX_CNIC_ULP_TYPE) {
> > +		printk(KERN_ERR PFX "cnic_register_driver: Bad type %d\n",
> > +		       ulp_type);
> > +		return -EINVAL;
> > +	}
> > +	mutex_lock(&cnic_lock);
> > +	if (cnic_ulp_tbl[ulp_type]) {
> > +		printk(KERN_ERR PFX "cnic_register_driver: Type %d has already "
> > +				    "been registered\n", ulp_type);
> > +		mutex_unlock(&cnic_lock);
> > +		return -EBUSY;
> > +	}
> > +
> > +	read_lock(&cnic_dev_lock);
> > +	list_for_each_entry(dev, &cnic_dev_list, list) {
> > +		struct cnic_local *cp = dev->cnic_priv;
> > +
> > +		clear_bit(ULP_F_INIT, &cp->ulp_flags[ulp_type]);
> > +	}
> > +	read_unlock(&cnic_dev_lock);
> > +
> > +	rcu_assign_pointer(cnic_ulp_tbl[ulp_type], ulp_ops);
> 
> OK, protected by cnic_lock.
> 
> > +	mutex_unlock(&cnic_lock);
> > +
> > +	/* Prevent race conditions with netdev_event */
> > +	rtnl_lock();
> > +	read_lock(&cnic_dev_lock);
> > +	list_for_each_entry(dev, &cnic_dev_list, list) {
> > +		struct cnic_local *cp = dev->cnic_priv;
> > +
> > +		if (!test_and_set_bit(ULP_F_INIT, &cp->ulp_flags[ulp_type]))
> > +			ulp_ops->cnic_init(dev);
> > +	}
> > +	read_unlock(&cnic_dev_lock);
> > +	rtnl_unlock();
> > +
> > +	return 0;
> > +}
> > +
> > +int cnic_unregister_driver(int ulp_type)
> > +{
> > +	struct cnic_dev *dev;
> > +
> > +	if (ulp_type >= MAX_CNIC_ULP_TYPE) {
> > +		printk(KERN_ERR PFX "cnic_unregister_driver: Bad type %d\n",
> > +		       ulp_type);
> > +		return -EINVAL;
> > +	}
> > +	mutex_lock(&cnic_lock);
> > +	if (!cnic_ulp_tbl[ulp_type]) {
> > +		printk(KERN_ERR PFX "cnic_unregister_driver: Type %d has not "
> > +				    "been registered\n", ulp_type);
> > +		goto out_unlock;
> > +	}
> > +	read_lock(&cnic_dev_lock);
> > +	list_for_each_entry(dev, &cnic_dev_list, list) {
> > +		struct cnic_local *cp = dev->cnic_priv;
> > +		
> > +		if (rcu_dereference(cp->ulp_ops[ulp_type])) {
> 
> The rcu_dereference() is redundant because we hold cnic_lock.
> (Which is OK, just wanting to make sure I understand the code.)

Yes, I wanted to access these RCU protected pointers in a uniform way.

> 
> > +			printk(KERN_ERR PFX "cnic_unregister_driver: Type %d "
> > +			       "still has devices registered\n", ulp_type);
> > +			read_unlock(&cnic_dev_lock);
> > +			goto out_unlock;
> > +		}
> > +	}
> > +	read_unlock(&cnic_dev_lock);
> > +
> > +	rcu_assign_pointer(cnic_ulp_tbl[ulp_type], NULL);
> 
> OK, protected by cnic_lock.
> 
> > +
> > +	mutex_unlock(&cnic_lock);
> > +	synchronize_rcu();
> 
> The caller is responsible for freeing up cnic_ulp_tbl[ulp_type]?  If so,
> the caller had better have kept a pointer to it...
> 
> But the caller would need to snapshot the pointer before the cnic_lock
> was acquired, which means that some other pointer might in fact be
> in place by the time this function returns.
> 
> So, is this data element statically allocated?  Or is there some other
> trick being used?
> 
> Or is the whole point of this code simply to ensure that any calls to
> the old cnic_ulp_tbl[ulp_type] functions have completed before this
> function returns?  If so, please add a comment to this effect.

Yes, once again to ensure that any calls have completed before
continuing.  I will document the use of RCU more in the next version.

> 
> > +	return 0;
> > +
> > +out_unlock:
> > +	mutex_unlock(&cnic_lock);
> > +	return -EINVAL;
> > +}
> > +
> > +EXPORT_SYMBOL(cnic_register_driver);
> > +EXPORT_SYMBOL(cnic_unregister_driver);
> > +
> > +static int cnic_start_hw(struct cnic_dev *);
> > +static void cnic_stop_hw(struct cnic_dev *);
> > +
> > +static int cnic_register_device(struct cnic_dev *dev, int ulp_type,
> > +				void *ulp_ctx)
> > +{
> > +	struct cnic_local *cp = dev->cnic_priv;
> > +	struct cnic_ulp_ops *ulp_ops;
> > +
> > +	if (ulp_type >= MAX_CNIC_ULP_TYPE) {
> > +		printk(KERN_ERR PFX "cnic_register_device: Bad type %d\n",
> > +		       ulp_type);
> > +		return -EINVAL;
> > +	}
> > +	mutex_lock(&cnic_lock);
> > +	if (cnic_ulp_tbl[ulp_type] == NULL) {
> > +		printk(KERN_ERR PFX "cnic_register_device: Driver with type %d "
> > +				    "has not been registered\n", ulp_type);
> > +		mutex_unlock(&cnic_lock);
> > +		return -EAGAIN;
> > +	}
> > +	if (rcu_dereference(cp->ulp_ops[ulp_type])) {
> 
> Again, the rcu_dereference() is redundant due to the cnic_lock being
> held, and again, this is OK, just checking to make sure I understand it.
> 
> > +		printk(KERN_ERR PFX "cnic_register_device: Type %d has already "
> > +		       "been registered to this device\n", ulp_type);
> > +		mutex_unlock(&cnic_lock);
> > +		return -EBUSY;
> > +	}
> > +	if (!try_module_get(cnic_ulp_tbl[ulp_type]->owner)) {
> > +		mutex_unlock(&cnic_lock);
> > +		return -EBUSY;
> > +	}
> > +
> > +	clear_bit(ULP_F_START, &cp->ulp_flags[ulp_type]);
> > +	cp->ulp_handle[ulp_type] = ulp_ctx;
> > +	ulp_ops = cnic_ulp_tbl[ulp_type];
> > +	rcu_assign_pointer(cp->ulp_ops[ulp_type], ulp_ops);
> 
> Good, protected by cnic_lock.
> 
> > +	cnic_hold(dev);
> > +	if (!dev->use_count) {
> > +		if (!test_bit(CNIC_F_IF_GOING_DOWN, &dev->flags)) {
> > +			if (dev->netdev->flags & IFF_UP)
> > +				set_bit(CNIC_F_IF_UP, &dev->flags);
> > +		}
> > +	}
> > +	dev->use_count++;
> > +
> > +	if (dev->use_count == 1) {
> > +		if (test_bit(CNIC_F_IF_UP, &dev->flags))
> > +			cnic_start_hw(dev);
> > +	}
> > +
> > +	if (test_bit(CNIC_F_CNIC_UP, &dev->flags))
> > +		if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[ulp_type]))
> > +			ulp_ops->cnic_start(cp->ulp_handle[ulp_type]);
> > +
> > +	mutex_unlock(&cnic_lock);
> > +
> > +	return 0;
> > +
> > +}
> > +
> > +static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
> > +{
> > +	struct cnic_local *cp = dev->cnic_priv;
> > +
> > +	if (ulp_type >= MAX_CNIC_ULP_TYPE) {
> > +		printk(KERN_ERR PFX "cnic_unregister_device: Bad type %d\n",
> > +		       ulp_type);
> > +		return -EINVAL;
> > +	}
> > +	mutex_lock(&cnic_lock);
> > +	if (rcu_dereference(cp->ulp_ops[ulp_type])) {
> 
> Ditto...
> 
> > +		dev->use_count--;
> > +		module_put(cp->ulp_ops[ulp_type]->owner);
> > +		rcu_assign_pointer(cp->ulp_ops[ulp_type], NULL);
> 
> OK, cnic_lock held...
> 
> > +		if (dev->use_count == 0)
> > +			cnic_stop_hw(dev);
> > +		cnic_put(dev);
> > +	} else {
> > +		printk(KERN_ERR PFX "cnic_unregister_device: device not "
> > +		       "registered to this ulp type %d\n", ulp_type);
> > +		mutex_unlock(&cnic_lock);
> > +		return -EINVAL;
> > +	}
> > +	mutex_unlock(&cnic_lock);
> > +
> > +	synchronize_rcu();
> 
> Caller is again responsible for freeing up cp->ulp_ops[ulp_type]?
> If so, the caller had better have obtained a reference to it beforehand.
> But it might have changed in the meantime.  So, how is this freed?
> 
> Or is this statically allocated with the only purpose of the
> synchronize_rcu() being to ensure that calls though the old ops vector
> have completed before this function returns?  If so, please add a
> comment to this effect.

Yes same as above.

> > +
> > +static int cnic_cm_open(struct cnic_dev *dev)
> > +{
> > +	struct cnic_local *cp = dev->cnic_priv;
> > +	int err;
> > +
> > +	err = cnic_cm_alloc_mem(dev);
> > +	if (err)
> > +		return err;
> > +
> > +	err = cp->start_cm(dev);
> > +
> > +	if (err)
> > +		goto err_out;
> > +
> > +	spin_lock_init(&cp->wr_lock);
> > +
> > +	tasklet_init(&cp->cnic_task, &cnic_task, (unsigned long) cp);
> > +
> > +	cp->cm_nb.notifier_call = cnic_net_callback;
> > +	register_netevent_notifier(&cp->cm_nb);
> > +
> > +	dev->cm_create = cnic_cm_create;
> > +	dev->cm_destroy = cnic_cm_destroy;
> > +	dev->cm_connect = cnic_cm_connect;
> > +	dev->cm_abort = cnic_cm_abort;
> > +	dev->cm_close = cnic_cm_close;
> > +	dev->cm_select_dev = cnic_cm_select_dev;
> > +
> > +	cp->ulp_handle[CNIC_ULP_L4] = dev;
> > +	rcu_assign_pointer(cp->ulp_ops[CNIC_ULP_L4], &cm_ulp_ops);
> 
> The cnic_lock is not held due to this being initialization time, and
> that no one else can be messing with this until initialization is
> complete?

Yes, this is actually the CNIC driver registering with itself.  Only the
CNIC driver will be using the CNIC_ULP_L4 ID.

> 
> > +	return 0;
> > +
> > +err_out:
> > +	cnic_cm_free_mem(dev);
> > +	return err;
> > +}
> > +

> > +static void cnic_stop_bnx2_hw(struct cnic_dev *dev)
> > +{
> > +	struct cnic_local *cp = dev->cnic_priv;
> > +	struct cnic_eth_dev *ethdev = cp->ethdev;
> > +
> > +	cnic_disable_bnx2_int_sync(dev);
> > +
> > +	cnic_bnx2_reg_wr_ind(dev, BNX2_CP_SCRATCH + 0x20, 0);
> > +	cnic_bnx2_reg_wr_ind(dev, BNX2_COM_SCRATCH + 0x20, 0);
> > +
> > +	cnic_init_context(dev, KWQ_CID);
> > +	cnic_init_context(dev, KCQ_CID);
> > +
> > +	cnic_setup_5709_context(dev, 0);
> > +	cnic_free_irq(dev);
> > +
> > +	ethdev->drv_unregister_cnic(dev->netdev);
> > +
> > +	cnic_free_resc(dev);
> > +}
> > +
> > +static void cnic_stop_hw(struct cnic_dev *dev)
> > +{
> > +	if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) {
> > +		struct cnic_local *cp = dev->cnic_priv;
> > +
> > +		clear_bit(CNIC_F_CNIC_UP, &dev->flags);
> > +		rcu_assign_pointer(cp->ulp_ops[CNIC_ULP_L4], NULL);
> 
> Given that the cnic_lock does not appear to be held, what prevents
> other CPUs from manipulating cp->ulp_ops[CNIC_ULP_L4] concurrently
> with this function?
> 

This is again the CNIC driver unregistering with itself.  Only the CNIC
driver will be using the CNIC_ULP_L4 ID.

> > +		synchronize_rcu();
> > +		cnic_cm_shutdown(dev);
> > +		cp->stop_hw(dev);
> > +		pci_dev_put(dev->pcidev);
> > +	}
> > +}
> > +




  reply	other threads:[~2008-05-22 19:46 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-05-22  1:06 [PATCH 0/3] bnx2: Add iSCSI support Michael Chan
2008-05-22  1:06 ` [PATCH 2/3] cnic: Add CNIC driver Michael Chan
2008-05-22  7:44   ` Paul E. McKenney
2008-05-22 19:46     ` Michael Chan [this message]
2008-05-23  3:47       ` Paul E. McKenney
2008-05-23 20:09   ` Roland Dreier
2008-05-23 20:14   ` Roland Dreier
2008-05-24  0:42     ` Michael Chan
     [not found] ` <1211418386-18203-1-git-send-email-mchan-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
2008-05-22  1:06   ` [PATCH 1/3] bnx2: Add support for " Michael Chan
2008-05-22  6:45     ` Paul E. McKenney
     [not found]       ` <20080522064541.GA11933-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
2008-05-22 19:23         ` Michael Chan
2008-05-23  3:45           ` Paul E. McKenney
     [not found]             ` <20080523034522.GA8612-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
2008-05-23  4:52               ` Michael Chan
2008-05-23  5:32                 ` Paul E. McKenney
     [not found]     ` <1211418386-18203-2-git-send-email-mchan-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
2008-05-22 15:18       ` Konrad Rzeszutek
2008-05-22  1:06   ` [PATCH 3/3] bnx2i: Add bnx2i iSCSI driver Michael Chan
     [not found]     ` <1211418386-18203-4-git-send-email-mchan-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
2008-05-22 15:15       ` Konrad Rzeszutek
2008-05-22 15:52         ` Ben Hutchings
2008-05-22 19:06         ` Anil Veerabhadrappa
2008-05-22 21:15     ` Christoph Hellwig
2008-05-22 22:59       ` Michael Chan
2008-05-23 20:23     ` Roland Dreier
2008-05-23 21:42       ` Anil Veerabhadrappa
2008-05-27 14:38         ` Roland Dreier
2008-05-27 19:17           ` Michael Chan
2008-05-27 18:21             ` Roland Dreier
2008-05-27 19:52           ` David Miller
2008-05-28  0:48             ` Michael Chan
2008-05-28  3:39               ` Jeff Garzik
2008-05-28  8:47                 ` Hannes Reinecke

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1211485571.18326.41.camel@dell \
    --to=mchan@broadcom.com \
    --cc=anilgv@broadcom.com \
    --cc=davem@davemloft.net \
    --cc=linux-scsi@vger.kernel.org \
    --cc=michaelc@cs.wisc.edu \
    --cc=netdev@vger.kernel.org \
    --cc=open-iscsi@googlegroups.com \
    --cc=paulmck@linux.vnet.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).