From: Guenter Roeck <linux@roeck-us.net>
To: "Winkler, Tomas" <tomas.winkler@intel.com>,
"gregkh@linuxfoundation.org" <gregkh@linuxfoundation.org>,
"wim@iguana.be" <wim@iguana.be>
Cc: "linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"Usyskin, Alexander" <alexander.usyskin@intel.com>,
"linux-watchdog@vger.kernel.org" <linux-watchdog@vger.kernel.org>
Subject: Re: [char-misc-next 3/6] mei: wd: implement MEI iAMT watchdog driver
Date: Tue, 1 Dec 2015 08:02:03 -0800 [thread overview]
Message-ID: <565DC47B.3060402@roeck-us.net> (raw)
In-Reply-To: <1448970872.30677.15.camel@intel.com>
On 12/01/2015 03:55 AM, Winkler, Tomas wrote:
[ ... ]
>>> +/**
>>> + * struct mei_wdt_dev - watchdog device wrapper
>>> + *
>>> + * @wdd: watchdog device
>>> + * @wdt: back pointer to mei_wdt driver
>>> + * @refcnt: reference counter
>>> + */
>>> +struct mei_wdt_dev {
>>> + struct watchdog_device wdd;
>>> + struct mei_wdt *wdt;
>>> + struct kref refcnt;
>>> +};
>>> +
>>> +/**
>>> + * struct mei_wdt - mei watchdog driver
>>> + *
>>> + * @cldev: mei watchdog client device
>>> + * @mwd: watchdog device wrapper
>>> + * @state: watchdog internal state
>>> + * @timeout: watchdog current timeout
>>> + */
>>> +struct mei_wdt {
>>> + struct mei_cl_device *cldev;
>>> + struct mei_wdt_dev *mwd;
>>> + enum mei_wdt_state state;
>>> + u16 timeout;
>>> +};
>>
>> Any special reason for having two data structures instead of one ?
>> You could just move the variables from struct mei_wdt_dev into
>> struct mei_wdt, no ?
>
> Yes, on newer platform mei_wdt_dev might be not present in case the the
> device is not provisioned. This came to action in the following
> patches.
>
It is still an implementation choice to keep the data structures separate.
That mei_wdt_dev can be unregistered doesn't mean that the data structure
has to be destroyed or allocated on registration.
>>> +
>>> +struct mei_wdt_hdr {
>>> + u8 command;
>>> + u8 bytecount;
>>> + u8 subcommand;
>>> + u8 versionnumber;
>>> +};
>>> +
>>> +struct mei_wdt_start_request {
>>> + struct mei_wdt_hdr hdr;
>>> + u16 timeout;
>>> + u8 reserved[17];
>>> +} __packed;
>>> +
>>> +struct mei_wdt_stop_request {
>>> + struct mei_wdt_hdr hdr;
>>> +} __packed;
>>> +
>>> +/**
>>> + * mei_wdt_ping - send wd start command
>>> + *
>>> + * @wdt: mei watchdog device
>>> + *
>>> + * Return: number of bytes sent on success,
>>> + * negative errno code on failure
>>> + */
>>> +static int mei_wdt_ping(struct mei_wdt *wdt)
>>> +{
>>> + struct mei_wdt_start_request req;
>>> + const size_t req_len = sizeof(req);
>>> +
>>> + memset(&req, 0, req_len);
>>> + req.hdr.command = MEI_MANAGEMENT_CONTROL;
>>> + req.hdr.bytecount = req_len - offsetof(struct mei_wdt_hdr,
>>> subcommand);
>>> + req.hdr.subcommand = MEI_MC_START_WD_TIMER_REQ;
>>> + req.hdr.versionnumber = MEI_MC_VERSION_NUMBER;
>>> + req.timeout = wdt->timeout;
>>> +
>>> + return mei_cldev_send(wdt->cldev, (u8 *)&req, req_len);
>>> +}
>>> +
>>> +/**
>>> + * mei_wdt_stop - send wd stop command
>>> + *
>>> + * @wdt: mei watchdog device
>>> + *
>>> + * Return: number of bytes sent on success,
>>> + * negative errno code on failure
>>> + */
>>> +static int mei_wdt_stop(struct mei_wdt *wdt)
>>> +{
>>> + struct mei_wdt_stop_request req;
>>> + const size_t req_len = sizeof(req);
>>> +
>>> + memset(&req, 0, req_len);
>>> + req.hdr.command = MEI_MANAGEMENT_CONTROL;
>>> + req.hdr.bytecount = req_len - offsetof(struct mei_wdt_hdr,
>>> subcommand);
>>> + req.hdr.subcommand = MEI_MC_STOP_WD_TIMER_REQ;
>>> + req.hdr.versionnumber = MEI_MC_VERSION_NUMBER;
>>> +
>>> + return mei_cldev_send(wdt->cldev, (u8 *)&req, req_len);
>>> +}
>>> +
>>> +/**
>>> + * mei_wdt_ops_start - wd start command from the watchdog core.
>>> + *
>>> + * @wdd: watchdog device
>>> + *
>>> + * Return: 0 on success or -ENODEV;
>>> + */
>>> +static int mei_wdt_ops_start(struct watchdog_device *wdd)
>>> +{
>>> + struct mei_wdt_dev *mwd = watchdog_get_drvdata(wdd);
>>> +
>>> + if (!mwd)
>>> + return -ENODEV;
>>
>> This can only happen because you call watchdog_set_drvdata() after
>> watchdog device registration. If that happens, the system is in
>> really bad shape.
>
> mei_wdt_dev can destroyed during
> driver operation if the device is unprovisioned, but still you the
> condition should not happen unless we have a bug. We can put WARN_ON()
> there.
>
The calling code should take care of that and not call those functions
after unregistration. More on that below.
>>
>> I would suggest to move the call to watchdog_set_drvdata() ahead
>> of watchdog_register_device() and drop those checks.
>>
>>> +
>>> + mwd->wdt->state = MEI_WDT_START;
>>> + wdd->timeout = mwd->wdt->timeout;
>>> + return 0;
>>> +}
>>> +
>>> +/**
>>> + * mei_wdt_ops_stop - wd stop command from the watchdog core.
>>> + *
>>> + * @wdd: watchdog device
>>> + *
>>> + * Return: 0 if success, negative errno code for failure
>>> + */
>>> +static int mei_wdt_ops_stop(struct watchdog_device *wdd)
>>> +{
>>> + struct mei_wdt_dev *mwd = watchdog_get_drvdata(wdd);
>>> + struct mei_wdt *wdt;
>>> + int ret;
>>> +
>>> + if (!mwd)
>>> + return -ENODEV;
>>> +
>>> + wdt = mwd->wdt;
>>> +
>>> + if (wdt->state != MEI_WDT_RUNNING)
>>> + return 0;
>>> +
>>> + wdt->state = MEI_WDT_STOPPING;
>>> +
>>> + ret = mei_wdt_stop(wdt);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + wdt->state = MEI_WDT_IDLE;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/**
>>> + * mei_wdt_ops_ping - wd ping command from the watchdog core.
>>> + *
>>> + * @wdd: watchdog device
>>> + *
>>> + * Return: 0 if success, negative errno code on failure
>>> + */
>>> +static int mei_wdt_ops_ping(struct watchdog_device *wdd)
>>> +{
>>> + struct mei_wdt_dev *mwd = watchdog_get_drvdata(wdd);
>>> + struct mei_wdt *wdt;
>>> + int ret;
>>> +
>>> + if (!mwd)
>>> + return -ENODEV;
>>> +
>>> + wdt = mwd->wdt;
>>> +
>>> + if (wdt->state != MEI_WDT_START &&
>>> + wdt->state != MEI_WDT_RUNNING)
>>
>> Unnecessary continuation line ?
> Looks more readable to me. but we can also straight the condition.
>>
>>> + return 0;
>>> +
>>> + ret = mei_wdt_ping(wdt);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + wdt->state = MEI_WDT_RUNNING;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/**
>>> + * mei_wdt_ops_set_timeout - wd set timeout command from the
>>> watchdog core.
>>> + *
>>> + * @wdd: watchdog device
>>> + * @timeout: timeout value to set
>>> + *
>>> + * Return: 0 if success, negative errno code for failure
>>> + */
>>> +static int mei_wdt_ops_set_timeout(struct watchdog_device *wdd,
>>> + unsigned int timeout)
>>> +{
>>> + struct mei_wdt_dev *mwd = watchdog_get_drvdata(wdd);
>>> + struct mei_wdt *wdt;
>>> +
>>> + if (!mwd)
>>> + return -ENODEV;
>>> +
>>> + wdt = mwd->wdt;
>>> +
>>> + /* valid value is already checked by the caller */
>>> + wdt->timeout = timeout;
>>> + wdd->timeout = timeout;
>>
>> One of those seems unnecessary. Why keep the timeout twice ?
>
> We need two as wdd may not exists and we still need to send ping to
> detect if the device is provisioned.
>
Ok, makes sense.
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void mei_wdt_release(struct kref *ref)
>>> +{
>>> + struct mei_wdt_dev *mwd = container_of(ref, struct
>>> mei_wdt_dev, refcnt);
>>> +
>>> + kfree(mwd);
>>> +}
>>> +
>>> +static void mei_wdt_ops_ref(struct watchdog_device *wdd)
>>> +{
>>> + struct mei_wdt_dev *mwd = watchdog_get_drvdata(wdd);
>>> +
>>> + kref_get(&mwd->refcnt);
>>> +}
>>> +
>>> +static void mei_wdt_ops_unref(struct watchdog_device *wdd)
>>> +{
>>> + struct mei_wdt_dev *mwd = watchdog_get_drvdata(wdd);
>>> +
>>> + kref_put(&mwd->refcnt, mei_wdt_release);
>>> +}
>>> +
>>> +static const struct watchdog_ops wd_ops = {
>>> + .owner = THIS_MODULE,
>>> + .start = mei_wdt_ops_start,
>>> + .stop = mei_wdt_ops_stop,
>>> + .ping = mei_wdt_ops_ping,
>>> + .set_timeout = mei_wdt_ops_set_timeout,
>>> + .ref = mei_wdt_ops_ref,
>>> + .unref = mei_wdt_ops_unref,
>>> +};
>>> +
>>> +static struct watchdog_info wd_info = {
>>> + .identity = INTEL_AMT_WATCHDOG_ID,
>>> + .options = WDIOF_KEEPALIVEPING |
>>> + WDIOF_SETTIMEOUT |
>>> + WDIOF_ALARMONLY,
>>> +};
>>> +
>>> +static int mei_wdt_register(struct mei_wdt *wdt)
>>> +{
>>> + struct mei_wdt_dev *mwd;
>>> + struct device *dev;
>>> + int ret;
>>> +
>>> + if (!wdt || !wdt->cldev)
>>> + return -EINVAL;
>>> +
>>> + dev = &wdt->cldev->dev;
>>> +
>>> + mwd = kzalloc(sizeof(struct mei_wdt_dev), GFP_KERNEL);
>>> + if (!mwd)
>>> + return -ENOMEM;
>>> +
>>> + mwd->wdt = wdt;
>>> + mwd->wdd.info = &wd_info;
>>> + mwd->wdd.ops = &wd_ops;
>>> + mwd->wdd.parent = dev;
>>> + mwd->wdd.timeout = MEI_WDT_DEFAULT_TIMEOUT;
>>> + mwd->wdd.min_timeout = MEI_WDT_MIN_TIMEOUT;
>>> + mwd->wdd.max_timeout = MEI_WDT_MAX_TIMEOUT;
>>> + kref_init(&mwd->refcnt);
>>> +
>>> + ret = watchdog_register_device(&mwd->wdd);
>>> + if (ret) {
>>> + dev_err(dev, "unable to register watchdog device =
>>> %d.\n", ret);
>>> + kref_put(&mwd->refcnt, mei_wdt_release);
>>> + return ret;
>>> + }
>>> +
>>> + wdt->mwd = mwd;
>>> + watchdog_set_drvdata(&mwd->wdd, mwd);
>>> + return 0;
>>> +}
>>> +
>>> +static void mei_wdt_unregister(struct mei_wdt *wdt)
>>> +{
>>> + if (!wdt->mwd)
>>> + return;
>>> +
>>> + watchdog_unregister_device(&wdt->mwd->wdd);
>>> + kref_put(&wdt->mwd->refcnt, mei_wdt_release);
>>> + wdt->mwd = NULL;
If setting this to NULL was really needed, you would have a race condition here:
wdt->mwd is set to NULL only after the pointer it points to was freed, leaving
a small window where the code above could still access it.
>>> +}
>>
>> Seems to me that using two separate data structures instead of one
>> adds a lot of complexity.
>
> It might be reduced but I'm not sure it can be significantly simpler.
> It the reference counter will be part of watchdog_device it would be
> simpler.
>
It would be there if struct watchdog_device was allocated by the
watchdog core, which is not the case. I am still trying to create
a situation where the local data structure (struct mei_wdt in this case)
can be released while the watchdog device is still open
(ie how to unprovision the watchdog device while in use).
Once I figure that out, I hope I can find a way to protect it
differently and drop the ref/unref callbacks. I suspect it may
be necessary to separate struct watchdog_device into two parts:
one used by drivers and one used by the watchdog core.
The watchdog core would then manage its own data structure, and
no longer depend on struct watchdog_device (owned by the driver)
to actually be there. Different story, though.
Thanks,
Guenter
next prev parent reply other threads:[~2015-12-01 16:02 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-11-26 12:31 [char-misc-next 0/6] mei: create proper iAMT watchdog driver Tomas Winkler
2015-11-26 12:31 ` [char-misc-next 1/6] mei: drop nfc leftovers from the mei driver Tomas Winkler
2015-11-26 12:31 ` [char-misc-next 2/6] mei: wd: drop the watchdog code from the core " Tomas Winkler
2015-11-26 12:31 ` [char-misc-next 3/6] mei: wd: implement MEI iAMT watchdog driver Tomas Winkler
2015-11-30 16:55 ` Guenter Roeck
2015-12-01 11:55 ` Winkler, Tomas
2015-12-01 11:55 ` Winkler, Tomas
2015-12-01 16:02 ` Guenter Roeck [this message]
2015-12-02 7:41 ` Winkler, Tomas
2015-12-02 7:41 ` Winkler, Tomas
2015-11-26 12:31 ` [char-misc-next 4/6] mei: bus: whitelist the watchdog client Tomas Winkler
2015-11-26 12:31 ` [char-misc-next 5/6] mei: wd: register wd device only if required Tomas Winkler
2015-11-26 12:31 ` [char-misc-next 6/6] mei: wd: re-register device on event Tomas Winkler
2015-11-30 17:08 ` Guenter Roeck
2015-12-01 11:59 ` Winkler, Tomas
2015-12-01 11:59 ` Winkler, Tomas
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=565DC47B.3060402@roeck-us.net \
--to=linux@roeck-us.net \
--cc=alexander.usyskin@intel.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-watchdog@vger.kernel.org \
--cc=tomas.winkler@intel.com \
--cc=wim@iguana.be \
/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.