From: Andi Kleen <andi@firstfloor.org>
To: Robert Love <robert.w.love@intel.com>
Cc: james.bottomley@hansenpartnership.com,
linux-scsi@vger.kernel.org, jgarzik@redhat.com,
james.smart@emulex.com, davem@davemloft.net,
michaelc@cs.wisc.edu, jeffrey.t.kirsher@intel.com,
jeykholt@cisco.com
Subject: Re: [PATCH 2/3] libfc: A modular Fibre Channel library
Date: Mon, 24 Nov 2008 11:13:29 +0100 [thread overview]
Message-ID: <87tz9xh1o6.fsf@basil.nowhere.org> (raw)
In-Reply-To: <20081118222111.2289.35843.stgit@fritz> (Robert Love's message of "Tue, 18 Nov 2008 14:21:11 -0800")
Robert Love <robert.w.love@intel.com> writes:
A quick review. I'm not a SCSI layer expert, so it's
more general code comments.
Could you perhaps give a brief design overview of the library
in the description?
What it does, where it sits on the stack etc.? Doesn't need
to be very extensive, but should be understandable even
for people not intimate with the FCoE spec.
> +#define FC_DISC_RETRY_LIMIT 3 /* max retries */
> +#define FC_DISC_RETRY_DELAY 500UL /* (msecs) delay */
Define is not used?
> +
> +#define FC_DISC_DELAY 3
> +
> +static int fc_disc_debug;
> +
> +#define FC_DEBUG_DISC(fmt...) \
> + do { \
> + if (fc_disc_debug) \
> + FC_DBG(fmt); \
> + } while (0)
In 2.6.28 there is dynamic_printk.h now to handle this.
> +
> +static struct mutex disc_list_lock;
> +static struct list_head disc_list;
> +
> +struct fc_disc {
> + unsigned char retry_count;
> + unsigned char delay;
> + unsigned char pending;
> + unsigned char requested;
> + unsigned short seq_count;
> + unsigned char buf_len;
> + enum fc_disc_event event;
> +
> + void (*disc_callback)(struct fc_lport *,
> + enum fc_disc_event);
While it may seem excessive for a single callback
the tend is towards separate ->ops structures. They
have the advantage that they can be made const
which has at least some theoretical security advantages.
Also callback numbers tend to grow over time.
> +/**
> + * fc_disc_lookup_rport - lookup a remote port by port_id
> + * @lport: Fibre Channel host port instance
> + * @port_id: remote port port_id to match
> + */
> +struct fc_rport *fc_disc_lookup_rport(const struct fc_lport *lport,
> + u32 port_id)
> +{
> + struct fc_disc *disc;
> + struct fc_rport *rport, *found = NULL;
> + struct fc_rport_libfc_priv *rdata;
> + int disc_found = 0;
> +
> + mutex_lock(&disc_list_lock);
> + list_for_each_entry(disc, &disc_list, list) {
> + if (disc->lport == lport) {
> + list_for_each_entry(rdata, &disc->rports, peers) {
Hopefully the max numbers of either of those are strictly bounded.
> +
> +/**
> + * fc_disc_alloc - Allocate a discovery work object
> + * @lport: The FC lport associated with the discovery job
> + */
> +static inline struct fc_disc *fc_disc_alloc(struct fc_lport *lport)
> +{
> + struct fc_disc *disc;
> +
> + disc = kzalloc(sizeof(struct fc_disc), GFP_KERNEL);
This misses a NULL check
> + INIT_LIST_HEAD(&disc->list);
> + INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
> + mutex_init(&disc->disc_mutex);
> + INIT_LIST_HEAD(&disc->rports);
> +
> + disc->lport = lport;
> + disc->delay = FC_DISC_DELAY;
> + disc->event = DISC_EV_NONE;
Is it ok that the callback (and probably some other fields) is not
initialized here yet?
> +
> + mutex_lock(&disc_list_lock);
> + list_add_tail(&disc->list, &disc_list);
> + mutex_unlock(&disc_list_lock);
... when you make this globally visible?
> + if (event == RPORT_EV_CREATED) {
> + mutex_lock(&disc_list_lock);
> + list_for_each_entry(disc, &disc_list, list) {
> + if (disc->lport == lport) {
> + found = 1;
> + mutex_lock(&disc->disc_mutex);
> + list_add_tail(&rdata->peers, &disc->rports);
> + mutex_unlock(&disc->disc_mutex);
> + }
> + }
> + mutex_unlock(&disc_list_lock);
That's a lot of locks. Did you have a locking order comment somewhere?
> + if (!rp || rp->rscn_page_len != sizeof(*pp))
> + goto reject;
> +
> + len = ntohs(rp->rscn_plen);
> + if (len < sizeof(*rp))
> + goto reject;
> + len -= sizeof(*rp);
Don't you need to check here than len is a multiple of sizeof(*pp) ?
Otherwise bad input could make it loop a very long time.
> +
> + for (pp = (void *)(rp + 1); len; len -= sizeof(*pp), pp++) {
Also len >= 0 is always safer.
> + redisc, lport->state, disc->pending);
> + list_for_each_entry_safe(dp, next, &disc_ports, peers) {
> + list_del(&dp->peers);
> + rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
> + if (rport) {
> + rdata = RPORT_TO_PRIV(rport);
> + list_del(&rdata->peers);
Hopefully there's a mutex here hold for that list.
> + lport->tt.rport_logoff(rport);
> + }
> + fc_disc_single(disc, dp);
> + }
> + }
> + fc_frame_free(fp);
> + return;
> +reject:
> + rjt_data.fp = NULL;
> + rjt_data.reason = ELS_RJT_LOGIC;
> + rjt_data.explan = ELS_EXPL_NONE;
> + lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
> + fc_frame_free(fp);
Some statistics counter for that case would be probably useful.
> +
> + op = fc_frame_payload_op(fp);
> + switch (op) {
> + case ELS_RSCN:
> + mutex_lock(&disc->disc_mutex);
> + fc_disc_recv_rscn_req(sp, fp, disc);
> + mutex_unlock(&disc->disc_mutex);
Ok yes seems the lock is hold.
> +
> + list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
> + rport = PRIV_TO_RPORT(rdata);
> + FC_DEBUG_DISC("list_del(%6x)\n", rport->port_id);
> + list_del(&rdata->peers);
> + lport->tt.rport_logoff(rport);
Hopefully that callback knows for which peer that is.
> +static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
> + enum fc_disc_event),
> + struct fc_lport *lport)
> +{
> + struct fc_rport *rport;
> + struct fc_rport_identifiers ids;
> + struct fc_disc *disc;
> + int found = 0;
> +
> + mutex_lock(&disc_list_lock);
> + list_for_each_entry(disc, &disc_list, list) {
> + if (disc->lport == lport) {
> + found = 1;
> + break;
> + }
> + }
> + mutex_unlock(&disc_list_lock);
This seems to be opencoded a lot. Put it into some helper.
> +
> + /*
> + * Handle point-to-point mode as a simple discovery
> + * of the remote port. Yucky, yucky, yuck, yuck!
> + */
> + rport = disc->lport->ptp_rp;
> + if (rport) {
> + ids.port_id = rport->port_id;
> + ids.port_name = rport->port_name;
> + ids.node_name = rport->node_name;
> + ids.roles = FC_RPORT_ROLE_UNKNOWN;
> + get_device(&rport->dev);
> +
> + if (!fc_disc_new_target(disc, rport, &ids)) {
> + disc->event = DISC_EV_SUCCESS;
> + fc_disc_done(disc);
> + }
> + put_device(&rport->dev);
The ref counting here seems a little dubious. If it's because
disc_new_target can sleep then preemptible kernels can
do that anyways. Or it's not needed.
> + }
> + if (((ids->port_name != -1) || (ids->port_id != -1)) &&
> + ids->port_id != fc_host_port_id(lport->host) &&
> + ids->port_name != lport->wwpn) {
> + if (!rport) {
> + rport = lport->tt.rport_lookup(lport, ids->port_id);
> + if (!rport) {
> + struct fc_disc_port dp;
> + dp.lp = lport;
> + dp.ids.port_id = ids->port_id;
> + dp.ids.port_name = ids->port_name;
> + dp.ids.node_name = ids->node_name;
> + dp.ids.roles = ids->roles;
> + rport = fc_rport_rogue_create(&dp);
> + }
> + if (!rport)
> + error = ENOMEM;
> + }
> + if (rport) {
> + rp = rport->dd_data;
> + rp->event_callback = fc_disc_rport_event;
> + rp->rp_state = RPORT_ST_INIT;
> + lport->tt.rport_login(rport);
> + }
> + }
> + return error;
Unusual positive error return convention. Ok?
> + FC_DBG("Error %ld, retries %d/%d\n",
> + PTR_ERR(fp), disc->retry_count,
> + FC_DISC_RETRY_LIMIT);
> +
> + if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
The mixing of non errnos into PTR_ERR is also unusual,
but ok, but make sure you never leak them.
> + /*
> + * Memory allocation failure, or the exchange timed out,
> + * retry after delay.
> + */
> + if (disc->retry_count < FC_DISC_RETRY_LIMIT) {
> + /* go ahead and retry */
> + if (!fp)
> + delay = msecs_to_jiffies(500);
Ah that was the missing define above?
> + else {
> + delay = jiffies +
> + msecs_to_jiffies(lport->e_d_tov);
> +
> + /* timeout faster first time */
> + if (!disc->retry_count)
> + delay /= 4;
You have to divide by four before you add to jiffies, dividing
absolute jiffies doesn't make sense.
> + /*
> + * Handle partial name record left over from previous call.
> + */
> + bp = buf;
> + plen = len;
> + np = (struct fc_gpn_ft_resp *)bp;
> + tlen = disc->buf_len;
> + if (tlen) {
> + WARN_ON(tlen >= sizeof(*np));
> + plen = sizeof(*np) - tlen;
> + WARN_ON(plen <= 0);
> + WARN_ON(plen >= sizeof(*np));
I hope none of these WARN_ONs in network controllable. They nearly look like
some of them are.
Ok stopping here for now. That's a lot of code. Perhaps split it up
into a little smaller patches?
General impressions:
- Your locking is to complicated I think. Less locks is often more.
For short locks we also recommend spinlocks which perform
better (although you have to make sure you don't sleep then of course)
- I would double check everything that handles network data to
make sure it's not exploitable.
-Andi
--
ak@linux.intel.com
next prev parent reply other threads:[~2008-11-24 10:13 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-11-18 22:20 [PATCH 0/3] Open-FCoE Submission Robert Love
2008-11-18 22:21 ` [PATCH 1/3] FC protocol definition header files Robert Love
2008-11-18 22:21 ` [PATCH 2/3] libfc: A modular Fibre Channel library Robert Love
2008-11-24 10:13 ` Andi Kleen [this message]
2008-11-25 20:47 ` Love, Robert W
2008-12-02 23:36 ` Love, Robert W
2008-11-18 22:21 ` [PATCH 3/3] fcoe: Fibre Channel over Ethernet Robert Love
-- strict thread matches above, loose matches on Subject: below --
2008-12-09 23:10 [PATCH 0/3] Open-FCoE Submission (round 2) Robert Love
2008-12-09 23:10 ` [PATCH 2/3] libfc: A modular Fibre Channel library Robert Love
2008-12-10 0:03 ` Andi Kleen
2008-12-10 18:42 ` Vasu Dev
2008-12-10 19:42 ` Andi Kleen
2008-12-12 1:55 ` Vasu Dev
2008-12-12 2:19 ` Joe Eykholt
2008-12-11 0:44 ` Chris Leech
2008-12-11 0:49 ` Chris Leech
2008-12-11 20:32 ` Zou, Yi
2008-12-11 23:33 ` Andi Kleen
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=87tz9xh1o6.fsf@basil.nowhere.org \
--to=andi@firstfloor.org \
--cc=davem@davemloft.net \
--cc=james.bottomley@hansenpartnership.com \
--cc=james.smart@emulex.com \
--cc=jeffrey.t.kirsher@intel.com \
--cc=jeykholt@cisco.com \
--cc=jgarzik@redhat.com \
--cc=linux-scsi@vger.kernel.org \
--cc=michaelc@cs.wisc.edu \
--cc=robert.w.love@intel.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.