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