Commit f6549f53 authored by Niklas Cassel's avatar Niklas Cassel

ata,scsi: libata-core: Do not leak memory for ata_port struct members

libsas is currently not freeing all the struct ata_port struct members,
e.g. ncq_sense_buf for a driver supporting Command Duration Limits (CDL).

Add a function, ata_port_free(), that is used to free a ata_port,
including its struct members. It makes sense to keep the code related to
freeing a ata_port in its own function, which will also free all the
struct members of struct ata_port.

Fixes: 18bd7718 ("scsi: ata: libata: Handle completion of CDL commands using policy 0xD")
Reviewed-by: default avatarJohn Garry <john.g.garry@oracle.com>
Link: https://lore.kernel.org/r/20240629124210.181537-8-cassel@kernel.orgSigned-off-by: default avatarNiklas Cassel <cassel@kernel.org>
parent 5d92c7c5
...@@ -5489,6 +5489,18 @@ struct ata_port *ata_port_alloc(struct ata_host *host) ...@@ -5489,6 +5489,18 @@ struct ata_port *ata_port_alloc(struct ata_host *host)
return ap; return ap;
} }
void ata_port_free(struct ata_port *ap)
{
if (!ap)
return;
kfree(ap->pmp_link);
kfree(ap->slave_link);
kfree(ap->ncq_sense_buf);
kfree(ap);
}
EXPORT_SYMBOL_GPL(ata_port_free);
static void ata_devres_release(struct device *gendev, void *res) static void ata_devres_release(struct device *gendev, void *res)
{ {
struct ata_host *host = dev_get_drvdata(gendev); struct ata_host *host = dev_get_drvdata(gendev);
...@@ -5515,15 +5527,7 @@ static void ata_host_release(struct kref *kref) ...@@ -5515,15 +5527,7 @@ static void ata_host_release(struct kref *kref)
int i; int i;
for (i = 0; i < host->n_ports; i++) { for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i]; ata_port_free(host->ports[i]);
if (!ap)
continue;
kfree(ap->pmp_link);
kfree(ap->slave_link);
kfree(ap->ncq_sense_buf);
kfree(ap);
host->ports[i] = NULL; host->ports[i] = NULL;
} }
kfree(host); kfree(host);
...@@ -5906,7 +5910,7 @@ int ata_host_register(struct ata_host *host, const struct scsi_host_template *sh ...@@ -5906,7 +5910,7 @@ int ata_host_register(struct ata_host *host, const struct scsi_host_template *sh
* allocation time. * allocation time.
*/ */
for (i = host->n_ports; host->ports[i]; i++) for (i = host->n_ports; host->ports[i]; i++)
kfree(host->ports[i]); ata_port_free(host->ports[i]);
/* give ports names and add SCSI hosts */ /* give ports names and add SCSI hosts */
for (i = 0; i < host->n_ports; i++) { for (i = 0; i < host->n_ports; i++) {
......
...@@ -610,15 +610,15 @@ int sas_ata_init(struct domain_device *found_dev) ...@@ -610,15 +610,15 @@ int sas_ata_init(struct domain_device *found_dev)
rc = ata_sas_tport_add(ata_host->dev, ap); rc = ata_sas_tport_add(ata_host->dev, ap);
if (rc) if (rc)
goto destroy_port; goto free_port;
found_dev->sata_dev.ata_host = ata_host; found_dev->sata_dev.ata_host = ata_host;
found_dev->sata_dev.ap = ap; found_dev->sata_dev.ap = ap;
return 0; return 0;
destroy_port: free_port:
kfree(ap); ata_port_free(ap);
free_host: free_host:
ata_host_put(ata_host); ata_host_put(ata_host);
return rc; return rc;
......
...@@ -301,7 +301,7 @@ void sas_free_device(struct kref *kref) ...@@ -301,7 +301,7 @@ void sas_free_device(struct kref *kref)
if (dev_is_sata(dev) && dev->sata_dev.ap) { if (dev_is_sata(dev) && dev->sata_dev.ap) {
ata_sas_tport_delete(dev->sata_dev.ap); ata_sas_tport_delete(dev->sata_dev.ap);
kfree(dev->sata_dev.ap); ata_port_free(dev->sata_dev.ap);
ata_host_put(dev->sata_dev.ata_host); ata_host_put(dev->sata_dev.ata_host);
dev->sata_dev.ata_host = NULL; dev->sata_dev.ata_host = NULL;
dev->sata_dev.ap = NULL; dev->sata_dev.ap = NULL;
......
...@@ -1249,6 +1249,7 @@ extern int ata_slave_link_init(struct ata_port *ap); ...@@ -1249,6 +1249,7 @@ extern int ata_slave_link_init(struct ata_port *ap);
extern struct ata_port *ata_sas_port_alloc(struct ata_host *, extern struct ata_port *ata_sas_port_alloc(struct ata_host *,
struct ata_port_info *, struct Scsi_Host *); struct ata_port_info *, struct Scsi_Host *);
extern void ata_port_probe(struct ata_port *ap); extern void ata_port_probe(struct ata_port *ap);
extern void ata_port_free(struct ata_port *ap);
extern int ata_sas_tport_add(struct device *parent, struct ata_port *ap); extern int ata_sas_tport_add(struct device *parent, struct ata_port *ap);
extern void ata_sas_tport_delete(struct ata_port *ap); extern void ata_sas_tport_delete(struct ata_port *ap);
int ata_sas_device_configure(struct scsi_device *sdev, struct queue_limits *lim, int ata_sas_device_configure(struct scsi_device *sdev, struct queue_limits *lim,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment