linux/drivers/misc/mic/cosm_client/cosm_scif_client.c
<<
>>
Prefs
   1/*
   2 * Intel MIC Platform Software Stack (MPSS)
   3 *
   4 * Copyright(c) 2015 Intel Corporation.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License, version 2, as
   8 * published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13 * General Public License for more details.
  14 *
  15 * The full GNU General Public License is included in this distribution in
  16 * the file called "COPYING".
  17 *
  18 * Intel MIC COSM Client Driver
  19 *
  20 */
  21#include <linux/module.h>
  22#include <linux/delay.h>
  23#include <linux/reboot.h>
  24#include <linux/kthread.h>
  25#include "../cosm/cosm_main.h"
  26
  27#define COSM_SCIF_MAX_RETRIES 10
  28#define COSM_HEARTBEAT_SEND_MSEC (COSM_HEARTBEAT_SEND_SEC * MSEC_PER_SEC)
  29
  30static struct task_struct *client_thread;
  31static scif_epd_t client_epd;
  32static struct scif_peer_dev *client_spdev;
  33
  34/*
  35 * Reboot notifier: receives shutdown status from the OS and communicates it
  36 * back to the COSM process on the host
  37 */
  38static int cosm_reboot_event(struct notifier_block *this, unsigned long event,
  39                             void *ptr)
  40{
  41        struct cosm_msg msg = { .id = COSM_MSG_SHUTDOWN_STATUS };
  42        int rc;
  43
  44        event = (event == SYS_RESTART) ? SYSTEM_RESTART : event;
  45        dev_info(&client_spdev->dev, "%s %d received event %ld\n",
  46                 __func__, __LINE__, event);
  47
  48        msg.shutdown_status = event;
  49        rc = scif_send(client_epd, &msg, sizeof(msg), SCIF_SEND_BLOCK);
  50        if (rc < 0)
  51                dev_err(&client_spdev->dev, "%s %d scif_send rc %d\n",
  52                        __func__, __LINE__, rc);
  53
  54        return NOTIFY_DONE;
  55}
  56
  57static struct notifier_block cosm_reboot = {
  58        .notifier_call  = cosm_reboot_event,
  59};
  60
  61/* Set system time from timespec value received from the host */
  62static void cosm_set_time(struct cosm_msg *msg)
  63{
  64        int rc = do_settimeofday64(&msg->timespec);
  65
  66        if (rc)
  67                dev_err(&client_spdev->dev, "%s: %d settimeofday rc %d\n",
  68                        __func__, __LINE__, rc);
  69}
  70
  71/* COSM client receive message processing */
  72static void cosm_client_recv(void)
  73{
  74        struct cosm_msg msg;
  75        int rc;
  76
  77        while (1) {
  78                rc = scif_recv(client_epd, &msg, sizeof(msg), 0);
  79                if (!rc) {
  80                        return;
  81                } else if (rc < 0) {
  82                        dev_err(&client_spdev->dev, "%s: %d rc %d\n",
  83                                __func__, __LINE__, rc);
  84                        return;
  85                }
  86
  87                dev_dbg(&client_spdev->dev, "%s: %d rc %d id 0x%llx\n",
  88                        __func__, __LINE__, rc, msg.id);
  89
  90                switch (msg.id) {
  91                case COSM_MSG_SYNC_TIME:
  92                        cosm_set_time(&msg);
  93                        break;
  94                case COSM_MSG_SHUTDOWN:
  95                        orderly_poweroff(true);
  96                        break;
  97                default:
  98                        dev_err(&client_spdev->dev, "%s: %d unknown id %lld\n",
  99                                __func__, __LINE__, msg.id);
 100                        break;
 101                }
 102        }
 103}
 104
 105/* Initiate connection to the COSM server on the host */
 106static int cosm_scif_connect(void)
 107{
 108        struct scif_port_id port_id;
 109        int i, rc;
 110
 111        client_epd = scif_open();
 112        if (!client_epd) {
 113                dev_err(&client_spdev->dev, "%s %d scif_open failed\n",
 114                        __func__, __LINE__);
 115                return -ENOMEM;
 116        }
 117
 118        port_id.node = 0;
 119        port_id.port = SCIF_COSM_LISTEN_PORT;
 120
 121        for (i = 0; i < COSM_SCIF_MAX_RETRIES; i++) {
 122                rc = scif_connect(client_epd, &port_id);
 123                if (rc < 0)
 124                        msleep(1000);
 125                else
 126                        break;
 127        }
 128
 129        if (rc < 0) {
 130                dev_err(&client_spdev->dev, "%s %d scif_connect rc %d\n",
 131                        __func__, __LINE__, rc);
 132                scif_close(client_epd);
 133                client_epd = NULL;
 134        }
 135        return rc < 0 ? rc : 0;
 136}
 137
 138/* Close host SCIF connection */
 139static void cosm_scif_connect_exit(void)
 140{
 141        if (client_epd) {
 142                scif_close(client_epd);
 143                client_epd = NULL;
 144        }
 145}
 146
 147/*
 148 * COSM SCIF client thread function: waits for messages from the host and sends
 149 * a heartbeat to the host
 150 */
 151static int cosm_scif_client(void *unused)
 152{
 153        struct cosm_msg msg = { .id = COSM_MSG_HEARTBEAT };
 154        struct scif_pollepd pollepd;
 155        int rc;
 156
 157        allow_signal(SIGKILL);
 158
 159        while (!kthread_should_stop()) {
 160                pollepd.epd = client_epd;
 161                pollepd.events = POLLIN;
 162
 163                rc = scif_poll(&pollepd, 1, COSM_HEARTBEAT_SEND_MSEC);
 164                if (rc < 0) {
 165                        if (-EINTR != rc)
 166                                dev_err(&client_spdev->dev,
 167                                        "%s %d scif_poll rc %d\n",
 168                                        __func__, __LINE__, rc);
 169                        continue;
 170                }
 171
 172                if (pollepd.revents & POLLIN)
 173                        cosm_client_recv();
 174
 175                msg.id = COSM_MSG_HEARTBEAT;
 176                rc = scif_send(client_epd, &msg, sizeof(msg), SCIF_SEND_BLOCK);
 177                if (rc < 0)
 178                        dev_err(&client_spdev->dev, "%s %d scif_send rc %d\n",
 179                                __func__, __LINE__, rc);
 180        }
 181
 182        dev_dbg(&client_spdev->dev, "%s %d Client thread stopped\n",
 183                __func__, __LINE__);
 184        return 0;
 185}
 186
 187static void cosm_scif_probe(struct scif_peer_dev *spdev)
 188{
 189        int rc;
 190
 191        dev_dbg(&spdev->dev, "%s %d: dnode %d\n",
 192                __func__, __LINE__, spdev->dnode);
 193
 194        /* We are only interested in the host with spdev->dnode == 0 */
 195        if (spdev->dnode)
 196                return;
 197
 198        client_spdev = spdev;
 199        rc = cosm_scif_connect();
 200        if (rc)
 201                goto exit;
 202
 203        rc = register_reboot_notifier(&cosm_reboot);
 204        if (rc) {
 205                dev_err(&spdev->dev,
 206                        "reboot notifier registration failed rc %d\n", rc);
 207                goto connect_exit;
 208        }
 209
 210        client_thread = kthread_run(cosm_scif_client, NULL, "cosm_client");
 211        if (IS_ERR(client_thread)) {
 212                rc = PTR_ERR(client_thread);
 213                dev_err(&spdev->dev, "%s %d kthread_run rc %d\n",
 214                        __func__, __LINE__, rc);
 215                goto unreg_reboot;
 216        }
 217        return;
 218unreg_reboot:
 219        unregister_reboot_notifier(&cosm_reboot);
 220connect_exit:
 221        cosm_scif_connect_exit();
 222exit:
 223        client_spdev = NULL;
 224}
 225
 226static void cosm_scif_remove(struct scif_peer_dev *spdev)
 227{
 228        int rc;
 229
 230        dev_dbg(&spdev->dev, "%s %d: dnode %d\n",
 231                __func__, __LINE__, spdev->dnode);
 232
 233        if (spdev->dnode)
 234                return;
 235
 236        if (!IS_ERR_OR_NULL(client_thread)) {
 237                rc = send_sig(SIGKILL, client_thread, 0);
 238                if (rc) {
 239                        pr_err("%s %d send_sig rc %d\n",
 240                               __func__, __LINE__, rc);
 241                        return;
 242                }
 243                kthread_stop(client_thread);
 244        }
 245        unregister_reboot_notifier(&cosm_reboot);
 246        cosm_scif_connect_exit();
 247        client_spdev = NULL;
 248}
 249
 250static struct scif_client scif_client_cosm = {
 251        .name = KBUILD_MODNAME,
 252        .probe = cosm_scif_probe,
 253        .remove = cosm_scif_remove,
 254};
 255
 256static int __init cosm_client_init(void)
 257{
 258        int rc = scif_client_register(&scif_client_cosm);
 259
 260        if (rc)
 261                pr_err("scif_client_register failed rc %d\n", rc);
 262        return rc;
 263}
 264
 265static void __exit cosm_client_exit(void)
 266{
 267        scif_client_unregister(&scif_client_cosm);
 268}
 269
 270module_init(cosm_client_init);
 271module_exit(cosm_client_exit);
 272
 273MODULE_AUTHOR("Intel Corporation");
 274MODULE_DESCRIPTION("Intel(R) MIC card OS state management client driver");
 275MODULE_LICENSE("GPL v2");
 276