| /* |
| * teamd_workq.c - Teamd work queue |
| * Copyright (C) 2013-2015 Jiri Pirko <jiri@resnulli.us> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <private/misc.h> |
| |
| #include "teamd_workq.h" |
| |
| #define WORKQ_CB_NAME "workq" |
| |
| static int teamd_workq_callback_socket(struct teamd_context *ctx, int events, |
| void *priv) |
| { |
| struct teamd_workq *workq; |
| struct teamd_workq *tmp; |
| char bytes[16]; |
| int ret; |
| int err; |
| |
| again: |
| ret = read(ctx->workq.pipe_r, bytes, sizeof(bytes)); |
| if (ret == -1) { |
| if (errno == EINTR) |
| goto again; |
| else if (errno != EAGAIN) |
| return -errno; |
| } |
| |
| teamd_loop_callback_disable(ctx, WORKQ_CB_NAME, ctx); |
| |
| list_for_each_node_entry_safe(workq, tmp, &ctx->workq.work_list, list) { |
| list_del(&workq->list); |
| list_init(&workq->list); |
| err = workq->func(ctx, workq); |
| if (err) |
| return err; |
| } |
| return 0; |
| } |
| |
| int teamd_workq_init(struct teamd_context *ctx) |
| { |
| int fds[2]; |
| int err; |
| |
| list_init(&ctx->workq.work_list); |
| err = pipe2(fds, O_NONBLOCK); |
| if (err) |
| return -errno; |
| ctx->workq.pipe_r = fds[0]; |
| ctx->workq.pipe_w = fds[1]; |
| |
| err = teamd_loop_callback_fd_add_tail(ctx, WORKQ_CB_NAME, ctx, |
| teamd_workq_callback_socket, |
| ctx->workq.pipe_r, |
| TEAMD_LOOP_FD_EVENT_READ); |
| if (err) { |
| teamd_log_err("Failed add workq callback."); |
| goto close_pipe; |
| } |
| return 0; |
| |
| close_pipe: |
| close(ctx->workq.pipe_r); |
| close(ctx->workq.pipe_w); |
| return 0; |
| } |
| |
| void teamd_workq_fini(struct teamd_context *ctx) |
| { |
| struct teamd_workq *workq; |
| struct teamd_workq *tmp; |
| |
| teamd_loop_callback_del(ctx, WORKQ_CB_NAME, ctx); |
| close(ctx->workq.pipe_r); |
| close(ctx->workq.pipe_w); |
| list_for_each_node_entry_safe(workq, tmp, &ctx->workq.work_list, list) { |
| list_del(&workq->list); |
| list_init(&workq->list); |
| } |
| } |
| |
| static void teamd_workq_set_for_process(struct teamd_context *ctx) |
| { |
| int err; |
| const char byte = 0; |
| |
| retry: |
| err = write(ctx->workq.pipe_w, &byte, 1); |
| if (err == -1 && errno == EINTR) |
| goto retry; |
| teamd_loop_callback_enable(ctx, WORKQ_CB_NAME, ctx); |
| } |
| |
| void teamd_workq_schedule_work(struct teamd_context *ctx, |
| struct teamd_workq *workq) |
| { |
| if (!list_empty(&workq->list)) |
| return; |
| list_add_tail(&ctx->workq.work_list, &workq->list); |
| teamd_workq_set_for_process(ctx); |
| } |
| |
| void teamd_workq_init_work(struct teamd_workq *workq, teamd_workq_func_t func) |
| { |
| workq->func = func; |
| list_init(&workq->list); |
| } |