diff options
author | Inaky Perez-Gonzalez <inaky.perez-gonzalez> | 2008-12-05 10:36:28 -0800 |
---|---|---|
committer | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2008-12-05 10:36:28 -0800 |
commit | b3125da65abf030482901104f74578ac49010d47 (patch) | |
tree | 2c7e1ec3caed3f69a964a3ccee6bf20ac124dddf | |
parent | 3740ecc83b38a2395c4d107f2b842976a1edb7a4 (diff) |
libwimaxll: (fb Johannes Berg) fix handling of ack/nlerr in op-open
We were not handling the thing properly and thus it was working only
by chance. We were missing the nlerr as reponses to messages.
The callbacks need to be setup to listen for the message we are
waiting for, ack and nlerr. And then we need to call nl_recvmsgs()
until the ack / or nlerr handler have been seen.
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
-rw-r--r-- | include/wimaxll.h | 9 | ||||
-rw-r--r-- | lib/op-open.c | 84 |
2 files changed, 82 insertions, 11 deletions
diff --git a/include/wimaxll.h b/include/wimaxll.h index 3632542..7d06c53 100644 --- a/include/wimaxll.h +++ b/include/wimaxll.h @@ -319,6 +319,8 @@ typedef int (*wimaxll_state_change_cb_f)( /** * General structure for storing callback context * + * \ingroup callbacks + * * Callbacks set by the user receive a user-set pointer to a context * structure. The user can wrap this struct in a bigger context struct * and use wimaxll_container_of() during the callback to obtain its @@ -360,11 +362,16 @@ typedef int (*wimaxll_state_change_cb_f)( * be interpreted by the message handler as no processing was done * on the message. * - * \ingroup callbacks + * \internal + * + * \param msg_done This is used internally to mark when the acks (or + * errors) for a message have been received and the message + * receiving loop can be considered done. */ struct wimaxll_gnl_cb_context { struct wimaxll_handle *wmx; ssize_t result; + unsigned msg_done:1; /* internal */ }; diff --git a/lib/op-open.c b/lib/op-open.c index 53fcec9..2674d8a 100644 --- a/lib/op-open.c +++ b/lib/op-open.c @@ -240,8 +240,64 @@ int wimaxll_gnl_rp_ifinfo_error_cb(struct sockaddr_nl *nla, nla, nlerr, nlerr->error, _ctx); if (ctx->gnl.result == -EINPROGRESS) ctx->gnl.result = nlerr->error; - d_fnend(7, wmx, "(nla %p nlnerr %p [%d] ctx %p) = %d\n", - nla, nlerr, nlerr->error, _ctx, NL_STOP); + ctx->gnl.msg_done = 1; + d_fnend(7, wmx, "(nla %p nlnerr %p [%d] ctx %p) = NL_STOP\n", + nla, nlerr, nlerr->error, _ctx); + return NL_STOP; +} + + +static +int wimaxll_gnl_rp_ifinfo_ack_cb(struct nl_msg *msg, void *_ctx) +{ + int result; + struct nlmsghdr *nl_hdr; + struct nlmsgerr *nl_err; + size_t size = nlmsg_len(nlmsg_hdr(msg)); + struct wimaxll_ifinfo_context *ctx = _ctx; + + d_fnstart(7, NULL, "(msg %p ctx %p)\n", msg, ctx); + nl_hdr = nlmsg_hdr(msg); + size = nlmsg_len(nl_hdr); + nl_err = nlmsg_data(nl_hdr); + + if (size < sizeof(*nl_err)) { + wimaxll_msg(NULL, "E: netlink ack: buffer too small " + "(%zu vs %zu expected)\n", + size, sizeof(*nl_hdr) + sizeof(*nl_err)); + result = -EIO; + goto error_ack_short; + } + d_printf(4, NULL, "netlink ack: nlmsghdr len %u type %u flags 0x%04x " + "seq 0x%x pid %u\n", nl_hdr->nlmsg_len, nl_hdr->nlmsg_type, + nl_hdr->nlmsg_flags, nl_hdr->nlmsg_seq, nl_hdr->nlmsg_pid); + if (nl_hdr->nlmsg_type != NLMSG_ERROR) { + wimaxll_msg(NULL, "E: netlink ack: message is not an ack but " + "type %u\n", nl_hdr->nlmsg_type); + result = -EBADE; + goto error_bad_type; + } + d_printf(4, NULL, "netlink ack: nlmsgerr error %d for " + "nlmsghdr len %u type %u flags 0x%04x seq 0x%x pid %u\n", + nl_err->error, + nl_err->msg.nlmsg_len, nl_err->msg.nlmsg_type, + nl_err->msg.nlmsg_flags, nl_err->msg.nlmsg_seq, + nl_err->msg.nlmsg_pid); + if (ctx->gnl.result == -EINPROGRESS) { + ctx->gnl.result = nl_err->error; + d_printf(4, NULL, "netlink ack: updating gnl.result\n"); + } + else + d_printf(4, NULL, "netlink ack: Not updating gnl.result (%d)\n", + ctx->gnl.result); + + if (nl_err->error < 0) + d_printf(2, NULL, "D: netlink ack: received netlink error %d\n", + nl_err->error); + ctx->gnl.msg_done = 1; +error_ack_short: +error_bad_type: + d_fnend(7, NULL, "(msg %p ctx %p) = NL_STOP\n", msg, ctx); return NL_STOP; } @@ -368,22 +424,30 @@ int __wimaxll_cmd_open(struct wimaxll_handle *wmx) */ ctx.gnl.wmx = wmx; ctx.gnl.result = -EINPROGRESS; + ctx.gnl.msg_done = 0; cb = nl_socket_get_cb(wmx->nlh_tx); nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, wimaxll_gnl_rp_ifinfo_cb, &ctx); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, + wimaxll_gnl_rp_ifinfo_ack_cb, &ctx); nl_cb_err(cb, NL_CB_CUSTOM, wimaxll_gnl_rp_ifinfo_error_cb, &ctx.gnl); - nl_recvmsgs_default(wmx->nlh_tx); - nl_cb_put(cb); + do + result = nl_recvmsgs(wmx->nlh_tx, cb); + while (ctx.gnl.msg_done == 0 && result >= 0); result = ctx.gnl.result; - if (result >= 0) - nl_wait_for_ack(wmx->nlh_tx); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, NL_CB_DEFAULT, NULL); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, NL_CB_DEFAULT, NULL); + nl_cb_err(cb, NL_CB_CUSTOM, NL_CB_DEFAULT, NULL); + nl_cb_put(cb); if (result == -EINPROGRESS) wimaxll_msg(wmx, "E: %s: the kernel didn't reply with a " "WIMAX_GNL_RP_IFINFO message\n", __func__); - d_printf(1, wmx, "D: processing result is %d\n", result); - - /* All fine and dandy, commit the data */ - memcpy(wmx->gnl_mc, ctx.gnl_mc, sizeof(ctx.gnl_mc)); + else if (result >= 0) { + d_printf(1, wmx, "D: processing result is %d\n", result); + /* All fine and dandy, commit the data */ + memcpy(wmx->gnl_mc, ctx.gnl_mc, sizeof(ctx.gnl_mc)); + } else + d_printf(1, wmx, "E: rp_ifinfo failed %d\n", result); error_msg_prep: error_msg_send: nlmsg_free(msg); |